将列名作为函数参数:dplyr自定义聚合函数报错问题
解决dplyr自定义聚合函数的列名参数报错问题
咱们先拆解下你遇到的问题,其实函数里有两个容易忽略的小问题:
- 函数最后一行返回的是原始的
df,不管前面的group_by和summarize处理得怎么样,最后都会返回原数据框,这等于白做了聚合操作; - 在
summarize里直接用sum(colName),dplyr没办法正确识别你传入的列名——传字符串时,sum会尝试对字符串求和,自然报错;传裸列名时,函数环境里找不到这个变量,因为它是数据框的列,不是函数里的对象。
下面给你几个针对性的解决方案,按需选择:
方案1:支持裸列名参数(推荐,符合dplyr风格)
用dplyr推荐的{{ }}(curly-curly)运算符,它能帮你把传入的裸列名转换成数据框里的列引用,写法非常简洁:
sumByColumn <- function(df, colName) { df %>% group_by(a) %>% summarize(tot = sum({{ colName }})) }
调用的时候直接传裸列名就行:
sumByColumn(data, b)
运行后就能得到你想要的结果:
# A tibble: 2 x 2 a tot <int> <int> 1 1 24 2 2 27
方案2:同时兼容字符串和裸列名参数
如果需要同时支持两种传参方式(比如既可以传"b"也可以传b),可以用rlang包的ensym()函数把参数转换成符号,再用!!(bang-bang)解引用:
sumByColumn <- function(df, colName) { col_sym <- rlang::ensym(colName) df %>% group_by(a) %>% summarize(tot = sum(!!col_sym)) }
现在两种调用方式都能正常工作:
sumByColumn(data, "b") # 传字符串格式的列名 sumByColumn(data, b) # 传裸列名
方案3:只支持字符串参数(.data代词)
如果你只打算用字符串形式传列名,可以用dplyr的.data代词来精准引用数据框里的列:
sumByColumn <- function(df, colName) { df %>% group_by(a) %>% summarize(tot = sum(.data[[colName]])) }
调用时传入字符串列名即可:
sumByColumn(data, "b")
关键提醒
一定要注意函数的返回值!你原来的函数最后一行写的是df,这会直接返回原始数据框,把前面的聚合操作覆盖了。上面的方案里都去掉了这个多余的df,直接返回管道处理后的聚合结果,这才是正确的写法。
内容的提问来源于stack exchange,提问作者user3206440
相关产品推荐
相关产品推荐

