本节将分为两部分:“基础数据读写”和“空间数据读写”。通过对数据进行输入输出操作,认识 R 中的各种数据类型以及一些运算符和函数。 主要内容有
相关的 R 语言的知识:
<-
:
[]
%in%
运算符请先从 这里 下载教程所需要的数据,并放在一个合适的文件夹中。
严格地说,这三者并不是一一对应的。这三者相关的定义可以有很多,但是在本教程中,约定:
.
符号后的字符串,一般用于让计算机系统便于选择合适的软件打开该文件。例如,使用 Word 软件创建的文档,如果后缀名为 docx
,则系统默认使用 Word 软件打开。但如果后缀名改为 zip
,则系统默认使用解压缩软件打开。但有时,这三者有一些约定的关系,例如后缀名为 csv
的文件,文件格式是“逗号分隔值”,存储的是表类型的数据。
当说到 csv
文件时,可以默认是一个表数据。
所以很多时候这三者会被混用,但仍需注意其本身含义并不一样。
该部分主要讲解分隔符形式存储的数据,以及 R 特有数据格式文件的读写。某种程度上,空间数据也可以使用基础数据的形式进行存储,因此基础数据的读写时非常重要的。
表数据,是一种常见的数据类型,用列表示的一个或更多的数据种类,每行包含一个唯一的数据实体,这些数据是被列定义的种类。 表又称“关系”,列又称“字段”,行又称“记录”。一般情况下,同一列中的数据类型是相同的。 在统计学研究中,行对应了一个样本,列对应了样本中的一个属性。 这种数据类型就可以轻松的描述样本的所有特征。
由于表示“表”这一数据类型的文件格式非常多,本节主要讲解以分隔符形式存储的表数据,如 CSV 和 TSV 等,这里以 CSV 文件为例。在 R 中,使用 Excel 文件存储的数据也可以进行读取,请自行参考《R语言空间数据处理与分析实践教程》一书。
逗号分隔值(Comma Seperated Values, CSV)是一个常用的文件格式,以纯文本形式存储表格数据,但该文件格式并没有统一通行的规范。 一般地,该类型的文件一般在第一行记录各列的名字。有的文件也在第一列记录该行的索引,可称该列为“索引列”,索引列一般没有列名。 值得注意的是,严谨的 CSV 文件一般会用到以下四个特殊字符:
,
,用于分隔不同列的名称和值"
,在一对引号中的所有内容都被认为是同一个字段的值,其中的 ,
也被认为是字段值的一部分#
,出现在该符号后面的文字认为是注释,不算在字段值内.
,用于分隔数字类型值得整数部分和小数部分当然,最常见的 CSV 文件一般只使用分隔符,这就会造成一些问题。
在 R 中,对 CSV 文件的读取是通过 read.csv
函数完成的。
demo.table <- read.csv(file = "data/LNHP03.csv", header = TRUE, sep = ",", quote = "\"", dec = ".", comment.char = "#")
该语句创建了名为 demo.table
的变量,这个变量中记录了程序读取文件 data/LNHP03.csv
的结果。
在函数 read.csv
中,包含如下几个参数:
参数名 | 类型 | 含义 | 默认值 |
---|---|---|---|
file | character | 输入文件路径(相对路径或绝对路径) | |
header | logical | 文件中是否包含列名。如果值是 TRUE ,则认为第一行是列名;否则认为表中没有列名,读取后再进行指定。 | TRUE |
sep | character | 分隔符 | , |
quote | character | 引号 | " |
dec | character | 小数点 | . |
comment.char | character | 注释 | # |
在 R 中,如果一个函数各个参数是按照顺序进行赋值的,就不需要填写参数名。如果不是,则需要指定参数名。
在 R 中,具有默认值的参数,在调用函数时,可以不传值。
对于该函数而言,除了第一个参数 file
必须指定以外,其余三个参数在使用时可以不进行指定,此时将采用默认值。
只有当文件中所使用的符号与默认值不同时,才需要进行指定。例如
xxxxxxxxxx
demo.table <- read.csv("data/LNHP03.csv")
我们通过以下语句可以输出这个变量前5行的值
xxxxxxxxxx
head(demo.table)
在输出的表格中,可以很清楚地看到列名、列类型、行名以及各个样本各个字段的值。
变量 demo.table
的类型,是一种名为 data.frame
的类型, R 语言中使用这种数据类型来表示表格数据。
我们可以使用以下操作查看这个表格数据的一些信息
xxxxxxxxxx
nrow(demo.table) # 行数
ncol(demo.table) # 列数
rownames(demo.table) # 行名
colnames(demo.table) # 列名
有关 data.frame
类型变量的相关操作,在后续教程中会进行详细的讲解。
可以看到,不同的列具有不同的数据类型,有的标注的是 chr
,有的标注的是 dbl
或者 int
。而在参数中,我们有一个参数的类型是 logical
,这就是 R 中的四种数量类型:
chr
代表字符串(character),表示文本dbl
代表浮点数(numeric),也是 R 中数字字面量的默认类型。该类型有几个特殊取值,Inf
表示无限数,NaN
表示“Note a Number”,可能是由于计算错误导致的。int
代表整数logical
该表逻辑值,取值为 TRUE
或 FALSE
或 NA
。TRUE
可以简写为 T
,FALSE
可以简写为 F
。这些数据类型的用法和 Python 、 Java 等高级语言并没有太大区别。如果要查看某个数据的类型,可以使用 class
函数,例如
xxxxxxxxxx
class(1)
class(1.0)
class("1")
这些数据类型可以进行转换,使用如下一些“as 族”函数:
类似于其他编程语言中的强制类型转换。注意 as.integer
不是四舍五入,而是下取整。
xxxxxxxxxx
as.character(1.5)
as.numeric("1.5")
as.integer(1.5)
标量类型可以使用常用的运算符,如 +
-
*
/
,%%
表示取模运算, %\%
表示整除运算,其他一些运算符和别的编程语言没有什么区别。
xxxxxxxxxx
1+2
1*2
1/2
10 %% 3
10 %/% 3
1 > 1
1 >= 1
TRUE | FALSE
TRUE & FALSE
TRUE || FALSE
TRUE && FALSE
在上述语句中,使用了一个运算符 <-
,该运算符的含义与“深拷贝”比较类似,用于将函数返回值保存到变量中,或者将一个变量复制一份保存到零一个变量中。
在 R 语言中,变量赋值前不需要提前声明,随时使用随时创建,所以 =
和 <-
都可以创建变量。
这两个符号在实际使用起来区别不是很大,这里就不进行区分了,但需要注意官方推荐使用 <-
而不是 =
。
例如,将 demo.table
复制一份到变量 londonhp
中,则使用如下语句
xxxxxxxxxx
londonhp <- demo.table
有 read.csv
进行读取,相应地就有 write.csv
进行写入。先看示例
xxxxxxxxxx
write.table(x = demo.table, file = "output-01/demo.table.csv", append = FALSE,
quote = TRUE, sep = ",", eol = "\n", na = "NA", dec = ".",
row.names = FALSE, col.names = TRUE)
除了 sep
dec
等参数和 read.csv
的含义相同外,该函数具有其他一些参数:
参数名 | 类型 | 含义 | 默认值 |
---|---|---|---|
x | object | 要输出到文件中的对象 | |
file | character | 输出文件路径(相对路径或绝对路径) | |
append | logical | 是否追加到输出文件中。如果值是 TRUE ,则在已有文件后面继续添加数据;否则重新创建文件。 | FALSE |
quote | logical/vector(numeric) | 是否将值使用引号引起来,或指定哪几列的值应当使用引号引起来 | TRUE |
sep | character | 分隔符 | \n |
eol | character | 如果某一行某一列的值是 NA ,则用什么字符表示 | "NA" |
row.names | logical/vector(character) | 是否输出行名,或输出什么行名 | TRUE |
col.names | logical/vector(character) | 是否输出列名,或输出什么列名 | TRUE |
值得注意的是,有三个参数可以接收多种类型,而可选的参数类型,都是 vector
类型。该类型是 R 中非常重要的数据类型之一:向量。
向量是一系列数量的一维数组,一般情况下,这些数量具有相同的类型。在 R 中,向量都是列向量。
创建向量的方法有很多,例如
xxxxxxxxxx
c(1,2,3,4) # 将一组值组合到一个向量中
c(1:4)
numeric(10) # 创建 10 个元素的 numeric 类型的向量,默认值全为 0
c("x", "y")
character(4) # 创建 4 个元素的 character 类型的向量,默认值全为空字符串
二元运算符 :
表示生成一个向量,向量以第一个操作数作为第一个元素,以后每个元素比前一个元素增 1,并且小于第二个操作数。
所以一般用于生成一个整数序列,为了体现这个特点,本教程称以这种方式生成的向量为“序列”
xxxxxxxxxx
1.1:4.0
如果要取某一个向量中的第 个元素,直接使用 变量名[索引]
即可,注意这里的 索引
是从 1 开始计算的,可以是整数、序列、整数类型的向量、逻辑类型的向量。
xxxxxxxxxx
demo.vector.numeric <- c(1:10)
demo.vector.numeric
demo.vector.numeric[1]
demo.vector.numeric[5:6]
demo.vector.numeric[c(F, T, T, F, F, T, T, F, F, T)]
其中,整数、整数类型的向量中的元素可以是正整数也可以是负整数,如果是负整数,则表示“出去该元素以外的元素”
xxxxxxxxxx
demo.vector.numeric[-c(1:2)]
如果要判断一个值或一组值是否在向量中,使用 %in%
运算符,根据返回的 TRUE 或 FALSE 判断。
xxxxxxxxxx
1 %in% demo.vector.numeric
c(1,5,20) %in% demo.vector.numeric
向量可以与向量或者数量进行数量运算或逻辑运算。 当向量与数量进行数量运算时,是向量的每个元素与该数量进行运算。 向量与向量进行数量运算时,是两个向量对应位置的元素进行运算,这两个向量不一定要等长,但最好长向量的长度是短向量的长度的整数倍。
xxxxxxxxxx
c(1:5) + 1
c(1:5) - 1
c(1:5) * 2
c(1:5) / 10
c(1:5) %% 10
c(1:5) %/% 10
c(1:5) > 2
c(1:5) + c(1:10)
c(1:5) * c(1:10)
向量中元素的类型可以整体进行转换,还是使用“as 族”函数
xxxxxxxxxx
as.character(c(1:5))
as.integer(c(1.1:4.0))
as.numeric(c("1.0", "1.3", "1.5"))
当向量与数量或向量进行逻辑运算时,运算方法和数量运算还是一样的,但是此时 |
&
两个运算符和 ||
&&
两个运算符会产生不一样的结果。
xxxxxxxxxx
c(TRUE, TRUE, FALSE, FALSE) | c(TRUE, FALSE, TRUE, FALSE)
c(TRUE, TRUE, FALSE, FALSE) || c(TRUE, FALSE, TRUE, FALSE)
c(TRUE, TRUE, FALSE, FALSE) || c(FALSE, FALSE, TRUE, FALSE)
c(FALSE, TRUE, FALSE, FALSE) || c(FALSE, FALSE, TRUE, FALSE)
c(TRUE, TRUE, FALSE, FALSE) & c(TRUE, FALSE, TRUE, FALSE)
c(TRUE, TRUE, FALSE, FALSE) && c(TRUE, FALSE, TRUE, FALSE)
c(TRUE, TRUE, FALSE, FALSE) && c(FALSE, FALSE, TRUE, FALSE)
可见,使用 |
&
会生成一个结果向量,各个元素是两个变量中各个元素对应运算的结果。而 ||
&&
只是两个向量第一个元素运算的结果。
这是一种 R 语言特有的数据文件,基本相当于以二进制的方式将数据直接存储到文件中,使用时可以直接加载到内存,节省了读取、解析数据的时间。
这种文件常用 rds
或 rda
作为扩展名。
如果我想将当前的 demo.table
和 londonhp
同时保存到一个 rds 文件中,则可使用如下两种方式
xxxxxxxxxx
save(demo.table, londonhp, file = "output-01/demo1.rds")
save(list = c("demo.table", "londonhp"), file = "output-01/demo2.rds")
该函数具有如下参数
参数名 | 类型 | 含义 | 默认值 |
---|---|---|---|
... | object | 要输出到文件中的变量。这是一个变长参数,可以接收任意个参数。 | |
list | vector(character) | 要输出到文件中的变量的名称 | |
file | character | 输出文件路径(相对路径或绝对路径) |
需要注意的是,该函数具有变长参数,所以只要参数没有指定参数名,都被认为是这个参数的值。
因此,当需要给参数 list
或者 file
传值时,就必须指定该参数的名称,软件才能正确地把参数值传递到对应的参数中。
如果要加载之前保存的变量,就可以使用 load()
函数
xxxxxxxxxx
load("output-01/demo1.rds")
这样,output-01/demo2.rds
文件中所保存的变量,就被加载到当前工作空间中。
工作空间是一个非常复杂的问题,这里暂不展开讨论。需要注意的是,R 语言在运行时,会开启一个 R 控制台,又称“节点”,在 RStudio 中又称 Terminal,在 Jupyter Notebook 中又称 Kernel 。 这个节点只要不关闭,就一直保存着所有创建的变量。这些变量所在的地方,就是一个工作空间。
如果想要验证数据是否被加载,此时重启 R 进程(Terminal, Kernel),然后再重新运行如下语句
xxxxxxxxxx
load("output-01/demo2.rds") # 或者 load("output-01/demo2.rds")
head(londonhp)
数据可以被正确加载。可以看到,这样加载数据的方式,比 read.csv
的方式要快,因此推荐比较大的数据可以在使用 read.csv
读取后,保存为 rds
文件。
R 本身还有一些软件包自带了一些数据,例如 iris
数据集。可以使用 data()
函数进行加载
xxxxxxxxxx
data(iris)
head(iris)
以上数据中出现的
fct
类型也是一种数量类型,指 factor 类型,即“因子”。这里暂不展开讨论。
空间数据读写主要指使用 ESRI Shapefile 格式存储的数据(不包含栅格数据)。 存储空间数据的文件格式有很多,除了 ESRI Shapeifle 以外,常用的还有 GeoJSON 、 Geography Markup Language 等,一些数据库也支持存储空间数据。 但仍属 ESRI Shapeifle 最为常用,几乎所有的空间数据分析软件(如 ArcGIS、GeoDa 等)都支持该文件格式。
有关 ESRI Shapefile 文件格式,需要注意以下几点:
shp
shx
dbf
prj
sbn
sbx
。如果要读取一个 ESRI Shapefile 文件,需要使用 rgdal
,如果没有安装过这个包,则使用函数 install.packages(rgdal)
进行安装,参数是包的名字。
使用 rgdal
包中的 readOGR()
函数读取一个空间点数据
xxxxxxxxxx
demo.point <- rgdal::readOGR("data/LNHP03.shp")
如果经常使用 rgdal
包中的函数,可以先使用 library()
函数将包进行加载,然后直接使用 readOGR()
函数读取数据。
xxxxxxxxxx
library(rgdal)
demo.point <- readOGR("data/LNHP03.shp")
readOGR()
函数的参数其实有很多,但是如果读取 ESRI Shapefile 文件的话,一般只需要这一个参数即可。所以关于其他参数,这里就不展开讲解了。
可以输出一下 demo.point
变量的值,来查看该数据的详情
xxxxxxxxxx
demo.point
该数据也类似于一个表数据,但是之前多了一列 coordinates
。
实际上,该变量的类型是 SpatialPointDataFrame
,类似于一种自定义的结构体,一般具有以下几个“插槽”(类似于结构体中的属性)
@bbox
表示数据的包围盒@proj4string
表示数据的坐标系@coords
表示每个要素的坐标@data
表示每个要素对应的属性值和结构体类似,每个插槽也有不同的数据类型。通过如下方式获取这些插槽的值
xxxxxxxxxx
demo.point@bbox
demo.point@proj4string
head(demo.point@coords)
head(demo.point@data)
注意 @bbox
插槽的值,和 @coords
插槽的值
xxxxxxxxxx
demo.point@bbox
可以看到,该插槽值的类型是 matrix
,这是 R 中非常重要的数据类型之一:矩阵。
矩阵是一系列数量的二维数组,一般情况下,这些数量具有相同的类型。
矩阵的创建一般是通过 matrix()
函数进行的,该函数指明了矩阵中的数据、行数、列数等信息,以及数据是按照行优先还是列优先的方式给出的。
xxxxxxxxxx
demo.matrix.numeric <- matrix(data = c(1:12), nrow = 3, ncol = 4, byrow = FALSE)
demo.matrix.numeric
该函数具有如下参数
参数名 | 类型 | 含义 | 默认值 |
---|---|---|---|
data | numeric/charactor/vector | 矩阵的数据。如果传入的是数量,则所有元素初始化为该值。如果传入的是向量,则按照 byrow 指定的顺序填入矩阵各个元素中。 | NA |
nrow | numeric | 行数 | 1 |
ncol | numeric | 列数 | 1 |
byrow | logical | 元素赋值的顺序。如果为 TRUE 则数据一行一行地填入;否则,数据将一列一列地填入。 | FALSE |
可以通过运算符 []
获取矩阵中一个区域的值,使用方法是 变量名[行号,列号]
,这里的行号和列号可以是整数、序列、整型向量、逻辑型向量,还可以不填(表示所有),可以实是正整数也可以是负整数。
xxxxxxxxxx
demo.matrix.numeric[1:2, ] # 取 1、2 行
demo.matrix.numeric[, 1:2] # 取 1、2 列
demo.matrix.numeric[1, 2] # 取第 1 行第 2 个元素
demo.matrix.numeric[1, -2] # 取第 1 行除第 2 个元素以外的元素
demo.matrix.numeric[c(1:3) > 1, ] # 取第 2 行之后的所有行
可以使用 %in%
运算符判断元素在不在矩阵中
xxxxxxxxxx
1:2 %in% demo.matrix.numeric
矩阵可以使用 +
-
*
/
四个运算符,这时两个矩阵必须形状一样,进行元素对应运算。
矩阵乘法使用的 %*%
运算符,例如
xxxxxxxxxx
matrix(1:3, nrow = 3) %*% matrix(1:5, ncol = 5)
运算符 %*%
也可以用于向量和矩阵。因此上面这条语句相当于
xxxxxxxxxx
c(1:3) %*% t(c(1:5))
这里面使用了一个函数 t()
表示转置,向量经过转置后,就形成了 matrix
类型。
这个函数可以转置很多类型的变量,包括 vector
, matrix
, 以及 data.frame
类型。
矩阵也是可以有行名和列名的,虽然一般不用,但是在将矩阵转换为 data.frame
时会用到。可以通过如下方式设置列名和行名,但可以不必同时设置
xxxxxxxxxx
rownames(demo.matrix.numeric) <- c("r1", "r2", "r3")
colnames(demo.matrix.numeric) <- c("c1", "c2", "c3", "c4")
demo.matrix.numeric
矩阵同样可以使用“as族”函数进行数据类型转换。此外,还可以使用 as.data.frame()
函数将矩阵转换为 data.frame
,例如
xxxxxxxxxx
as.data.frame(demo.matrix.numeric)
有些情况下,CSV 文件也被用来存储空间数据,一般是空间点数据。我们可以通过一些方法,将这种表数据转换成空间点数据。
这里需要用到函数包 sp
中的 coordinates()
和 proj4string()
两个函数。
xxxxxxxxxx
library(sp)
demo.csv.point <- read.csv("data/WHSHP.csv")
coordinates(demo.csv.point) <- ~ lon + lat # 设置坐标所存在的列
proj4string(demo.csv.point) <- "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs" # 设置空间参考
class(demo.csv.point)
这里使用到了一个 ~
多元运算符,该运算符的作用是生成一个 formula
类型的变量。
从名字可以看出来,这个类型的变量表示一个数学公式, ~
两边可以有不定数量的字段名,不同函数对这一类型变量在 ~
两边的字段名数量不同。
对于 coordinates()
函数,接收右边有两个字段名的变量,这两个变量分别表示 和 坐标。
proj4string()
函数接收一个 Proj4 格式的字符串,不同坐标系的字符串表示方法不同,可以在 SpatialReference 网站上查到。
既然有 readOGR()
函数,相应的就有 writeOGR()
函数,该函数可以将 Spatial*DataFrame
类型的变量输出到 ESRI Shapefile 格式的文件中。
xxxxxxxxxx
writeOGR(demo.csv.point, "output-01/WHSHP.shp", layer = "WHSHP", driver = "ESRI Shapefile")
该函数具有如下参数:
参数名 | 类型 | 含义 | 默认值 |
---|---|---|---|
obj | Spatial*DataFrame | 要输出到文件中的变量。 | |
dsn | character | 输出文件的路径 | |
layer | character | 输出图层的名称 | |
driver | character | 输出文件的格式,不是扩展名。 | |
overwrite_layer | logical | 是否覆盖图层 | FALSE |
上述命令在重复执行时,会报错,原因是参数 overwrite_layer
默认值是 FALSE
,导致函数默认以追加的方式写入。
如果输出文件已经存在,则需要将 overwrite_layer
参数指定为 TRUE
。
readOGR()
和 writeOGR()
支持相当多的格式
因此,有时 ESRI Shapefile 并不一定是最好的选择,尤其考虑到其字段名不能超过 10 个字符的限制。