如何使用fread函数读取CSV文件的特定行

5
我有一个由双精度浮点数组成的大型CSV文件(10百万行,每行500列),我只想读取其中几千行数据(位于1百万到10百万之间的不同位置),这些行由长度为10百万的二进制向量V定义,如果我不想读取该行,则V的值为0,如果我想读取该行,则V的值为1。
我该如何使用data.table包中的io函数fread来实现?我询问是因为与所有其他io方法相比,fread速度非常快。
该问题的最佳解决方案Reading specific rows of large matrix data file提供了以下解决方案: read.csv( pipe( paste0("sed -n '" , paste0( c( 1 , which( V == 1 ) + 1 ) , collapse = "p; " ) , "p' C:/Data/target.csv" , collapse = "" ) ) , head=TRUE)

这里的 C:/Data/target.csv 是一个大型 CSV 文件,V 是由 01 组成的向量。

然而我发现,即使对于总行数只有一小部分为 1 的情况,使用整个矩阵的 fread 相比之下要慢几个数量级。

因此,由于整个矩阵的 fread 会支配上面的解决方案,我应该如何将 fread(特别是 fread)与行抽样相结合?

这不是重复问题,因为它只涉及函数 fread

以下是我的问题设置:

 #create csv
 csv <- do.call(rbind,lapply(1:50,function(i) { rnorm(5) }))
 #my csv has a header:
 colnames(csv) <- LETTERS[1:5]
 #save csv
 write.csv(csv,"/home/user/test_csv.csv",quote=FALSE,row.names=FALSE)
 #create vector of 0s and 1s that I want to read the CSV from
 read_vec <- rep(0,50)
 read_vec[c(1,5,29)] <- 1 #I only want to read in 1st,5th,29th rows
 #the following is the effect that I want, but I want an efficient approach to it:
 csv <- read.csv("/home/user/test_csv.csv") #inefficient!
 csv <- csv[which(read_vec==1),] #inefficient!
 #the alternative approach, too slow when scaled up!
 csv <- fread( pipe( paste0("sed -n '" , paste0( c( 1 , which( read_vec == 1 ) + 1 ) , collapse = "p; " ) , "p' /home/user/test_csv.csv" , collapse = "" ) ) , head=TRUE)
 #the fastest approach yet still not optimal because it needs to read all rows
 require(data.table)
 csv <- data.matrix(fread('/home/user/test_csv.csv'))
 csv <- csv[which(read_vec==1),] 
1个回答

6

此方法采用向量 v(对应您的read_vec),识别要读取的行序列,将其提供给连续调用的fread(...),并使用rbinds将结果汇总。

如果您想要的行在文件中随机分布,则可能不会更快。但是,如果行按块分布(例如,c(1:50, 55, 70, 100:500, 700:1500)),则调用fread(...)的次数较少,您可能会看到显着的改进。

# create sample dataset
set.seed(1)
m   <- matrix(rnorm(1e5),ncol=10)
csv <- data.frame(x=1:1e4,m)
write.csv(csv,"test.csv")
# s: rows we want to read
s <- c(1:50,53, 65,77,90,100:200,350:500, 5000:6000)
# v: logical, T means read this row (equivalent to your read_vec)
v <- (1:1e4 %in% s)

seq  <- rle(v)
idx  <- c(0, cumsum(seq$lengths))[which(seq$values)] + 1
# indx: start = starting row of sequence, length = length of sequence (compare to s)
indx <- data.frame(start=idx, length=seq$length[which(seq$values)])

library(data.table)
result <- do.call(rbind,apply(indx,1, function(x) return(fread("test.csv",nrows=x[2],skip=x[1]))))

这看起来很有前途。谢谢。 - user2763361
好方法。了解基本的R apply函数花了一些时间,但是这是一个很好的学习机会。@jlhoward - Lazarus Thurston

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接