第 5 章 数据的导入与导出

5.1 R载入文件中的数据

5.1.1 问题

你想从文件中载入数据。

5.1.2 方案

5.1.2.1 带分隔符的文本文件

最简单的输入数据的方式就是将其保存为带分隔符(如:制表位或逗号)的文本文件。

data <- read.csv("datafile.csv")

# 导入一个没有表头的 CSV 文件
data <- read.csv("datafile-noheader.csv", header=FALSE)

函数 read.table() 是一个更为常用的函数。它允许你设置分隔符,不管该文件是否有表头或者字符串是否有引号,等等。使用 ?read.table 查看更多详细信息。

data <- read.table("datafile-noheader.csv",
                   header=FALSE,
                   sep=","         # 制表位分隔的文件用 "\t"
)

5.1.2.2 打开文件的文件选择器

有些平台可以使用 file.choose() 命令打开文件选择的对话窗口;而另一些平台则只会提示用户输入一个文件名。

data <- read.csv(file.choose())

5.1.2.3 把字符串看作因子(factor)或字符(character)

默认条件下,数据中的字符串都被转换为因子。如果你用 read.csv() 载入数据,所有的文本列都会被视为因子,而实际上某些文本列被处理为字符串才更为合理。要实现这一点,可以设定 stringsAsFactors=FALSE:

data <- read.csv("datafile.csv", stringsAsFactors=FALSE)

# 将某一列转化为因子
data$Sex <- factor(data$Sex)

另一种将全部文本列加载为因子,再把其中一些转换为字符的方法:

data <- read.csv("datafile.csv")

data$First <- as.character(data$First)
data$Last  <- as.character(data$Last)

# 另一种方法:转化名为 “First” 和 “Last” 的两列
stringcols <- c("First","Last")
data[stringcols] <- lapply(data[stringcols], as.character)

5.1.2.4 从网上导入文件

也可以从 URL 加载数据。下面这些(很长的) URL 可以描述了将要加载的相关文件。

data <- read.csv("http://www.cookbook-r.com/Data_input_and_output/Loading_data_from_a_file/datafile.csv")


# 读取没有表头的 CSV 文件
data <- read.csv("http://www.cookbook-r.com/Data_input_and_output/Loading_data_from_a_file/datafile-noheader.csv", 
  header = FALSE)

# 手动添加表头
names(data) <- c("First", "Last", "Sex", "Number")

上述所使用的数据文件及内容:

  • datafile.csv

    "First","Last","Sex","Number"
    "Currer","Bell","F",2
    "Dr.","Seuss","M",49
    "","Student",NA,21
  • datafile-noheader.csv:

    "Currer","Bell","F",2
    "Dr.","Seuss","M",49
    "","Student",NA,21

5.1.2.5 定宽文本文件

假如你的数据列宽固定,如下:

  First     Last  Sex Number
 Currer     Bell    F      2
    Dr.    Seuss    M     49
    ""   Student   NA     21

读取这种数据的一种方式是简单地使用 read.table() 函数 strip.white=TRUE,可以清除额外的空格。

read.table("clipboard", header=TRUE, strip.white=TRUE)

然而,数据文件里的某些列可能含有空格,也可能不包含空格。比如说下面的数据里,scores 列表示六个不同的测量值,每一个从 0 到 3 。

subject  sex  scores
   N  1    M  113311
   NE 2    F  112231
   S  3    F  111221
   W  4    M  011002

这种情况,你可能需要使用 read.fwf() 函数。如果你读的列名来自于文件,按照要求它们需要用分隔符(如:制表位,空格,逗号)分开。如果它们之间是通过多个空格来隔开的(如下例),你需要直接指定列的名称。

# 指定列的名称
read.fwf("myfile.txt",
         c(7,5,-2,1,1,1,1,1,1), # 列的宽度,-2意味着放弃这些列
         skip=1,                # 跳过第一行(包括表头)
         col.names=c("subject","sex","s1","s2","s3","s4","s5","s6"),
         strip.white=TRUE)      # 跳过每个数据的前导和尾随空格
#>   subject sex s1 s2 s3 s4 s5 s6
#> 1    N  1   M  1  1  3  3  1  1
#> 2    NE 2   F  1  1  2  2  3  1
#> 3    S  3   F  1  1  1  2  2  1
#> 4    W  4   M  0  1  1  0  0  2
# subject sex s1 s2 s3 s4 s5 s6
#    N  1   M  1  1  3  3  1  1
#    NE 2   F  1  1  2  2  3  1
#    S  3   F  1  1  1  2  2  1
#    W  4   M  0  1  1  0  0  2

