使用 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
中是否有任何语法来实现像rbind
和union
中的sql
那样的垂直连接?
谢谢。
【问题讨论】:
【参考方案1】:使用rbindlist()
设计为rbind
和list
的list
一起...
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% 的处理时间会是多少?for
或 lapply
或读取 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 无法读取小数字
R data.table v1.9.6 中的错误 - 函数“fread”
追加多个大data.table;使用 colClasses 和 fread 的自定义数据强制;命名管道