如何用dplyr自动化生成分组统计表格?解决函数调用报错
问题:按性别拆分多变量生成计数百分比表格的函数报错
我有一份数据集,想要按sex列拆分分析。已经写出针对单个变量(比如awarskin)生成男女响应计数百分比表格的代码:
df %>% group_by(x, sex) %>% mutate(n=n()) %>% # 创建计数列 group_by(x) %>% distinct(x,sex,n) %>% mutate(Per=n/sum(n), np=paste0(n," (",round(Per*100,2)," %)")) %>% # 创建百分比列并格式化 select(-n,-Per) %>% # 仅保留所需列 spread(sex,np) # 将性别列展开为多列
但需要对50多个变量执行此操作,所以想写一个函数,传入列名就能自动生成表格。
尝试定义了如下函数:
sex_percent_table <- function(column){ ctdf %>% group_by(column, sex) %>% mutate(n=n()) %>% # 创建计数列 group_by(column) %>% distinct(column,sex,n) %>% mutate(Per=n/sum(n), np=paste0(n," (",round(Per*100,2)," %)")) %>% # 创建百分比列并格式化 select(-n,-Per) %>% # 仅保留所需列 spread(sex,np) # 将性别列展开为多列 }
函数定义没有报错,但调用sex_percent_table(markex)时抛出错误:"column 'x' is not found",回溯信息如下:
Error in group_by(., x, sex) : ✖ Column `x` is not found. 14. stop(fallback) 13. signal_abort(cnd, .file) 12. abort(bullets, call = error_call) 11. group_by_prepare(.data, ..., .add = .add, caller_env = caller_env()) 10. group_by.data.frame(., x, sex) 9. group_by(., x, sex) 8. mutate(., n = n()) 7. group_by(., x) 6. distinct(., x, sex, n) 5. mutate(., Per = n/sum(n), np = paste0(n, " (", round(Per * 100, 2), " %)")) 4. select(., -n, -Per) 3. spread(., sex, np) 2. ctdf %>% group_by(x, sex) %>% mutate(n = n()) %>% group_by(x) %>% distinct(x, sex, n) %>% mutate(Per = n/sum(n), np = paste0(n, " (", round(Per * 100, 2), " %)")) %>% select(-n, -Per) %>% spread(sex, np) 1. sex_percent_table(markex)
我刚接触R和编程,想知道错误原因,以及正确的自动化实现方法。
以下是用mtcars数据集、二元变量vs复现的示例函数:
percent_table <- function(column){ mtcars %>% group_by(column, vs) %>% mutate(n=n()) %>% # 创建计数列 group_by(column) %>% distinct(column,vs,n) %>% mutate(Per=n/sum(n), np=paste0(n," (",round(Per*100,2)," %)")) %>% # 创建百分比列并格式化 select(-n,-Per) %>% # 仅保留所需列 spread(vs,np) # 将值展开为多列 }
错误原因与解决方法
错误原因
问题出在**非标准求值(NSE)**上:dplyr的函数(比如group_by)会把你传入的参数column当作字面量列名column,而不是你实际传入的列名(比如markex)。回溯里提到的x是因为你最初的单变量代码用了x,但函数里替换成column后,dplyr没有正确解析这个参数。
正确实现方法
需要用dplyr的准引用工具,用{{ }}包裹参数,让dplyr正确识别传入的列名。同时建议把数据集也作为参数传入函数,避免依赖全局变量,让函数更通用。
改进后的函数(针对你的数据集)
sex_percent_table <- function(data, column){ data %>% group_by({{ column }}, sex) %>% summarise(n = n(), .groups = "drop_last") %>% # 用summarise替代mutate+distinct更高效 mutate(Per = n/sum(n), np = paste0(n, " (", round(Per*100, 2), " %)")) %>% select(-n, -Per) %>% spread(sex, np) }
使用方式
# 调用时传入数据集和列名 sex_percent_table(ctdf, markex)
针对mtcars复现的改进函数
percent_table <- function(data, column){ data %>% group_by({{ column }}, vs) %>% summarise(n = n(), .groups = "drop_last") %>% mutate(Per = n/sum(n), np = paste0(n, " (", round(Per*100, 2), " %)")) %>% select(-n, -Per) %>% spread(vs, np) } # 使用示例 percent_table(mtcars, cyl)
额外优化建议
- 用
summarise替代mutate+distinct:直接分组统计计数更高效,不需要先给每一行加计数再去重。 - 添加
.groups参数:控制分组后的处理,避免不必要的警告。 - 传入数据集参数:函数不依赖全局变量,复用性更强。
- 批量处理多变量:如果要一次性处理50多个变量,可以用
purrr包的map函数批量生成表格:
library(purrr) # 替换成你要处理的变量列表 cols_to_process <- c("awarskin", "markex", "var3") # 批量生成表格,结果存为列表 result_list <- map(cols_to_process, ~sex_percent_table(ctdf, !!sym(.x)))
内容的提问来源于stack exchange,提问作者Veritas
相关产品推荐
相关产品推荐