# 如果第一行如下:
# subject,sex,scores
# 我们可以使用 header=TRUE
read.fwf("myfile.txt", c(7,5,-2,1,1,1,1,1,1), header=TRUE, strip.white=TRUE)
#> Error in read.table(file = FILE, header = header, sep = sep, row.names = row.names, : more columns than column names
# 错误:列数比列名数量多

5.1.2.6 Excel 文件

gdata 包里的 read.xls() 函数可以读取 Excel 文件。

library(gdata)
data <- read.xls("data.xls")

gdata 包使用说明见 http://cran.r-project.org/doc/manuals/R-data.html#Reading-Excel-spreadsheets

包的安装,见安装和使用R包

5.1.2.7 SPSS 数据

foreign 包里的 read.spss() 函数可以读取 SPSS 文件。

library(foreign)
data <- read.spss("data.sav", to.data.frame=TRUE)

5.2 通过键盘和剪贴板把数据载入并保存到 R

5.2.1 问题

你想用键盘输入数据,而不是从文件中载入。

5.2.2 方案

5.2.2.1 数据输入

假如你的数据如下:

size weight cost
  small      5    6
 medium      8   10
  large     11    9
5.2.2.1.1 通过键盘输入或从剪贴板载入数据

从键盘上输入的方法之一是从标准输入读取( stdin() )。

# 使用 read.table 和 stdin() 剪切和粘贴
data <- read.table(stdin(), header=TRUE)
# 系统将提示您输入:在这复制和粘贴文本

# 或者:
# data <- read.csv(stdin())

你也可以直接从剪贴板载入:

# 首先将数据复制到剪贴板
data <- read.table('clipboard', header=TRUE)

# 或者:
# data <- read.csv('clipboard')
5.2.2.1.2 在脚本中载入数据

前面的方法不能用来加载脚本文件中的数据,因为输入(通过键入或粘贴)必须发生在运行该命令之后。

