如何用循环为数据框各列生成频率表并存储为列表
解决DataFrame列分组频数与百分比计算的批量处理问题
问题背景
现有如下示例DataFrame,每列存储分组归属数据,需要为每列计算各组的频数及占该列总频数的百分比,并将结果以列表形式存储(列表每个元素对应一列的计算结果):
df <- data.frame(gender = c("male", "female", "", "male", "female"), score1 = c(1, 1, NA, NA, 3), score2 = c(NA, NA, 3, 4, 5), score3 = c(2, 2, 3, 3, NA))
已编写处理单列的函数countsbyvars:
countsbyvars <- function(var) { df %>% select({{var}}) %>% drop_na() %>% group_by({{var}}) %>% summarise(n = n()) %>% mutate(freq = paste0(n / sum(n) * 100, "%")) }
但尝试for循环、lapply调用时均报错:
for循环报错:Error ingroup_by():! Must group by variables found in.data.✖ Columniis not found.lapply报错:Error ingroup_by():ℹ In argument:names(df).Caused by error:!names(df)must be size 0 or 1, not 317.
同时不清楚如何不硬编码列名使用across()函数,期望得到的结果格式为列对应列表元素的频数表(含分组、频数、百分比)。
解决方案
方法1:修正原函数,用lapply批量处理
原函数依赖tidy eval的{{var}}语法(接收裸变量名),但lapply传递的是字符串型列名,因此需要修改函数以支持字符型列名引用:
countsbyvars <- function(var_name) { df %>% select(all_of(var_name)) %>% # 用all_of引用字符型列名 drop_na() %>% group_by(.data[[var_name]]) %>% # 用.data[[ ]]访问字符型列名 summarise(n = n(), .groups = "drop") %>% # .groups="drop"避免分组残留 mutate(freq = paste0(round(n / sum(n) * 100, 6), "%")) # 保留6位小数优化格式 } # 调用生成结果列表 result_list <- lapply(names(df), countsbyvars)
方法2:用purrr::map简化调用(适配向量输入)
调整函数接收列向量而非列名,结合purrr::map直接遍历DataFrame的每一列:
library(purrr) countsbyvars <- function(var_vec) { tibble(group = var_vec) %>% drop_na() %>% group_by(group) %>% summarise(n = n(), .groups = "drop") %>% mutate(freq = paste0(round(n / sum(n) * 100, 6), "%")) } # 直接遍历df的每一列生成列表 result_list <- map(df, countsbyvars)
方法3:用across实现(无需显式循环)
通过across结合列表型输出,直接生成目标列表:
library(tidyr) result_list <- df %>% summarise(across(everything(), ~ { clean_vec <- drop_na(.x) tab <- table(clean_vec) tibble( group = names(tab), n = as.integer(tab), freq = paste0(round(tab/sum(tab)*100, 6), "%") ) })) %>% pivot_longer(everything(), values_to = "freq_table") %>% pull(freq_table)
修复原for循环的写法
如果坚持使用原函数的{{var}}语法,可通过sym()将字符串列名转换为裸变量名:
library(rlang) result_list <- list() for(col in names(df)) { result_list[[col]] <- countsbyvars(!!sym(col)) # !!sym()解析字符串为裸变量名 }
结果验证
调用上述任意方法后,result_list的元素即为对应列的频数表,例如:
# 查看gender列的结果 result_list[[1]] #> # A tibble: 2 × 3 #> group n freq #> <chr> <int> <chr> #> 1 male 2 50% #> 2 female 2 50%
内容的提问来源于stack exchange,提问作者DTYK
相关产品推荐
相关产品推荐

