使用 data.table (with fread) 快速读取和组合多个文件

Posted

技术标签:

【中文标题】使用 data.table (with fread) 快速读取和组合多个文件【英文标题】:Fast reading and combining several files using data.table (with fread) 【发布时间】:2014-02-05 00:49:50 【问题描述】:

我有几个结构相同的不同 txt 文件。现在我想使用 fread 将它们读入 R,然后将它们合并到一个更大的数据集中。

## First put all file names into a list 
library(data.table)
all.files <- list.files(path = "C:/Users",pattern = ".txt")

## Read data using fread
readdata <- function(fn)
    dt_temp <- fread(fn, sep=",")
    keycols <- c("ID", "date")
    setkeyv(dt_temp,keycols)  # Notice there's a "v" after setkey with multiple keys
    return(dt_temp)


# then using 
mylist <- lapply(all.files, readdata)
mydata <- do.call('rbind',mylist)

代码运行良好,但速度并不理想。每个 txt 文件有 1M 的观测值和 12 个字段。

如果我使用fread 读取单个文件,它会很快。但是使用apply,则速度极慢,显然比一个一个地读取文件要花很多时间。我想知道这里哪里出错了,速度增益有什么改进吗?

我尝试了plyr 包中的llply,速度提升不大。

另外,data.table 中是否有任何语法来实现像rbindunion 中的sql 那样的垂直连接?

谢谢。

【问题讨论】:

【参考方案1】:

使用rbindlist() 设计为rbindlistlist 一起...

mylist <- lapply(all.files, readdata)
mydata <- rbindlist( mylist )

正如 @Roland 所说,不要在函数的每次迭代中设置键!

总而言之,这是最好的:

l <- lapply(all.files, fread, sep=",")
dt <- rbindlist( l )
setkey( dt , ID, date )

【讨论】:

另外,最后只设置一次它们的键。 @SimonO'Hanlon,非常感谢。 for 循环比 lapply 快吗? @Bigchao 不确定。但是,如果您考虑一下,您预计 99.999% 的处理时间会是多少? forlapply 或读取 1e6 观察数据的计算开销?在这种情况下,这完全是任意的。我认为使用for 循环可能会更好地管理内存,而且肯定不会比lapply 差。两者之间不会有速度差异。 @SimonO'Hanlon 非常感谢:) 如果您在工作目录之外调用文件,请务必将full.names = TRUE 添加到list.files(),例如list.files(path = "C:/Users",pattern = ".txt",full.names=TRUE)。这会将完整的文件路径附加到每个调用的文件,从而使lapply 函数可以成功地定位和操作每个文件。【参考方案2】:

我已经重写了太多次这样的代码了。最后把它变成了一个方便的函数,如下。

data.table_fread_mult <- function(filepaths = NULL, dir = NULL, recursive = FALSE, pattern = NULL, fileCol = FALSE, ...)
  # fread multiple filepaths and then combine the results into a single data.table
  # This function has two interfaces: either
  # 1) provide `filepaths` as a character vector of filepaths to read or 
  # 2) provide `dir` (and optionally `pattern` and `recursive`) to identify the directory to read from
  # If fileCol = TRUE, result will incude a column called File with the full source file path of each record
  # ... should be arguments to pass on to fread()
  # `pattern` is an optional regular expression to match files (e.g. pattern='csv$' matches files ending with 'csv')
  
  if(!is.null(filepaths) & (!is.null(dir) | !is.null(pattern)))
    stop("If `filepaths` is given, `dir` and `pattern` should be NULL")
   else if(is.null(filepaths) & is.null(dir))
    stop("If `filepaths` is not given, `dir` should be given")
  
  
  # If filepaths isn't given, build it from dir, recursive, pattern
  if(is.null(filepaths))
    filepaths <- list.files(
      path = dir, 
      full.names = TRUE, 
      recursive = recursive, 
      pattern = pattern
    )
  
  
  # Read and combine files
  if(fileCol)
    return(rbindlist(lapply(filepaths, function(x) fread(x, ...)[, File := x]), use.names = TRUE))
   else
    return(rbindlist(lapply(filepaths, fread, ...), use.names = TRUE))
  

【讨论】:

如何使用此功能添加带有文件名的列?例如,如果我的目录中有sample1.txt sample2.txt sample3.txt,我想读取它们并将它们合并到一个以 V2 作为文件名的数据表(例如 sample1)。所以我的数据看起来像sample1scontent sample1 这通常还是太慢了。这里有一些快 25-50 倍的方法***.com/a/58131427/1563960

以上是关于使用 data.table (with fread) 快速读取和组合多个文件的主要内容,如果未能解决你的问题,请参考以下文章

data.table fread 可以接受连接吗?

来自 data.table 包的 fread 无法读取小数字

R data.table v1.9.6 中的错误 - 函数“fread”

追加多个大data.table;使用 colClasses 和 fread 的自定义数据强制;命名管道

data.table fread错误 - gzip文件 - 设置临时目录

data.table::fread 不喜欢第一列中的缺失值