R语言基于主键实现等大小数据分块 不拆分同ID完整记录
R语言按ID完整性拆分超大DataFrame导出多CSV方案
核心目标是将拆分边界从固定行号调整为ID组的边界,保证同一ID的全部记录不会被拆分到不同文件,同时尽可能让每个导出文件的大小均匀。
前置要求
必须保证数据中同一个ID对应的所有记录是连续排布的。如果当前数据中同ID记录分散在不同位置,先按ID字段排序即可,否则任何分块逻辑都无法避免同ID被拆分到不同文件。
实现逻辑
- 先计算单块的基准行数:总记录数 / 目标分块数
- 按ID出现顺序统计每个ID的记录条数,计算累计行数
- 按累计行数匹配分块阈值,给每个ID分配唯一的块号,保证同ID不会被分配到两个块
- 把块号映射回原数据集,按块号拆分后导出文件
高性能实现(适配2000万行级数据,推荐)
使用data.table包处理,运算和导出速度比base R高数十倍,不会出现内存不足或运行过慢的问题:
library(data.table) n <- 14 # 自定义分块数量 # 转换为data.table格式,不改变原有数据顺序 setDT(df_t3) # 同ID记录不连续时执行排序,确认连续可注释该行节省时间 setorder(df_t3, ID) # 按ID顺序统计每个ID的记录数,计算累计行数 id_stat <- df_t3[, .(row_cnt = .N), by = .(ID, id_seq = rleid(ID))][order(id_seq)] id_stat[, cum_rows := cumsum(row_cnt)] # 计算单块基准行数,给每个ID分配唯一块号 chunk_base_size <- nrow(df_t3) / n id_stat[, chunk_id := findInterval( cum_rows - 1, seq(0, nrow(df_t3), by = chunk_base_size) )] # 将块号匹配回原数据集 df_t3[id_stat, chunk_id := i.chunk_id, on = "ID"] # 分块导出CSV,fwrite导出速度远快于base包write.csv lapply( split(df_t3, by = "chunk_id", keep.by = FALSE), function(chunk) { fwrite(chunk, paste0("data_chunk_", unique(chunk$chunk_id), ".csv"), row.names = FALSE) } )
Base R实现(无额外依赖)
如果不想安装第三方包,可以用base R实现,逻辑完全一致,仅运行速度较慢,适合内存充足的场景:
n <- 14 # 自定义分块数量 # 同ID记录不连续时先排序,确认连续可注释该行 df_t3 <- df_t3[order(df_t3$ID), ] # 按ID统计行数、计算累计值、分配块号 chunk_base_size <- nrow(df_t3) / n id_stat := aggregate( rep(1, nrow(df_t3)), by = list(ID = df_t3$ID), FUN = length ) id_stat$cum_rows <- cumsum(id_stat$x) id_stat$chunk_id <- findInterval( id_stat$cum_rows - 1, seq(0, nrow(df_t3), by = chunk_base_size) ) # 块号匹配回原数据框 df_t3$chunk_id <- id_stat$chunk_id[match(df_t3$ID, id_stat$ID)] # 分块导出 split_df <- split(df_t3, df_t3$chunk_id) lapply( names(split_df), function(chunk_name) { chunk_data <- split_df[[chunk_name]] # 导出前移除临时添加的chunk_id字段 chunk_data <- chunk_data[, !colnames(chunk_data) %in% "chunk_id"] write.csv(chunk_data, paste0("data_chunk_", chunk_name, ".csv"), row.names = FALSE) } )
说明
- 该方案生成的分块大小会在基准值上下小幅浮动,浮动范围由单个ID的最大记录数决定,不会出现文件大小严重不均的情况
- 原代码按行号取模拆分的逻辑,本质是硬按固定行号切分,刚好切到某个ID的中间行时就会拆断同ID记录,新逻辑把拆分点全部落在两个ID的间隔位置,从根本上避免了ID被拆分的问题
内容的提问来源于stack exchange,提问作者Neel
相关产品推荐
相关产品推荐

