为何count()、filter()、mutate()在R语言for循环中无法运行?
解决dplyr循环引用列名时count/filter/mutate的报错问题
问题根源
问题出在dplyr的默认求值逻辑:它的核心函数(count、filter、mutate等)默认用非标准求值——你直接写count(Q),它会在数据框里找名叫Q的列,而不是把变量Q里存的字符串(比如Q29、Q30)当作列名来用,自然就会报错说找不到Q列。后面的filter和mutate报错也是同一个逻辑。
一、修正现有代码
要让dplyr识别Q里的字符串是列名,需要用准引用(quasiquotation),通过!!sym(Q)把字符串转换成列名引用,告诉dplyr展开这个变量的值而不是当作列名。
1. 修正循环中的代码
listDFs <- list() for (i in 1:2) { Q <- names(ch_data[, 1:2][i]) listDFs[[i]] <- ch_data %>% dplyr::count(!!sym(Q)) %>% mutate(prop = round((prop.table(n) * 100), digits = 2), sd = round(sd(prop.table(n)), digits = 2)) %>% arrange(!!sym(Q)) }
sym(Q):把字符串格式的列名转换成dplyr能识别的符号对象!!(双感叹号):告诉dplyr把符号对象展开成实际的列名,而非当作变量名处理
2. 修正edit部分的代码
Q <- names(ch_data[, 1:2][1]) # 示例:取第一列列名 ch_data %>% select(!!sym(Q)) %>% filter(!(!!sym(Q)) %in% 'Não oferta') %>% count(!!sym(Q)) %>% mutate(prop = round((prop.table(n) * 100), digits = 2), sd = round(sd(prop.table(n)), digits = 2), CH = !!sym(Q), QUESTION = rep('mylabel', n())) %>% select(-!!sym(Q)) %>% arrange(CH) %>% relocate(QUESTION, CH, .before = everything())
- filter里要给
!!sym(Q)加括号,避免优先级问题 rep('mylabel', n())用n()更稳妥,匹配当前结果的行数
二、更优方案:批量处理替代循环
用purrr::map结合函数封装,更符合tidyverse风格,代码更简洁且易维护:
library(purrr) library(dplyr) # 定义处理单列的函数 process_single_col <- function(col_name) { ch_data %>% select(!!sym(col_name)) %>% filter(!(!!sym(col_name)) %in% 'Não oferta') %>% count(!!sym(col_name)) %>% mutate(prop = round((prop.table(n) * 100), digits = 2), sd = round(sd(prop.table(n)), digits = 2), CH = !!sym(col_name), QUESTION = rep('mylabel', n())) %>% select(-!!sym(col_name)) %>% arrange(CH) %>% relocate(QUESTION, CH, .before = everything()) } # 批量处理指定列 target_cols <- names(ch_data[, 1:2]) listDFs <- map(target_cols, process_single_col)
三、关于你用unlist(Q)解决的原理
你写ch_data$QUnlist <- unlist(Q),本质是把Q对应的列(比如ch_data$Q29)赋值给了新列QUnlist,之后count(QUnlist)自然能找到这个新增列。但这种方法会在原数据框中冗余新列,处理多列时会产生大量无用列,灵活性远不如准引用方案。
测试数据
ch_data <- structure(list(Q29 = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 5L, 1L, 1L, 1L, 1L, 4L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 4L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 5L, 1L, 1L, 1L, 1L, 1L, 1L, 2L), .Label = c("Não oferta", "1h - 3h", "Mais de 10h", "50% em LA", "100% em LA"), class = "factor"), Q30 = structure(c(4L, 8L, 3L, 1L, 2L, 2L, 1L, 2L, 1L, 1L, 1L, 2L, 5L, 1L, 1L, 1L, 7L, 7L, 1L, 1L, 1L, 1L, 5L, 1L, 1L, 2L, 1L, 6L, 2L, 8L, 8L, 2L, 8L, 3L, 7L, 7L, 1L, 2L, 2L, 8L, 3L, 2L, 1L, 8L, 3L, 1L, 1L, 1L, 1L, 6L, 3L), .Label = c("Não oferta", "1h - 3h", "4h - 5h", "6h - 8h", "9h - 10h", "Mais de 10h", "50% em LA", "100% em LA"), class = "factor")), row.names = c(NA, -51L), class = "data.frame")
内容的提问来源于stack exchange,提问作者Larissa Cury
相关产品推荐
相关产品推荐

