R 的 data.table::fread 中的 "Select" 参数

"Select" argument in R's data.table::fread

我正在尝试使用 fread() 从 csv 中读取选定的列。我发现我 可以 使用列号向量,但 不能 使用列名。关于 "select" 参数,文档只是说 "Vector of column names or numbers to keep, drop the rest." 他们还提供了

的例子
fread(data, select=c("A","D"))

因此,为什么我的代码会抛出 下标越界 错误?这是我的代码的要点,希望可以推广到其他用户:

test = data.frame(matrix(c(1:50),ncol = 5))
names(test) = c("A", "B", "C", "D", "E")
write.table(test, file = "/Users/me/Desktop/test.txt", sep = ",")
fread("/Users/me/Desktop/test.txt", sep = ",", header = TRUE, select = c("A","B"))

给予

Error in ans[[1]] : subscript out of bounds

然而,这给出了第一列以及行号作为列:

fread("/Users/me/Desktop/test.txt", sep = ",", header = TRUE, select = c(1,2))
    1  1
1:  2  2
2:  3  3
3:  4  4
4:  5  5
5:  6  6
6:  7  7
7:  8  8
8:  9  9
9: 10 10

...并且 read.table() 能够顺利读取整个数据集:

read.table("/Users/me/Desktop/test.txt", sep = ",", header = TRUE)
    A  B  C  D  E
1   1 11 21 31 41
2   2 12 22 32 42
3   3 13 23 33 43
4   4 14 24 34 44
5   5 15 25 35 45
6   6 16 26 36 46
7   7 17 27 37 47
8   8 18 28 38 48
9   9 19 29 39 49
10 10 20 30 40 50

行名和 header 显然有问题,但我不确定如何解决它。我试过使用和不使用 headers。我正在使用的数据集(不在本例中)已经有行名,所以 re-writing rownames = FALSE 不是一个选项。

library(data.table)
library(readr)

# save mtcars as CSV w/o row names or column names

write_csv(mtcars, "mtcars.csv", col_names=FALSE)

# read in the same file with fread but since we tell it
# to not use a header, we have to specify the column names
# the way fread will create them otherwise you get your error

fread("mtcars.csv", header=FALSE, select=c("V1", "V4"))

##      V1  V4
## 1: 21.0 110
## 2: 21.0 110
## 3: 22.8  93
## 4: 21.4 110
## 5: 18.7 175
## 6: 18.1 105

# try again, this time keeping column names in the 
# data file
write.csv(mtcars, file = "mtcars.csv", row.names=FALSE)

# now read it back in and select based on column names
# I picked different columns
head(fread("mtcars.csv", select=c("mpg", "qsec")))

##     mpg  qsec
## 1: 21.0 16.46
## 2: 21.0 17.02
## 3: 22.8 18.61
## 4: 21.4 19.44
## 5: 18.7 17.02
## 6: 18.1 20.2

此答案假设您的原始数据不是通过 write.table() 生成的,您获得了一个文件并试图通过 fread() 读取它(问题中也有说明)。


我相信您遇到这个问题是因为文件中的行名。我还没有想出一个直接的方法来将 fread() 应用于数据,但我认为这个 work-around 是安全的并且不会在效率方面花费你太多。以下是步骤...

1) 读取带有scan()的文件的第一行,并在开头添加一个额外的""元素。这是为了偏移 header 行以说明文件中的行名称。

nm <- c("", scan("test.txt", "", nlines = 1, sep = ","))

2) 定义您想要的列并在 nm 中找到它们。偏移量不再是 1 和 4,而是 2 和 5 并说明了行名称。

sel <- nm %in% c("A", "D")

3) 读取文件,从第二行开始(即没有 header),并在选择参数中使用 sel

library(data.table)
dt <- fread("test.txt", skip = 1, select = which(sel))

4) 现在我们已经读取了我们想要的数据,我们可以重新设置列名了。

setnames(dt, nm[sel])[]
#      A  D
#  1:  1 31
#  2:  2 32
#  3:  3 33
#  4:  4 34
#  5:  5 35
#  6:  6 36
#  7:  7 37
#  8:  8 38
#  9:  9 39
# 10: 10 40

如果您提供的示例很好地代表了实际数据,那么我看不出有任何理由不可行。希望对你有用。

问题不在你的恐惧,而在你的 write.table。 默认情况下,它在第一列中写入每一行的名称。看看写的文件。

试试这个:(明确不写 row.names)

write.table(test, file = "/Users/me/Desktop/test.txt", 
            sep = ",", row.names=FALSE)

然后做你的 fread()。它会起作用。

这个例子说明了为什么您总是需要仔细检查您正在生成的文件的格式。 read.tablefread有一些区别;这里的问题来自于行名以及它们是如何被 write.table 书写的。一如既往,仔细阅读文档 (?write.table) 有很大帮助。

write.table 默认写入行名称。但方法如下:

filename<-"somefilename.txt"
write.table(test, file = filename, sep = ",")
readLines(filename,2)
#[1] "\"A\",\"B\",\"C\",\"D\",\"E\"" 
#"\"1\",1,11,21,31,41"

我阅读了生成文件的前两行。仔细阅读它们,您会发现这不是 "standard" CSV。为什么?因为 header 有 4 个逗号,而 "data" 第 5 行。对于标准 CSV,您应该在第一个列名称 之前放置一个逗号 。这是通过在 write.table:

中添加 col.names=NA 来实现的
write.table(test, file = filename, sep = ",", col.names=NA)
#now works
fread(filename, sep = ",", header = TRUE, select = c("A","B"))

您可以检查并看到现在出现了一个逗号作为文件的第一个字符。或者,您可以避免将 row.names=FALSE 放在 write.table 中的行名称写入,但这并不总是可行的,因为有时它们是有意义的。