如何在R中不加载全量数据到内存,按列合并多个CSV文件
在R中增量合并大型CSV文件(不载入全量内存)
问题背景
现有一组按列拆分存储的大型CSV文件(示例为5个文件,每个文件包含原矩阵的2列),需要在不将所有文件全量载入内存的前提下,按列合并这些文件并输出完整的CSV到磁盘。
示例文件对应关系:
- 1.csv:包含X1、X2列
- 2.csv:包含X3、X4列
- 3.csv:包含X5、X6列
- 4.csv:包含X7、X8列
- 5.csv:包含X9、X10列
解决方案
方法1:使用readr包逐行处理
readr提供高效的逐行读取接口,适合内存有限的场景:
library(readr) # 定位文件路径并排序(保证列顺序正确) path <- paste0(getwd(), "/example/") file_paths <- sort(paste0(path, dir(path))) # 初始化输出文件连接,写入合并后的表头 out_con <- file("merged_full.csv", "w") # 收集所有文件的列名 full_header <- unlist(lapply(file_paths, function(f) names(read_csv(f, n_max = 0)))) writeLines(paste(full_header, collapse = ","), out_con) # 逐行读取并合并写入 line_idx <- 1 repeat { # 读取每个文件的当前行 current_rows <- lapply(file_paths, function(f) { read_csv(f, skip = line_idx, n_max = 1, col_names = FALSE) }) # 所有文件都读取完毕则终止循环 if (all(sapply(current_rows, nrow) == 0)) break # 合并当前行的所有列并写入 merged_row <- do.call(cbind, current_rows) writeLines(paste(unlist(merged_row), collapse = ","), out_con) line_idx <- line_idx + 1 } close(out_con)
方法2:使用data.table分块读取合并
data.table的fread读取效率极高,分块读取适合超大型文件:
library(data.table) path <- paste0(getwd(), "/example/") file_paths <- sort(paste0(path, dir(path))) # 生成完整表头 full_header <- unlist(lapply(file_paths, function(f) names(fread(f, nrows = 0)))) # 写入表头到输出文件 fwrite(data.table(t(full_header)), "merged_full.csv", col.names = FALSE) # 设置分块大小(根据内存情况调整,如1000/5000行) chunk_size <- 1000 start_row <- 1 repeat { # 分块读取每个文件的对应行 chunks <- lapply(file_paths, function(f) { fread(f, skip = start_row - 1, nrows = chunk_size) }) # 所有块都为空则终止 if (all(sapply(chunks, nrow) == 0)) break # 合并分块并追加写入 merged_chunk <- do.call(cbind, chunks) fwrite(merged_chunk, "merged_full.csv", append = TRUE, col.names = FALSE) start_row <- start_row + chunk_size }
方法3:基础R实现(无需额外包)
如果不想依赖第三方包,可使用基础R的文件连接操作:
path <- paste0(getwd(), "/example/") file_paths <- sort(paste0(path, dir(path))) # 打开所有文件的读取连接 file_cons <- lapply(file_paths, file, "r") # 读取并合并表头 headers <- lapply(file_cons, readLines, n = 1) full_header <- paste(unlist(strsplit(unlist(headers), ",")), collapse = ",") # 初始化输出文件并写入表头 out_con <- file("merged_full.csv", "w") writeLines(full_header, out_con) # 逐行读取合并 repeat { current_lines <- lapply(file_cons, readLines, n = 1) # 所有文件读取完毕则终止 if (all(sapply(current_lines, length) == 0)) break # 处理部分文件先读完的空行情况 current_lines <- lapply(current_lines, function(x) if (length(x) == 0) "" else x) merged_line <- paste(unlist(strsplit(unlist(current_lines), ",")), collapse = ",") writeLines(merged_line, out_con) } # 关闭所有文件连接 lapply(file_cons, close) close(out_con)
注意事项
- 文件排序:必须保证
file_paths的顺序对应原矩阵的列顺序,若文件名无规律,可根据文件内的列名排序。 - 分块大小调整:方法2中的
chunk_size可根据可用内存灵活调整,内存充足时调大以提升效率。 - 异常处理:实际生产环境中可添加文件存在性检查、行数一致性校验等逻辑。
内容的提问来源于stack exchange,提问作者mr.T
相关产品推荐
相关产品推荐