# 使用 read.table()
data <- read.table(header = TRUE, text = "
  size weight cost
   small      5    6
  medium      8   10
   large     11    9
 ")

不同的数据格式(如:以逗号分隔,没有表头,等等),选择 read.table() 可以设置。

阅读R载入文件中的数据查看更多信息。

5.2.2.2 数据输出

默认情况下,R 会打印行名称。当你希望打印出来的表格可以被复制粘贴,那么最好将这一设定关闭。

print(data, row.names = FALSE)
#>    size weight cost
#>   small      5    6
#>  medium      8   10
#>   large     11    9
5.2.2.2.1 写入可以复制粘贴或粘贴到剪贴板的数据

可以将带分隔符的数据写入终端( stdout() ),这样它就可以被复制粘贴到其他地方,也可以直接写入到剪贴板。

write.csv(data, stdout(), row.names=FALSE)
# "size","weight","cost"
# "small",5,6
# "medium",8,10
# "large",11,9


# 写到剪贴板(不支持 Mac 或 Unix)
write.csv(data, 'clipboard', row.names=FALSE)
5.2.2.2.2 输出 R 中的载入数据

如果数据已经加载到 R,可以使用 dput() 保存数据。通过 dput() 得到的输出是一个命令,可以重建数据结构。这种方法的优点是,它可以保持各种数据类型的修改。举个例子,如果有一列包含各种数字的数据,并且你已经将这一列转化成了因子。这种方法将会在保留该数据类型的同时,只加载文本表格(如上所示)并将把它处理为数字。

# 假如你已经载入数据

dput(data)
#> structure(list(size = structure(c(3L, 2L, 1L), .Label = c("large",
#> "medium", "small"), class = "factor"), weight = c(5L, 8L, 11L
#> ), cost = c(6L, 10L, 9L)), .Names = c("size", "weight", "cost"
#> ), class = "data.frame", row.names = c(NA, -3L))

# 之后,我们可以使用 dput 中输出,重新创建数据结构
newdata <- structure(list(size = structure(c(3L, 2L, 1L), .Label = c("large",
  "medium", "small"), class = "factor"), weight = c(5L, 8L, 11L
  ), cost = c(6L, 10L, 9L)), .Names = c("size", "weight", "cost"
  ), class = "data.frame", row.names = c(NA, -3L))

5.3 运行 R 脚本

5.3.1 问题

你想从文本文件运行 R 代码

5.3.2 方案

使用 source() 函数。

# 首先,找到正确的目录
setwd('/home/username/desktop/rcode')

source('analyze.r')

请注意,如果你想让你的脚本生成文本输出,你必须使用 print()cat() 函数。

x <- 1:10

# 这在脚本中,不会启动任何命令
x
#>  [1]  1  2  3  4  5  6  7  8  9 10

# 使用print()函数:
print(x)
#>  [1]  1  2  3  4  5  6  7  8  9 10

# 更简单的输出: 没有行/列,没有文本
cat(x)
#> 1 2 3 4 5 6 7 8 9 10

另一种代替方法是:运行 source() 并加上 print.eval=TRUE 选项。

5.4 用 R 把数据写入文件

5.4.1 问题

你想把数据写入一个文件。

5.4.2 方案

5.4.2.1 写到有分隔符的文本文件

最简单的方式是用 write.csv() 。默认情况下,write.csv() 包含行名,但是这通常没必要,而且可能会导致混乱。

# 一个简单的例子
data <- read.table(header = TRUE, text = "
 subject sex size
     1   M    7
     2   F    NA
     3   F    9
     4   M   11
 ")

# 写入到文件,不显示行名
write.csv(data, "data.csv", row.names = FALSE)

# 替代 “NA”,输出空格
write.csv(data, "data.csv", row.names = FALSE, na = "")

# 用制表符可以不显示行和列名
write.table(data, "data.csv", sep = "\t", row.names = FALSE, 
  col.names = FALSE)

5.4.2.2 以 R 的数据格式保存

write.csv()write.table() 是最适合与其他数据分析程序交互操作的函数。然而,他们不会保持数据结构中的特殊属性,如:该列是否为一个字符类型或因子,或因子的水平的顺序。为了实现这一点,我们应该将该数据写成适合 R 的特殊格式。

下面是三种主要的方法:

第一个方法是输出 R 源代码,代码运行时,该对象将被重新创建。这种方法应该适用于大部分数据对象,但它可能无法专一地重构一些较为复杂的数据对象。

# 保存在一个能容易被 R 载入的文本文件中
dump("data", "data.Rdmpd")
# 可以同时保存多个对象
dump(c("data", "data1"), "data.Rdmpd")

# 再次加载数据
source("data.Rdmpd")
# 载入时,原始的数据名称将被自动使用

另一个方法是,在 RDS 格式中写入单个数据对象。这种格式可以是二进制或 ASCII。二进制更紧凑,而 ASCII 在配合版本控制系统(如 Git)方面则更有效率。

# 在二进制 RDS 格式中保存一个简单的对象
saveRDS(data, "data.rds")
# 或者使用 ASCII 格式
saveRDS(data, "data.rds", ascii=TRUE)

# 再次载入:
data <- readRDS("data.rds")

也可以将多个对象以 RData 格式保存到一个单一的文件。

# 在二进制 RData 格式中保存多个对象
save(data, file="data.RData")
# 或者使用 ASCII 格式
save(data, file="data.RData", ascii=TRUE)
# 可以保存多个对象
save(data, data1, file="data.RData")

# 再次载入:
load("data.RData")

saveRDS()save() 的一个重要区别:对于前者,当你用 readRDS() 读取数据时,是你指定对象的名称;而对于后者,当你用 load() 载入数据,R 会自动使用该数据原来的对象名称。自动使用原始对象名称有时可以简化工作流程,但这种在不同的环境中都使用相同数据对象名称的设定也会成为一个缺点。

5.5 用 R 写入文本,分析输出到文件

5.5.1 问题

如果你想将输出信息写到文件。

5.5.2 方案

sink() 函数将会重定向输出到一个文件,而不是 R 的终端。请注意,如果您在脚本中使用sink(),并且在结果输出到终端前崩溃,那么你将不会看到任何对你命令的响应。在不带任何参数的情况下调用 sink() ,将返回输出到终端。

# 开始写入到文件
sink('analysis-output.txt')

set.seed(12345)
x <-rnorm(10,10,1)
y <-rnorm(10,11,1)
# 添加一些内容
cat(sprintf("x has %d elements:\n", length(x)))
print(x)
cat("y =", y, "\n")

cat("=============================\n")
cat("T-test between x and y\n")
cat("=============================\n")
t.test(x,y)

# 停止写入
sink()


# 附加到文件
sink('analysis-output.txt', append=TRUE)
cat("Some more stuff here...\n")
sink()

输出文件的内容:

x has 10 elements:
 [1] 10.585529 10.709466  9.890697  9.546503 10.605887  8.182044 10.630099  9.723816
 [9]  9.715840  9.080678
y = 10.88375 12.81731 11.37063 11.52022 10.24947 11.8169 10.11364 10.66842 12.12071 11.29872
=============================
T-test between x and y
=============================

    Welch Two Sample t-test

data:  x and y
t = -3.8326, df = 17.979, p-value = 0.001222
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -2.196802 -0.641042
sample estimates:
mean of x mean of y
 9.867056 11.285978

Some more stuff here...