数据读写

本节将分为两部分:“基础数据读写”和“空间数据读写”。通过对数据进行输入输出操作,认识 R 中的各种数据类型以及一些运算符和函数。 主要内容有

相关的 R 语言的知识:

请先从 这里 下载教程所需要的数据,并放在一个合适的文件夹中。

前言:数据类型、文件格式、扩展名之间的关系

严格地说,这三者并不是一一对应的。这三者相关的定义可以有很多,但是在本教程中,约定:

但有时,这三者有一些约定的关系,例如后缀名为 csv 的文件,文件格式是“逗号分隔值”,存储的是表类型的数据。 当说到 csv 文件时,可以默认是一个表数据。 所以很多时候这三者会被混用,但仍需注意其本身含义并不一样。

基础数据读写

该部分主要讲解分隔符形式存储的数据,以及 R 特有数据格式文件的读写。某种程度上,空间数据也可以使用基础数据的形式进行存储,因此基础数据的读写时非常重要的。

表数据

表数据,是一种常见的数据类型,用列表示的一个或更多的数据种类,每行包含一个唯一的数据实体,这些数据是被列定义的种类。 表又称“关系”,列又称“字段”,行又称“记录”。一般情况下,同一列中的数据类型是相同的。 在统计学研究中,行对应了一个样本,列对应了样本中的一个属性。 这种数据类型就可以轻松的描述样本的所有特征。

由于表示“表”这一数据类型的文件格式非常多,本节主要讲解以分隔符形式存储的表数据,如 CSV 和 TSV 等,这里以 CSV 文件为例。在 R 中,使用 Excel 文件存储的数据也可以进行读取,请自行参考《R语言空间数据处理与分析实践教程》一书。

逗号分隔值(Comma Seperated Values, CSV)是一个常用的文件格式,以纯文本形式存储表格数据,但该文件格式并没有统一通行的规范。 一般地,该类型的文件一般在第一行记录各列的名字。有的文件也在第一列记录该行的索引,可称该列为“索引列”,索引列一般没有列名。 值得注意的是,严谨的 CSV 文件一般会用到以下四个特殊字符:

当然,最常见的 CSV 文件一般只使用分隔符,这就会造成一些问题。

表数据读取

在 R 中,对 CSV 文件的读取是通过 read.csv 函数完成的。

该语句创建了名为 demo.table 的变量,这个变量中记录了程序读取文件 data/LNHP03.csv 的结果。

在函数 read.csv 中,包含如下几个参数:

参数名类型含义默认值
filecharacter输入文件路径(相对路径或绝对路径) 
headerlogical文件中是否包含列名。如果值是 TRUE ,则认为第一行是列名;否则认为表中没有列名,读取后再进行指定。TRUE
sepcharacter分隔符,
quotecharacter引号"
deccharacter小数点.
comment.charcharacter注释#

在 R 中,如果一个函数各个参数是按照顺序进行赋值的,就不需要填写参数名。如果不是,则需要指定参数名。

在 R 中,具有默认值的参数,在调用函数时,可以不传值。 对于该函数而言,除了第一个参数 file 必须指定以外,其余三个参数在使用时可以不进行指定,此时将采用默认值。 只有当文件中所使用的符号与默认值不同时,才需要进行指定。例如

查看数据

我们通过以下语句可以输出这个变量前5行的值

在输出的表格中,可以很清楚地看到列名、列类型、行名以及各个样本各个字段的值。 变量 demo.table 的类型,是一种名为 data.frame 的类型, R 语言中使用这种数据类型来表示表格数据。

我们可以使用以下操作查看这个表格数据的一些信息

有关 data.frame 类型变量的相关操作,在后续教程中会进行详细的讲解。

数量类型

可以看到,不同的列具有不同的数据类型,有的标注的是 chr ,有的标注的是 dbl 或者 int 。而在参数中,我们有一个参数的类型是 logical ,这就是 R 中的四种数量类型:

这些数据类型的用法和 Python 、 Java 等高级语言并没有太大区别。如果要查看某个数据的类型,可以使用 class 函数,例如

这些数据类型可以进行转换,使用如下一些“as 族”函数:

类似于其他编程语言中的强制类型转换。注意 as.integer 不是四舍五入,而是下取整。

标量类型可以使用常用的运算符,如 + - * /%% 表示取模运算, %\% 表示整除运算,其他一些运算符和别的编程语言没有什么区别。

赋值运算符

在上述语句中,使用了一个运算符 <- ,该运算符的含义与“深拷贝”比较类似,用于将函数返回值保存到变量中,或者将一个变量复制一份保存到零一个变量中。 在 R 语言中,变量赋值前不需要提前声明,随时使用随时创建,所以 =<- 都可以创建变量。 这两个符号在实际使用起来区别不是很大,这里就不进行区分了,但需要注意官方推荐使用 <- 而不是 =

例如,将 demo.table 复制一份到变量 londonhp 中,则使用如下语句

表数据写入

