如何用R的fread高效读取多Zip中列数不同的多CSV文件
高效导入ZIP包内多CSV文件的优化方案
问题背景
需要导入6个独立ZIP文件中的约1400个CSV文件:
- 所有CSV为相似时序数据,大部分列一致,部分CSV包含额外列(如示例中的
yield_c) - 要求不解压完成导入
- 现有两种方案存在缺陷:
- 方案A:速度快,但每个CSV的表头被当作数据行导入,且所有列被识别为字符型
- 方案B:能正确处理数据,但速度极慢
问题根源分析
- 方案A问题:使用
unzip -p zipdir直接输出ZIP内所有CSV的合并内容,多个CSV的表头会被重复输出,fread无法区分表头和数据行,同时因列数不一致被迫将所有列转为字符型。 - 方案B问题:逐个读取每个CSV,每次调用
unzip和fread带来大量IO开销,导致速度骤降。
优化方案
利用awk命令配合unzip -p,只保留第一个CSV的表头,后续所有CSV跳过第一行(表头),让fread读取到的是一个带唯一表头、无重复表头的数据流,既保证速度,又能正确识别列类型和缺失列。
优化后代码
library(data.table) # 优化后的单ZIP处理函数 datprocessorOpt <- function(zipdir) { # 使用unzip导出所有CSV内容,通过awk只保留第一个表头,后续跳过第一行 cmd <- paste( "unzip -p", shQuote(zipdir), "| awk 'NR == 1 || FNR > 1'" ) # fread读取合并后的数据流,fill=TRUE处理列不一致的情况 fread(cmd, header = TRUE, fill = TRUE, stringsAsFactors = FALSE) } # 批量处理所有ZIP文件 begin <- Sys.time() import <- rbindlist(lapply(zips, datprocessorOpt), use.names = TRUE, fill = TRUE) end <- Sys.time() print(paste0("优化方案耗时: ", round(end - begin), " 秒"))
代码说明
unzip -p zipdir:将ZIP内所有文件内容直接输出到标准输出,不解压到磁盘awk 'NR == 1 || FNR > 1':NR是全局行号,NR==1保留第一行(第一个CSV的表头)FNR是当前文件的行号,FNR>1跳过后续每个CSV的第一行(表头)
fread直接读取处理后的数据流,fill=TRUE自动处理列数不一致的CSV,同时正确识别各列数据类型
效果验证
- 速度接近方案A,远快于方案B
- 不会将表头当作数据行导入
- 各列数据类型识别正确
- 自动处理含额外列的CSV,缺失列自动填充
NA
内容的提问来源于stack exchange,提问作者Riley
相关产品推荐
相关产品推荐

