pivot_longer处理大尺寸宽表时内存占用过高问题咨询
R大宽表执行pivot_longer内存溢出问题解决方案
内存占用暴涨的原因
- 数据规模本身的膨胀:当前宽表共2643246行,需要转换的列共511列(17至527列),转换为长表后总行数约为13.5亿行,是原数据行数的511倍,本质上数据总量是大幅提升的,并非没有新增数据。
- tidyr::pivot_longer的实现开销:该函数为了适配通用性场景,运行过程中会生成多份中间副本,同时默认不会对新生成的分类列做内存优化,会出现原数据集、中间过程数据集、最终结果数据集多份数据同时存于内存的情况,额外开销极高。
- 列类型的额外开销:默认生成的
G列为字符型,13.5亿行数据每行都存储一次字符串,仅该列的内存占用就会超过10GiB,如果转换为因子类型仅需要存储整数索引和唯一字符串映射,内存占用可降低90%以上。
高效解决代码
优先推荐使用内存优化更好的data.table包的melt函数实现转换,运行速度是pivot_longer的数倍,内存开销仅为其1/3到1/5:
library(data.table) # 转换为data.table对象,开销极低 setDT(Df) # 执行长表转换,id.vars可加入其他需要保留的列(如其余6个字符型变量) Df_long <- melt(Df, id.vars = c("Institution", "Year"), measure.vars = 17:527, variable.name = "G", value.name = "N", variable.factor = TRUE # 开启后G列为因子,大幅节省内存 ) # 如果需要转回tibble可以执行 Df_long <- as_tibble(Df_long)
如果内存仍有不足,可以采用分块处理的方式:
library(data.table) setDT(Df) # 按年份分块处理 years <- unique(Df$Year) res_list <- list() for (y in years) { tmp <- melt(Df[Year == y], id.vars = c("Institution", "Year"), measure.vars = 17:527, variable.name = "G", value.name = "N", variable.factor = TRUE) res_list[[as.character(y)]] <- tmp rm(tmp) gc() # 主动释放内存 } Df_long <- rbindlist(res_list) rm(res_list, Df) gc()
额外优化建议
- 处理前先删除不需要的列,仅保留ID列和需要转换的列,降低初始内存占用
- 提前将
Institution等字符型分类列转换为因子类型,进一步降低内存开销 - 不要在转换过程中同时保留原宽表和新长表,转换完成后及时删除原宽表并执行
gc()回收内存
内容的提问来源于stack exchange,提问作者Eric Nilsen
相关产品推荐
相关产品推荐