read.csv 进行读取,相应地就有 write.csv 进行写入。先看示例

除了 sep dec 等参数和 read.csv 的含义相同外,该函数具有其他一些参数:

参数名类型含义默认值
xobject要输出到文件中的对象 
filecharacter输出文件路径(相对路径或绝对路径) 
appendlogical是否追加到输出文件中。如果值是 TRUE ,则在已有文件后面继续添加数据;否则重新创建文件。FALSE
quotelogical/vector(numeric)是否将值使用引号引起来,或指定哪几列的值应当使用引号引起来TRUE
sepcharacter分隔符\n
eolcharacter如果某一行某一列的值是 NA,则用什么字符表示"NA"
row.nameslogical/vector(character)是否输出行名,或输出什么行名TRUE
col.nameslogical/vector(character)是否输出列名,或输出什么列名TRUE

向量

值得注意的是,有三个参数可以接收多种类型,而可选的参数类型,都是 vector 类型。该类型是 R 中非常重要的数据类型之一:向量。

向量是一系列数量的一维数组,一般情况下,这些数量具有相同的类型。在 R 中,向量都是列向量。

创建向量的方法有很多,例如

二元运算符 : 表示生成一个向量,向量以第一个操作数作为第一个元素,以后每个元素比前一个元素增 1,并且小于第二个操作数。 所以一般用于生成一个整数序列,为了体现这个特点,本教程称以这种方式生成的向量为“序列”

如果要取某一个向量中的第 个元素,直接使用 变量名[索引] 即可,注意这里的 索引 是从 1 开始计算的,可以是整数、序列、整数类型的向量、逻辑类型的向量。

其中,整数、整数类型的向量中的元素可以是正整数也可以是负整数,如果是负整数,则表示“出去该元素以外的元素”

如果要判断一个值或一组值是否在向量中,使用 %in% 运算符,根据返回的 TRUE 或 FALSE 判断。

向量可以与向量或者数量进行数量运算或逻辑运算。 当向量与数量进行数量运算时,是向量的每个元素与该数量进行运算。 向量与向量进行数量运算时,是两个向量对应位置的元素进行运算,这两个向量不一定要等长,但最好长向量的长度是短向量的长度的整数倍。

向量中元素的类型可以整体进行转换,还是使用“as 族”函数

当向量与数量或向量进行逻辑运算时,运算方法和数量运算还是一样的,但是此时 | & 两个运算符和 || && 两个运算符会产生不一样的结果。

可见,使用 | & 会生成一个结果向量,各个元素是两个变量中各个元素对应运算的结果。而 || && 只是两个向量第一个元素运算的结果。

R 语言二进制数据文件

这是一种 R 语言特有的数据文件,基本相当于以二进制的方式将数据直接存储到文件中,使用时可以直接加载到内存,节省了读取、解析数据的时间。 这种文件常用 rdsrda 作为扩展名。

如果我想将当前的 demo.tablelondonhp 同时保存到一个 rds 文件中,则可使用如下两种方式

该函数具有如下参数

参数名类型含义默认值
...object要输出到文件中的变量。这是一个变长参数,可以接收任意个参数。 
listvector(character)要输出到文件中的变量的名称 
filecharacter输出文件路径(相对路径或绝对路径) 

需要注意的是,该函数具有变长参数,所以只要参数没有指定参数名,都被认为是这个参数的值。 因此,当需要给参数 list 或者 file 传值时,就必须指定该参数的名称,软件才能正确地把参数值传递到对应的参数中。

如果要加载之前保存的变量,就可以使用 load() 函数

这样,output-01/demo2.rds 文件中所保存的变量,就被加载到当前工作空间中。

工作空间是一个非常复杂的问题,这里暂不展开讨论。需要注意的是,R 语言在运行时,会开启一个 R 控制台,又称“节点”,在 RStudio 中又称 Terminal,在 Jupyter Notebook 中又称 Kernel 。 这个节点只要不关闭,就一直保存着所有创建的变量。这些变量所在的地方,就是一个工作空间。

如果想要验证数据是否被加载,此时重启 R 进程(Terminal, Kernel),然后再重新运行如下语句

数据可以被正确加载。可以看到,这样加载数据的方式,比 read.csv 的方式要快,因此推荐比较大的数据可以在使用 read.csv 读取后,保存为 rds 文件。

R 本身还有一些软件包自带了一些数据,例如 iris 数据集。可以使用 data() 函数进行加载

以上数据中出现的 fct 类型也是一种数量类型,指 factor 类型,即“因子”。这里暂不展开讨论。

空间数据的读写

空间数据读写主要指使用 ESRI Shapefile 格式存储的数据(不包含栅格数据)。 存储空间数据的文件格式有很多,除了 ESRI Shapeifle 以外,常用的还有 GeoJSON 、 Geography Markup Language 等,一些数据库也支持存储空间数据。 但仍属 ESRI Shapeifle 最为常用,几乎所有的空间数据分析软件(如 ArcGIS、GeoDa 等)都支持该文件格式。

