按AT07分组计算各DT01列值为1的占比(R语言优化求助)
问题描述
我有一个包含ID、AT07以及DT01_1至DT01_19列的数据集,需要按AT07(取值为AT07_1至AT07_11)分组,统计每个DT01列中值为1的占比。之前用dplyr重复写mutate语句实现,效率极低,求高效方案。
附样本数据集:
ID AT07 DT01_1 DT01_2 DT01_3 DT01_4 DT01_5 DT01_6 DT01_7 DT01_8 DT01_9 DT01_10 DT01_11 DT01_12 DT01_12.1 1 vD0FPVt AT07_1 1 1 1 1 1 1 1 1 1 1 1 0 0 2 UwwHYWL AT07_1 0 0 0 0 1 0 0 0 0 0 0 0 0 3 Lw7HM6J AT07_1 0 0 0 0 0 0 1 1 0 1 0 0 0 4 Llpww1k AT07_1 0 0 0 0 0 0 0 0 0 1 0 0 0 5 OwXwA9j AT07_1 0 0 0 0 0 0 1 0 0 1 1 0 0
之前的低效代码:
library(dplyr) resLong <- resLong %>% group_by(AT07) %>% mutate(DT01_1_percent1 = label_percent()(sum(DT01_1 == 1)/n())) %>% mutate(DT01_2_percent1 = label_percent()(sum(DT01_2 == 1)/n())) %>% mutate(DT01_3_percent1 = label_percent()(sum(DT01_3 == 1)/n())) %>% mutate(DT01_4_percent1 = label_percent()(sum(DT01_4 == 1)/n())) %>% mutate(DT01_5_percent1 = label_percent()(sum(DT01_5 == 1)/n())) %>% mutate(DT01_6_percent1 = label_percent()(sum(DT01_6 == 1)/n())) %>% mutate(DT01_7_percent1 = label_percent()(sum(DT01_7 == 1)/n())) %>% mutate(DT01_8_percent1 = label_percent()(sum(DT01_8 == 1)/n())) %>% mutate(DT01_9_percent1 = label_percent()(sum(DT01_9 == 1)/n())) %>% mutate(DT01_10_percent1 = label_percent()(sum(DT01_10 == 1)/n())) %>% mutate(DT01_11_percent1 = label_percent()(sum(DT01_11 == 1)/n())) %>% mutate(DT01_12_percent1 = label_percent()(sum(DT01_12 == 1)/n())) %>% mutate(DT01_13_percent1 = label_percent()(sum(DT01_13 == 1)/n())) %>% mutate(DT01_14_percent1 = label_percent()(sum(DT01_14 == 1)/n())) %>% mutate(DT01_15_percent1 = label_percent()(sum(DT01_15 == 1)/n())) %>% mutate(DT01_16_percent1 = label_percent()(sum(DT01_16 == 1)/n())) %>% mutate(DT01_17_percent1 = label_percent()(sum(DT01_17 == 1)/n())) %>% mutate(DT01_18_percent1 = label_percent()(sum(DT01_18 == 1)/n())) %>% mutate(DT01_19_percent1 = label_percent()(sum(DT01_19 == 1)/n())) resLong <- resLong[,-c(1:21)] library(dplyr) resLong <- distinct(resLong)
高效解决方案
方法1:用dplyr的across函数(最简洁的tidyverse方式)
利用across批量处理所有DT01列,避免重复写mutate,一次完成分组计算:
library(dplyr) library(scales) # 需加载scales包使用label_percent res_summary <- resLong %>% group_by(AT07) %>% summarise( across(starts_with("DT01_"), ~ label_percent()(sum(.x == 1)/n()), .names = "{col}_percent1") )
说明:
starts_with("DT01_")自动匹配所有DT01开头的列,覆盖DT01_1到DT01_19.names = "{col}_percent1"自动生成和你之前手动命名一致的新列名summarise直接输出每个分组的统计结果,不需要后续删列、去重,一步到位
方法2:转长格式处理(适配tidy数据原则,方便后续扩展)
如果之后还要对统计结果做可视化或进一步分析,转长格式会更灵活:
library(tidyr) library(dplyr) library(scales) res_long_summary <- resLong %>% select(AT07, starts_with("DT01_")) %>% # 只保留需要的列 pivot_longer(cols = starts_with("DT01_"), names_to = "DT01_col", values_to = "value") %>% group_by(AT07, DT01_col) %>% summarise(percent1 = label_percent()(sum(value == 1)/n()), .groups = "drop") %>% pivot_wider(names_from = DT01_col, values_from = percent1, names_glue = "{DT01_col}_percent1")
说明:
- 先转长格式统一计算占比,再转回宽格式匹配原需求的输出结构
- 这种方式符合tidy数据规范,后续修改统计逻辑只需改一次代码
方法3:用data.table(大数据集下效率拉满)
如果你的数据集量级很大,data.table的运算速度和内存使用会比dplyr更有优势:
library(data.table) library(scales) setDT(resLong) # 把数据框转成data.table格式 res_dt_summary <- resLong[, lapply(.SD, function(x) label_percent()(sum(x == 1)/.N)), by = AT07, .SDcols = patterns("^DT01_") ] # 给列名加上_percent1后缀 setnames(res_dt_summary, old = patterns("^DT01_"), new = paste0(names(res_dt_summary)[-1], "_percent1"))
说明:
.SDcols = patterns("^DT01_")指定要处理的目标列.N是data.table中表示分组行数的变量,对应dplyr里的n()
内容的提问来源于stack exchange,提问作者Nadine M.
相关产品推荐
相关产品推荐

