使用列名作为函数参数

28

使用数据框(data frame),我正在使用dplyr来对一些列进行聚合,如下所示。

> data <- data.frame(a=rep(1:2,3), b=c(6:11))
> data
  a  b
1 1  6
2 2  7
3 1  8
4 2  9
5 1 10
6 2 11
> data %>% group_by(a) %>% summarize(tot=sum(b))
# A tibble: 2 x 2
      a   tot
  <int> <int>
1     1    24
2     2    27

这很完美。但是我想创建一个可重用的函数,使列名可以作为参数传递。

查看类似这里的相关问题的答案,我尝试了以下内容。

sumByColumn <- function(df, colName) {
  df %>%
  group_by(a) %>%
  summarize(tot=sum(colName))
  df
}

然而我无法使它工作。

> sumByColumn(data, "b")

 Error in summarise_impl(.data, dots) : 
  Evaluation error: invalid 'type' (character) of argument. 

> sumByColumn(data, b)

 Error in summarise_impl(.data, dots) : 
  Evaluation error: object 'b' not found. 
> 

7
你应该查阅关于使用dplyr进行编程的博客文章 - bouncyball
4个回答

35

使用最新的dplyr语法可以实现该功能(可以在github上看到):

library(dplyr)
library(rlang)
sumByColumn <- function(df, colName) {
  df %>%
    group_by(a) %>%
    summarize(tot = sum(!! sym(colName)))
}

sumByColumn(data, "b")
## A tibble: 2 x 2
#      a   tot
#  <int> <int>
#1     1    24
#2     2    27

指定变量 b 的另一种方式:

library(dplyr)
sumByColumn <- function(df, colName) {
  myenc <- enquo(colName)
  df %>%
    group_by(a) %>%
    summarize(tot = sum(!!myenc))
}

sumByColumn(data, b)
## A tibble: 2 x 2
#      a   tot
#  <int> <int>
#1     1    24
#2     2    27

这个代码可以正常工作... 但是如果我在 group_by 之前加上 filter( !!myenc > 7 ) ,它就不会返回任何行。在 filter() 中指定列名的正确方法是什么? - user3206440
2
这是文档的一部分。不要使用!!(它是一个方便函数,不能与逻辑向量一起使用),而应该使用正确的函数UQ。例如:filter(UQ(myenc) > 7)。这样就可以正常工作了。 - LyzandeR

13

我们可以使用{{}}

library(dplyr)

sumByColumn <- function(df, colName) {
  df %>%
    group_by(a) %>%
    summarize(tot=sum({{colName}}))
}

sumByColumn(data, b)

#      a   tot
#  <int> <int>
#1     1    24
#2     2    27

6
我们可以使用.data代词。
library(dplyr)

sumByColumn <- function(df, colName) {
  df %>%
    group_by(a) %>%
    summarise(tot = sum(.data[[colName]]))
}

sumByColumn(data, "b")

#      a   tot
#* <int> <int>
#1     1    24
#2     2    27

6

dplyr现在也提供帮助函数(summarise_at, 接受vars, funs参数)来实现这个功能

sumByColumn <- function(df, colName) {
  df %>%
    group_by(a) %>%
    summarize_at(vars(colName), funs(tot = sum))
}

提供相同的答案。
# A tibble: 2 x 2
      # a   tot
  # <int> <int>
# 1     1    24
# 2     2    27

最后一行可能是:summarize_at(colName, sum) - G. Grothendieck
@G.Grothendieck,如果OP想要重命名列,可以使用funs(tot = sum) - CPak
现在已经不再使用带有 _at 后缀的动词,因此对于现在查看此问题的人来说,其他答案更可取。 - Evargalo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接