ESRI Shapefile 文件空间数据的读取

有关 ESRI Shapefile 文件格式,需要注意以下几点:

如果要读取一个 ESRI Shapefile 文件,需要使用 rgdal ,如果没有安装过这个包,则使用函数 install.packages(rgdal) 进行安装,参数是包的名字。

空间点数据

使用 rgdal 包中的 readOGR() 函数读取一个空间点数据

如果经常使用 rgdal 包中的函数,可以先使用 library() 函数将包进行加载,然后直接使用 readOGR() 函数读取数据。

readOGR() 函数的参数其实有很多,但是如果读取 ESRI Shapefile 文件的话,一般只需要这一个参数即可。所以关于其他参数,这里就不展开讲解了。

可以输出一下 demo.point 变量的值,来查看该数据的详情

该数据也类似于一个表数据,但是之前多了一列 coordinates 实际上,该变量的类型是 SpatialPointDataFrame ,类似于一种自定义的结构体,一般具有以下几个“插槽”(类似于结构体中的属性)

和结构体类似,每个插槽也有不同的数据类型。通过如下方式获取这些插槽的值

矩阵

注意 @bbox 插槽的值,和 @coords 插槽的值

可以看到,该插槽值的类型是 matrix ,这是 R 中非常重要的数据类型之一:矩阵。 矩阵是一系列数量的二维数组,一般情况下,这些数量具有相同的类型。

矩阵的创建一般是通过 matrix() 函数进行的,该函数指明了矩阵中的数据、行数、列数等信息,以及数据是按照行优先还是列优先的方式给出的。

该函数具有如下参数

参数名类型含义默认值
datanumeric/charactor/vector矩阵的数据。如果传入的是数量,则所有元素初始化为该值。如果传入的是向量,则按照 byrow 指定的顺序填入矩阵各个元素中。NA
nrownumeric行数1
ncolnumeric列数1
byrowlogical元素赋值的顺序。如果为 TRUE 则数据一行一行地填入;否则,数据将一列一列地填入。FALSE

可以通过运算符 [] 获取矩阵中一个区域的值,使用方法是 变量名[行号,列号] ,这里的行号和列号可以是整数、序列、整型向量、逻辑型向量,还可以不填(表示所有),可以实是正整数也可以是负整数。

可以使用 %in% 运算符判断元素在不在矩阵中

矩阵可以使用 + - * / 四个运算符,这时两个矩阵必须形状一样,进行元素对应运算。 矩阵乘法使用的 %*% 运算符,例如

运算符 %*% 也可以用于向量和矩阵。因此上面这条语句相当于

这里面使用了一个函数 t() 表示转置,向量经过转置后,就形成了 matrix 类型。 这个函数可以转置很多类型的变量,包括 vector, matrix, 以及 data.frame 类型。

矩阵也是可以有行名和列名的,虽然一般不用,但是在将矩阵转换为 data.frame 时会用到。可以通过如下方式设置列名和行名,但可以不必同时设置

矩阵同样可以使用“as族”函数进行数据类型转换。此外,还可以使用 as.data.frame() 函数将矩阵转换为 data.frame ,例如

将表数据转换为空间数据

有些情况下,CSV 文件也被用来存储空间数据,一般是空间点数据。我们可以通过一些方法,将这种表数据转换成空间点数据。 这里需要用到函数包 sp 中的 coordinates()proj4string() 两个函数。

这里使用到了一个 ~ 多元运算符,该运算符的作用是生成一个 formula 类型的变量。 从名字可以看出来,这个类型的变量表示一个数学公式, ~ 两边可以有不定数量的字段名,不同函数对这一类型变量在 ~ 两边的字段名数量不同。 对于 coordinates() 函数,接收右边有两个字段名的变量,这两个变量分别表示 坐标。

proj4string() 函数接收一个 Proj4 格式的字符串,不同坐标系的字符串表示方法不同,可以在 SpatialReference 网站上查到。

将空间数据输出到文件中

既然有 readOGR() 函数,相应的就有 writeOGR() 函数,该函数可以将 Spatial*DataFrame 类型的变量输出到 ESRI Shapefile 格式的文件中。

该函数具有如下参数:

参数名类型含义默认值
objSpatial*DataFrame要输出到文件中的变量。 
dsncharacter输出文件的路径 
layercharacter输出图层的名称 
drivercharacter输出文件的格式,不是扩展名。 
overwrite_layerlogical是否覆盖图层FALSE

上述命令在重复执行时,会报错,原因是参数 overwrite_layer 默认值是 FALSE ,导致函数默认以追加的方式写入。 如果输出文件已经存在,则需要将 overwrite_layer 参数指定为 TRUE

附注:rgdal 支持的格式

readOGR()writeOGR() 支持相当多的格式

因此,有时 ESRI Shapefile 并不一定是最好的选择,尤其考虑到其字段名不能超过 10 个字符的限制。