按dplyr中的多列进行分组,使用string向量input

我试图把我对plyr的理解转换成dplyr,但我无法弄清楚如何按多列进行分组。

# make data with weird column names that can't be hard coded data = data.frame( asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE), a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE), value = rnorm(100) ) # get the columns we want to average within columns = names(data)[-3] # plyr - works ddply(data, columns, summarize, value=mean(value)) # dplyr - raises error data %.% group_by(columns) %.% summarise(Value = mean(value)) #> Error in eval(expr, envir, enclos) : index out of bounds 

我错过了将plyr的例子翻译成dplyr-esque的语法?

编辑2017年 :Dplyr已经更新,所以一个更简单的解决scheme是可用的。 看到当前select的答案。

自从发布这个问题以来,dplyr添加了group_by范围版本( 文档在这里 )。 这可以让你使用和select一样的函数,如下所示:

 data = data.frame( asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE), a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE), value = rnorm(100) ) # get the columns we want to average within columns = names(data)[-3] library(dplyr) df1 <- data %>% group_by_at(vars(one_of(columns))) %>% summarize(Value = mean(value)) #compare plyr for reference df2 <- plyr::ddply(data, columns, plyr::summarize, value=mean(value)) table(df1 == df2, useNA = 'ifany') ## TRUE ## 27 

示例问题的输出如预期的(参见上面的plyr和下面的输出):

 # A tibble: 9 x 3 # Groups: asihckhdoydkhxiydfgfTgdsx [?] asihckhdoydkhxiydfgfTgdsx a30mvxigxkghc5cdsvxvyv0ja Value <fctr> <fctr> <dbl> 1 AA 0.04095002 2 AB 0.24943935 3 AC -0.25783892 4 BA 0.15161805 5 BB 0.27189974 6 BC 0.20858897 7 CA 0.19502221 8 CB 0.56837548 9 CC -0.22682998 

请注意,由于dplyr::summarize只剥离一个分组层,所以在dplyr::summarize仍然有一些分组正在进行(有时候可能会被人们惊讶地发现)。 如果你想从意外的分组行为中绝对安全,总是可以在pipe道中添加%>% ungroup

为了编写完整的代码,下面是哈德利的新答案的更新:

 library(dplyr) df <- data.frame( asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE), a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE), value = rnorm(100) ) # Columns you want to group by grp_cols <- names(df)[-3] # Convert character vector to list of symbols dots <- lapply(grp_cols, as.symbol) # Perform frequency counts df %>% group_by_(.dots=dots) %>% summarise(n = n()) 

输出:

 Source: local data frame [9 x 3] Groups: asihckhdoydk asihckhdoydk a30mvxigxkgh n 1 AA 10 2 AB 10 3 AC 13 4 BA 14 5 BB 10 6 BC 12 7 CA 9 8 CB 12 9 CC 10 

在dplyr中对此的支持目前非常薄弱,最终我认为语法是这样的:

 df %.% group_by(.groups = c("asdfgfTgdsx", "asdfk30v0ja")) 

但这可能不会在那里一段时间(因为我需要考虑所有的后果)。

同时,您可以使用regroup() ,它带有一个符号列表:

 library(dplyr) df <- data.frame( asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE), a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE), value = rnorm(100) ) df %.% regroup(list(quote(asihckhdoydk), quote(a30mvxigxkgh))) %.% summarise(n = n()) 

如果你有一个列名称的字符向量,你可以使用lapply()as.symbol()将它们转换为正确的结构:

 vars <- setdiff(names(df), "value") vars2 <- lapply(vars, as.symbol) df %.% regroup(vars2) %.% summarise(n = n()) 

dplyr中列的string规范现在通过dplyr函数的变体来支持,其名称以下划线结尾。 例如,对应于group_by函数,有一个可能需要string参数的group_by_函数。 这个小插图详细描述了这些函数的语法。

以下代码清楚地解决了@sharoz最初提出的问题(注意需要写出.dots参数):

 # Given data and columns from the OP data %>% group_by_(.dots = columns) %>% summarise(Value = mean(value)) 

(请注意,dplyr现在使用%>%运算符,而%.%已弃用)。

直到dplyr完全支持string参数,或许这个要点是有用的:

https://gist.github.com/skranz/9681509

它包含一堆包装函数,如s_group_by,s_mutate,s_filter等等,它们使用string参数。 你可以将它们与普通的dplyr函数混合使用。 例如

 cols = c("cyl","gear") mtcars %.% s_group_by(cols) %.% s_summarise("avdisp=mean(disp), max(disp)") %.% arrange(avdisp) 

如果你把它传递给对象(当然,你不是,但是…),而不是作为一个字符向量:

 df %.% group_by(asdfgfTgdsx, asdfk30v0ja) %.% summarise(Value = mean(value)) > df %.% + group_by(asdfgfTgdsx, asdfk30v0ja) %.% + summarise(Value = mean(value)) Source: local data frame [9 x 3] Groups: asdfgfTgdsx asdfgfTgdsx asdfk30v0ja Value 1 AC 0.046538002 2 CB -0.286359899 3 BA -0.305159419 4 CA -0.004741504 5 BB 0.520126476 6 CC 0.086805492 7 BC -0.052613078 8 AA 0.368410146 9 AB 0.088462212 

df是你的data

?group_by说:

  ...: variables to group by. All tbls accept variable names, some will also accept functons of variables. Duplicated groups will be silently dropped. 

我解释的意思是不是名称的字符版本,但如何在foo$bar引用它们; bar在这里没有引用。 或者如何引用公式中的variables: foo ~ bar

@阿伦也提到你可以这样做:

 df %.% group_by("asdfgfTgdsx", "asdfk30v0ja") %.% summarise(Value = mean(value)) 

但是你不能传入一些未被评估的东西,而不是数据对象中的一个variables的名字。

我认为这是由于哈德利用内部方法来查看你通过...论证所传递的东西。

 data = data.frame( my.a = sample(LETTERS[1:3], 100, replace=TRUE), my.b = sample(LETTERS[1:3], 100, replace=TRUE), value = rnorm(100) ) group_by(data,newcol=paste(my.a,my.b,sep="_")) %>% summarise(Value=mean(value)) 

这个答案中缺less一个(很小的)例子,我想明确指出的是,在一个stream水线中间dynamic生成要分组的variables:

 library(wakefield) df_foo = r_series(rnorm, 10, 1000) df_foo %>% # 1. create quantized versions of base variables mutate_each( funs(Quantized = . > 0) ) %>% # 2. group_by the indicator variables group_by_( .dots = grep("Quantized", names(.), value = TRUE) ) %>% # 3. summarize the base variables summarize_each( funs(sum(., na.rm = TRUE)), contains("X_") ) 

这基本上说明了如何结合使用grepgroup_by_(.dots = ...)来实现这一点。