多分组观测时间聚合:data.table优化与tidyverse实现
嘿,这个问题我太熟悉了——当一个观测能属于多个分组时,重复写子集确实很麻烦。咱们来用更优雅的方式解决它,不管是data.table还是tidyverse都有简洁的方案!
用data.table优化的方案
核心思路是把宽格式的分组列转成长格式,这样就能一次性处理所有分组,不用重复写每个子集的代码:
library(data.table) set.seed(1) TF <- c(TRUE, FALSE) time <- rep(1:4, each = 5) df <- data.table(time = time, x = rnorm(20), groupA = sample(TF, size = 20, replace = TRUE), groupB = sample(TF, size = 20, replace = TRUE), groupC = sample(TF, size = 20, replace = TRUE)) # 优化写法:一步处理所有分组 result_dt <- df %>% melt(id.vars = c("time", "x"), measure.vars = patterns("^group"), variable.name = "group", value.name = "in_group") %>% .[in_group == TRUE, .(sum_x = sum(x)), by = .(time, group)] %>% dcast(time ~ group, value.var = "sum_x") %>% setnames(c("time", "A", "B", "C")) print(result_dt)
步骤说明:
melt:把所有以group开头的列转成长格式,生成group(标记分组名)和in_group(标记是否属于该分组)两列;- 过滤+聚合:只保留属于该分组的行,按
time和group计算x的总和; dcast:把长格式转回宽格式,缺失的分组自动填充NA;setnames:重命名列,和你期望的输出格式完全匹配。
用tidyverse实现的方案
tidyverse的思路和data.table一致,用pivot_longer/pivot_wider完成格式转换,代码同样简洁:
library(tidyverse) set.seed(1) TF <- c(TRUE, FALSE) time <- rep(1:4, each = 5) df <- tibble(time = time, x = rnorm(20), groupA = sample(TF, size = 20, replace = TRUE), groupB = sample(TF, size = 20, replace = TRUE), groupC = sample(TF, size = 20, replace = TRUE)) result_tidy <- df %>% # 转长格式,自动提取分组名(去掉group前缀) pivot_longer(cols = starts_with("group"), names_to = "group", names_prefix = "group", values_to = "in_group") %>% # 过滤出属于该分组的观测 filter(in_group) %>% # 按时间和分组求和 group_by(time, group) %>% summarise(sum_x = sum(x), .groups = "drop") %>% # 转回宽格式,缺失值自动填NA pivot_wider(names_from = group, values_from = sum_x) print(result_tidy)
步骤说明:
pivot_longer:直接提取分组名(去掉group前缀),避免后续重命名的麻烦;filter:只保留属于该分组的行;group_by+summarise:按时间和分组计算总和;pivot_wider:转回宽格式,自动处理缺失的分组-时间组合,填充NA。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

