读写原生数据文件
在前面的章节中,我们已经介绍了 CSV 文件和 Excel 工作簿的读写函数。这些不
是 R 的原生数据格式,也就是说,原始数据对象和输出文件之间有所不同。
例如,我们想把一个数据框导出为 CSV 格式,其中,这个数据框有很多具有不同的数
据类型的列。那么,在导出过程中,列的类型会被丢失。无论是数字、字符串还是日期,
它总是用文本格式表示。当然了,这可以使我们更容易地从输出文件中直接读取数据,但
最终的读取结果却要依赖于计算机如何猜测这些数据类型。换句话说,读入函数有时很难
把 CSV 格式的数据恢复为与原始格式完全相同的数据框,因为导出过程中已经丢失了列的
类型,反而由此得到了更强的可移植性(例如,一些其他软件也可以读取这个数据)。
如果不考虑可移植性,只用 R 处理数据,那么就可以用原生格式读写数据了。这样,
就不能再用文本编辑器读取数据,也不能从其他软件读取数据,但却可以高效地读写单个
数据对象,甚至是整个环境,而且也不会造成数据丢失。换句话说,原生格式允许在文件
中保存对象,恢复完全相同的数据,不必担心诸如缺失值符号、列的类型、类或者属性等
问题。
1.从原生数据文件读写单个对象
有两组与处理原生数据格式相关的函数。一组是将单个对象写入 RDS 文件或
从 RDS 文件读取单个对象,其中,RDS 文件是一种以序列化形式存储单个 R 对象的文件
格式。另一组则用于处理多个 R 对象,我们将会在下一节中加以介绍。在下面的例子中,
我们将数据 some_data 写入 RDS 文件,再从中读取出来,看一下这两个数据框是否相同。
首先,我们用 saveRDS( )将 some_data 保存为 data/some_data.rds:
saveRDS(some_data, "data/some_data.rds")
然后,从同一个文件中读取数据,并将数据框存储在 some_data2 中:
some_data2 <- readRDS("data/some_data.rds")
最后,使用 identical( )检验一下这两个数据框是不是一样的:
identical(some_data, some_data2)
## [1] TRUE
正如我们预期的那样,这两个数据框是完全相同的。
原生格式有两个明显的优点:空间效率和时间效率。在下面的例子中,我们将创建一
个包含 200000 行随机数据的大型数据框。然后,再分别将数据框保存到 CSV 文件和 RDS
文件中:
rows <- 200000
large_data <- data.frame(id = 1:rows, x = rnorm(rows), y = rnorm(rows))
system.time(write.csv(large_data, "data/large_data.csv"))
## user system elapsed
## 1.33 0.06 1.41
system.time(saveRDS(large_data, "data/large_data.rds"))
## user system elapsed
## 0.23 0.03 0.26
很明显,saveRDS( )的写入效率比write.csv( )高很多。然后使用 file.info( )
查看两个输出文件的大小:
fileinfo <- file.info("data/large_data.csv", "data/large_data.rds")
fileinfo[, "size", drop = FALSE]
## size
## data/large_data.csv 10442030
## data/large_data.rds 3498284
两个文件的大小差异很大——CSV 文件几乎是 RDS 文件的 3 倍,这说明了原生格式
具有更高的存储或空间效率。
最后,我们读取 CSV 和 RDS 文件,并查看两种格式所消耗的时间。要想读取 CSV 文
件,可以使用内置函数 read.csv( )和由 readr 包提供的更快的 read_csv( )函数:
system.time(read.csv("data/large_data.csv"))
## user system elapsed
## 1.46 0.07 1.53
system.time(readr::read_ _csv("data/large_data.csv"))
## user system elapsed
## 0.17 0.01 0.19
令人惊讶的是,在这种情况下,read_csv( )的读取速度几乎是内置函数read.csv( )
的 8 倍。但是如果使用的是原生格式,两个 CSV 读取函数的性能几乎是不可比的:
system.time(readRDS("data/large_data.rds"))
## user system elapsed
## 0.03 0.00 0.03
显然的,原生格式具有更高的读取效率。
此外,saveRDS( )和 readRDS( )不仅能处理数据框,也可以处理任何 R 对象。例
如,我们创建一个有缺失值的数值向量和一个具有嵌套结构的列表。然后,将它们分别保
存到不同的 RDS 文件中:
nums <- c(1.5, 2.5, NA, 3)
list1 <- list(x = c(1, 2, 3),
y = list(a = c("a", "b"),
b = c(NA, 1, 2.5)))
saveRDS(nums, "data/nums.rds")
saveRDS(list1, "data/list1.rds")
现在读取 RDS 文件,这两个对象都能被完全恢复:
readRDS("data/nums.rds")
## [1] 1.5 2.5 NA 3.0
readRDS("data/list1.rds")
## $x
## [1] 1 2 3
##
## $y
## $y$a
## [1] "a" "b"
##
## $y$b
## [1] NA 1.0 2.5
2.保存和恢复工作环境
RDS 格式可以存储单个 R 对象,RData 格式可以存储多个 R 对象。我们可以调用 save( )
将 some_data、nums 和 list1 一起存储在单个 RData 文件中:
save(some_data, nums, list1, file = "data/bundle1.RData")
为了验证这 3 个对象是否能够被存储与恢复,我们可以先移除它们,然后调用 load( )
从文件中恢复对象:
rm(some_data, nums, list1)
load("data/bundle1.RData")
现在,这 3 个对象完全恢复了:
some_data
## id grade width check_date
## 1 1 A 1.51 2016-03-05
## 2 2 A 1.52 2016-03-06
## 3 3 B 1.46 2016-03-10
## 4 4 <NA> NA 2016-03-11
nums
## [1] 1.5 2.5 NA 3.0
list1
## $x
## [1] 1 2 3
##
## $y
## $y$a
## [1] "a" "b"
##
## $y$b
## [1] NA 1.0 2.5
## [1] TRUE TRUE TRUE TRUE TRUE TRUE

浙公网安备 33010602011771号