读取 100,000 个 .dat.gz 文件的最快方法

Posted

技术标签:

【中文标题】读取 100,000 个 .dat.gz 文件的最快方法【英文标题】:Fastest way to read in 100,000 .dat.gz files 【发布时间】:2016-06-16 06:30:35 【问题描述】:

我有几十万个非常小的.dat.gz 文件,我想以最有效的方式将它们读入 R。我读入文件,然后立即聚合并丢弃数据,所以当我接近过程结束时,我并不担心管理内存。我只是真的想加快瓶颈,这恰好是解压缩和读取数据。

每个数据集由 366 行和 17 列组成。这是我到目前为止所做的一个可重复的示例:

构建可重现的数据:

require(data.table)

# Make dir
system("mkdir practice")

# Function to create data
create_write_data <- function(file.nm) 
  dt <- data.table(Day=0:365)
  dt[, (paste0("V", 1:17)) := lapply(1:17, function(x) rnorm(n=366))]
  write.table(dt, paste0("./practice/",file.nm), row.names=FALSE, sep="\t", quote=FALSE)
  system(paste0("gzip ./practice/", file.nm))    

这里是应用代码:

# Apply function to create 10 fake zipped data.frames (550 kb on disk)
tmp <- lapply(paste0("dt", 1:10,".dat"), function(x) create_write_data(x))

这是迄今为止我读取数据最有效的代码:

# Function to read in files as fast as possible
read_Fast <- function(path.gz) 
  system(paste0("gzip -d ", path.gz)) # Unzip file
  path.dat <- gsub(".gz", "", path.gz)
  dat_run <- fread(path.dat)


# Apply above function
dat.files <- list.files(path="./practice", full.names = TRUE)
system.time(dat.list <- rbindlist(lapply(dat.files, read_Fast), fill=TRUE))
dat.list

我已经将它装在一个函数中并并行应用它,但对于我需要它的用途来说,它仍然太慢了。

我已经尝试过美妙的h2o 包中的h2o.importFolder,但与使用普通的Rdata.table 相比,它实际上要慢得多。也许有一种方法可以加快文件的解压缩速度,但我不确定。从我运行它的几次中,我注意到文件的解压缩通常需要大约 2/3 的函数时间。

【问题讨论】:

通过使用“readr”包中的read_tsv,我的速度得到了提高(与您迄今为止最高效的代码相比)。 rbindlist(lapply(dat.files, read_tsv)) 【参考方案1】:

我有点惊讶这确实有效。希望它适用于您的情况。我很想知道速度与直接从 R 从磁盘读取压缩数据相比如何(尽管会因非矢量化而受到惩罚)。

tblNames = fread('cat *dat.gz | gunzip | head -n 1')[, colnames(.SD)]
tbl = fread('cat *dat.gz | gunzip | grep -v "^Day"')
setnames(tbl, tblNames)
tbl

【讨论】:

也很惊讶。这真太了不起了。知道它在速度方面与其他方法相比有多好? 我刚刚编辑了答案。也想知道,因为 OP 有一个很好的测试环境...... 很好的答案!使用这种方法,我能够更快地读取和汇总我的数据。使用 8 个内核,我能够在 1.5 分钟内读取和处理 696,000 个文件,而之前需要 12 分钟。接下来我需要将其扩展到数百万个文件,所以这是一个巨大的帮助!请问grep -v "^Day"部分代码是干什么的? @Mike 它正在删除每个文件中的第 1 行(标题)。这有点麻烦,但我还没有找到更清洁的方法,如果你有一个数字第一列,一般来说可能没问题。 '^' 是一个锚。所以它的意思是“删除该行前三个字符为 Day 的所有行”【参考方案2】:

R 可以使用gzfile 函数本地读取压缩文件。看看这是否有效。

rbindlist(lapply(dat.files, function(f) 
    read.delim(gzfile(f))
))

【讨论】:

顺便说一句,您可以将其简化为rbindlist(lapply(dat.files, read.delim))。 +1。这似乎也比read_tsv 快​​。 这确实有很大帮助。我现在可以在 12 分钟而不是 18 分钟内读取 232,000 个文件。我还需要这个速度更快一些,但这是一个很好的开始【参考方案3】:

瓶颈可能是由于使用了对外部应用程序的 system() 调用造成的。

您应该尝试使用构建函数来提取存档。 这个答案解释了如何:Decompress gz file using R

【讨论】:

以上是关于读取 100,000 个 .dat.gz 文件的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

在NodeJS中将许多文件中的JSON对象插入MongoDB的最有效方法

如何有效地读取和写入太大而无法放入内存的文件?

索引(用于搜索索引)70,000 个节点的最简单方法是啥?

在 Python 中发送 100,000 个 HTTP 请求的最快方法是啥?

是否可以将巨大的 dask 数据框保存到镶木地板中?

从包含 10 亿个整数的文件中显示前 100 个整数的最有效方法。内存最多可容纳 100 万个整数