如何用purrr包编写可应用于数据集列的自定义函数
解决purrr::map()调用自定义函数时的因子参数报错问题
问题场景
需要将自定义函数应用于数据集的一组列并返回列表,lapply()可正常实现,但使用purrr包的map()时遭遇报错。
示例数据
df <- data.frame(variableA = factor(sample(x = c("lessThan", "moreThan"), size = 20, replace = T, prob = c(0.5, 0.5))), variableB = factor(sample(x = c("lessThan", "moreThan"), size = 20, replace = T, prob = c(0.2, 0.8))), variableC = factor(sample(x = c("lessThan", "moreThan"), size = 20, replace = T, prob = c(0.4, 0.6))))
自定义函数
该函数接受字符串形式的变量名,返回各水平比例的DataFrame(已修正原代码中管道符的笔误):
countMoreLessFunct <- function(data, var) { data %>% group_by(.data[[var]]) %>% summarise(count = n()) %>% ungroup() %>% mutate(tot = sum(count), perc = round(x = count/tot*100, digits = 2)) }
报错代码及信息
直接对df调用map()时出现错误:
df %>% map(.f = ~countMoreLessFunct(df, .x))
错误信息:
# Error in `map()`: # ℹ In index: 1. # ℹ With name: variableA. # Caused by error in `group_by()`: # ℹ In argument: `.data[[structure(c(1L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, `." # Caused by error in `.data[[<fct>]]`: # ! Must subset the data pronoun with a string, not a <factor> object.
错误原因
直接对data.frame使用map()时,map()会迭代每一列的实际值(即因子向量),而不是列名(字符串)。但自定义函数countMoreLessFunct要求传入的var是字符串形式的列名,因此传入因子对象后,.data[[var]]无法识别,导致报错。
解决方案
方法1:迭代列名字符串向量
直接生成列名的字符串向量,用map()迭代该向量:
library(purrr) library(dplyr) names(df) %>% map(~countMoreLessFunct(df, .x))
方法2:使用imap()获取列名
imap()会同时传递每一列的值和对应的列名,我们可以用.y参数提取列名传入函数,此方法会保留列表元素的名称(对应原列名):
imap(df, ~countMoreLessFunct(df, .y))
可选:调整自定义函数兼容多种输入
如果希望函数既能接受字符串列名,也能直接接受列向量,可以修改函数使用dplyr的非标准求值语法:
countMoreLessFunct <- function(data, var) { var <- rlang::ensym(var) data %>% group_by({{var}}) %>% summarise(count = n()) %>% ungroup() %>% mutate(tot = sum(count), perc = round(x = count/tot*100, digits = 2)) } # 此时两种调用方式都可行 countMoreLessFunct(df, variableA) # 直接传列名 countMoreLessFunct(df, "variableA") # 传字符串 # 配合map的话,依然需要迭代列名 names(df) %>% map(~countMoreLessFunct(df, .x))
内容的提问来源于stack exchange,提问作者llewmills
相关产品推荐
相关产品推荐

