基于name+year分组合并同一DataFrame行的R语言高效实现方法
优雅压缩分组后的DataFrame(合并同组多行到单行)
你这个需求太典型啦——把同一分组(name+year)下分散在多行的非NA值合并到同一行,完全不用绕弯子做过滤连接,用分组聚合的思路就能高效解决,下面给你几种常用的方案:
1. 用tidyverse/dplyr(最常用的语法风格)
如果平时用tidyverse生态,这个方法最直观,先按name和year分组,然后对每一列提取非NA的值:
library(dplyr) df_compressed <- df %>% group_by(name, year) %>% summarise(across(A:C, ~ na.omit(.)[1]), .groups = "drop")
group_by(name, year):指定分组依据across(A:C, ~ na.omit(.)[1]):对A到C的每一列,先去掉NA值,再取第一个元素(因为每个组里每个列只有1个非NA值).groups = "drop":分组后取消分组状态,得到普通DataFrame
运行后就能得到你想要的结果:
# A tibble: 2 × 5 name year A B C <chr> <dbl> <dbl> <dbl> <dbl> 1 bar 18 2 4 5 2 foo 19 1 3 2
2. 用data.table(大数据场景更高效)
如果你的DataFrame行数特别多,data.table的速度优势会很明显:
library(data.table) setDT(df) df_compressed <- df[, lapply(.SD, function(x) na.omit(x)[1]), by = .(name, year)]
setDT(df):把普通DataFrame转成data.table格式lapply(.SD, function(x) na.omit(x)[1]):对每个分组的所有列(.SD代表Subset of Data)执行提取非NA值的操作by = .(name, year):指定分组键
3. Base R原生方法(不用加载额外包)
如果不想加载第三方包,用base R的aggregate函数也能搞定:
df_compressed <- aggregate(. ~ name + year, data = df, FUN = function(x) na.omit(x)[1])
. ~ name + year:表示把除了name和year之外的所有列,按这两列分组聚合FUN = function(x) na.omit(x)[1]:自定义聚合函数,提取非NA值
这三种方法都能完美实现你的需求,相比过滤连接,代码更简洁,效率也更高——尤其是data.table版本,处理几十万甚至上百万行数据的时候优势很明显。
内容的提问来源于stack exchange,提问作者TTS
相关产品推荐
相关产品推荐

