You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用循环为数据框各列生成频率表并存储为列表

解决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调用时均报错:

  1. for循环报错:Error in group_by():! Must group by variables found in .data.✖ Column i is not found.
  2. lapply报错:Error in group_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:17:39