能否用Arrow按列而非列值划分数据集?data.table内存优化求助
分批生成按列分组的Parquet文件并合并方案
前提准备
假设你已经有包含完整2900万行index列的主数据表main_index_dt(可从文件读取或生成),样本文件为带index列的单个样本数据(如sample1.csv到sample10.csv)。
方案一:每个Parquet文件包含index+分组样本列
步骤1:分组样本文件
将10个样本文件分成每组5个:
library(data.table) library(arrow) # 加载主index表 main_index_dt <- fread("main_index.csv") # 替换为你的主index文件路径 # 样本文件列表 sample_files <- paste0("sample", 1:10, ".csv") # 按每5个一组拆分 file_groups <- split(sample_files, ceiling(seq_along(sample_files)/5))
步骤2:分批处理并写入Parquet
循环处理每个分组,合并样本列与主index后写入文件,主动释放内存:
for (i in seq_along(file_groups)) { group_files <- file_groups[[i]] # 初始化临时表,先复制主index temp_dt <- copy(main_index_dt) # 逐个读取样本文件,合并到临时表 for (f in group_files) { sample_dt <- fread(f) # 按index合并,保证与主index行完全匹配(空值自动填充NA) sample_dt <- merge(temp_dt[, .(index)], sample_dt, by = "index", all.x = TRUE) # 将样本列加入临时表 temp_dt[, colnames(sample_dt)[-1] := sample_dt[, .SD, .SDcols = -"index"]] # 释放当前样本文件的内存 rm(sample_dt) gc() } # 写入Parquet文件 write_parquet(temp_dt, paste0("part-", i, ".parquet")) # 释放临时表内存 rm(temp_dt) gc() }
方案二:单独存储index,Parquet仅存样本列
步骤1:写入单独的index文件
write_parquet(main_index_dt, "index.parquet")
步骤2:分批处理样本列并写入Parquet
for (i in seq_along(file_groups)) { group_files <- file_groups[[i]] temp_dt <- copy(main_index_dt) for (f in group_files) { sample_dt <- fread(f) sample_dt <- merge(temp_dt[, .(index)], sample_dt, by = "index", all.x = TRUE) temp_dt[, colnames(sample_dt)[-1] := sample_dt[, .SD, .SDcols = -"index"]] rm(sample_dt) gc() } # 移除index列,仅保留样本列 temp_dt[, index := NULL] write_parquet(temp_dt, paste0("part-", i, ".parquet")) rm(temp_dt) gc() }
读取合并Parquet文件
针对方案一(每个Parquet带index)
# 获取所有part文件路径 part_files <- list.files(pattern = "part-.*\\.parquet", full.names = TRUE) # 逐个读取并按index合并(或直接cbind,因为index顺序一致) full_dt <- lapply(part_files, function(f) as.data.table(read_parquet(f))) %>% Reduce(function(x, y) merge(x, y, by = "index", all.x = TRUE), .) # 或用更高效的cbind(需保证所有文件的index顺序完全一致) full_dt <- do.call(cbind, lapply(part_files, function(f) as.data.table(read_parquet(f)))) # 去重重复的index列 full_dt <- full_dt[, !duplicated(colnames(full_dt)), with = FALSE]
针对方案二(单独index+样本列Parquet)
# 读取index表 index_dt <- as.data.table(read_parquet("index.parquet")) # 读取所有样本列Parquet part_dts <- lapply(list.files(pattern = "part-.*\\.parquet", full.names = TRUE), function(f) as.data.table(read_parquet(f))) # 合并index与所有样本列 full_dt <- cbind(index_dt, do.call(cbind, part_dts))
注意事项
- 处理样本文件时必须通过
merge与主index匹配,确保行顺序和完整性,避免直接读取样本文件就写入(可能存在index缺失或顺序不一致)。 - 每次处理完单个样本文件或分组后,用
rm()+gc()主动释放内存,避免内存累积。 - 方案一的容错性更高,每个Parquet文件独立包含index,合并时无需依赖行顺序;方案二的文件体积更小,但需严格保证样本列的行顺序与index完全一致。
内容的提问来源于stack exchange,提问作者basesorbytes
相关产品推荐
相关产品推荐

