如何基于分组变量合并含数值与字符列的R数据框行?
按ID聚合含数值与字符列的数据框
我有一份需要按ID字段聚合的数据框,其中同时包含数值型和字符型列。R原生的aggregate()函数无法处理字符型列,我自己写了循环实现,但不够优雅,希望得到更简便高效的方法,优先用R基础包或data.table包的方案,也接受其他包的实现。
示例数据
id winter wintercolor spring springcolor summer summercolor fall fallcolor 1: a 3 blue NA <NA> NA <NA> NA <NA> 2: a NA <NA> 4 purple NA <NA> NA <NA> 3: a NA <NA> NA <NA> 2 brown NA <NA> 4: a NA <NA> NA <NA> NA <NA> 5 red 5: b NA <NA> 4 yellow NA <NA> NA <NA> 6: b NA <NA> NA <NA> NA <NA> 2 blue 7: c 4 red NA <NA> NA <NA> NA <NA> 8: c NA <NA> NA <NA> 6 orange NA <NA> 9: c NA <NA> NA <NA> NA <NA> 3 blue 10: d 5 red NA <NA> NA <NA> NA <NA> 11: d NA <NA> NA <NA> 1 blue NA <NA>
期望结果
id winter wintercolor spring springcolor summer summercolor fall fallcolor 1: a 3 blue 4 purple 2 brown 5 red 2: b NA <NA> 4 yellow NA <NA> 2 blue 3: c 4 red NA <NA> 6 orange 3 blue 4: d 5 red NA <NA> 1 blue NA <NA>
现有循环实现代码
library(data.table) id <- c('a','a','a','a','b','b','c','c','c','d','d') winter <- c(3,NA,NA,NA,NA,NA,4,NA,NA,5,NA) wintercolor <- c('blue',NA,NA,NA,NA,NA,'red',NA,NA,'red',NA) spring <- c(NA,4,NA,NA,4,NA,NA,NA,NA,NA,NA) springcolor <- c(NA,'purple',NA,NA,'yellow',NA,NA,NA,NA,NA,NA) summer <- c(NA,NA,2,NA,NA,NA,NA,6,NA,NA,1) summercolor <- c(NA,NA,'brown',NA,NA,NA,NA,'orange',NA,NA,'blue') fall <- c(NA,NA,NA,5,NA,2,NA,NA,3,NA,NA) fallcolor <- c(NA,NA,NA,'red',NA,'blue',NA,NA,'blue',NA,NA) sampledat <- data.table(id,winter,wintercolor,spring,springcolor,summer,summercolor,fall,fallcolor) setkey(sampledat,id) colsets <- c('winter','spring','summer','fall') nnn <- length(colsets) holder <- vector('list',nnn) for(i in 1:nnn){ #i=1 loopcols <- c('id',names(sampledat)[grepl(colsets[i],names(sampledat))]) loopdat <- sampledat[,loopcols, with=F] col2 <- as.name(loopcols[2]) col3 <- as.name(loopcols[3]) holder[[i]] <- loopdat[!is.na(eval(col2)) & !is.na(eval(col3))] } combodat <- Reduce(function(x, y) merge(x, y, by='id', all=T), holder) combodat
解决方案
1. data.table简洁实现
利用data.table的按组操作,直接提取每组各列的第一个非NA值(每个ID对应每个季节的字段仅有一个有效非NA值):
library(data.table) result_dt <- sampledat[, lapply(.SD, function(x) x[!is.na(x)][1]), by = id] result_dt
2. R基础包实现
自定义适配数值/字符列的聚合函数,配合aggregate()使用:
# 自定义聚合函数:提取第一个非NA值 agg_fun <- function(x) { non_na <- x[!is.na(x)] if(length(non_na) > 0) non_na[1] else NA } # 按ID聚合所有列 result_base <- aggregate(. ~ id, data = sampledat, FUN = agg_fun) result_base
3. dplyr包实现
用tidyverse系列的dplyr分组汇总,代码更直观:
library(dplyr) result_dplyr <- sampledat %>% group_by(id) %>% summarise(across(everything(), ~ .x[!is.na(.x)][1]), .groups = "drop") result_dplyr
内容的提问来源于stack exchange,提问作者Will Phillips
相关产品推荐
相关产品推荐

