在R中按字符串列名聚合

时间:2015-03-04 19:26:48

标签: r

我想将data.frame中的数据分组为两列,然后对特定的第三列求和。例如:

> aggregate(mpg~gear+cyl, data=mtcars, FUN=sum)
  gear cyl   mpg
1    3   4  21.5
2    4   4 215.4
3    5   4  56.4
4    3   6  39.5
5    4   6  79.0
6    5   6  19.7
7    3   8 180.6
8    5   8  30.8

现在,我需要为不同的列多次执行此操作。所以我想编写一个概括它的函数。它采用data.frame和其中一个列(为了简单起见)并做同样的事情。

agg.data <- function(df, colname) {
  aggregate(mpg~gear+colname, data=df, FUN=sum) 
}

运行它将产生:

Error in eval(expr, envir, enclos) : object 'colname' not found

如何将colname的值传递给聚合?

6 个答案:

答案 0 :(得分:7)

将公式的字符串表示粘贴在一起,并将该字符串作为参数提供给公式()...

agg.data <- function(df, colname) {
  aggregate(formula(paste0("mpg~gear+", colname)), data=df, FUN=sum) 
}

> agg.data(mtcars, "cyl")
  gear cyl   mpg
1    3   4  21.5
2    4   4 215.4
3    5   4  56.4
4    3   6  39.5
5    4   6  79.0
6    5   6  19.7
7    3   8 180.6
8    5   8  30.8

答案 1 :(得分:2)

使用data.table

fun.dt <- function(dt, col) {
    dt[, .(mpg=sum(mpg)), by=c("gear", col)]
}

require(data.table)
dt = as.data.table(mtcars)
fun.dt(dt, "cyl")
#    gear cyl   mpg
# 1:    4   6  79.0
# 2:    4   4 215.4
# 3:    3   6  39.5
# 4:    3   8 180.6
# 5:    3   4  21.5
# 6:    5   4  56.4
# 7:    5   8  30.8
# 8:    5   6  19.7

data.tables 中的by表达式除了列/表达式列表外,还可以采用列名的字符向量。我们可以简单地为by参数提供一个字符向量。

答案 2 :(得分:1)

您可以轻松使用&#34;普通&#34; aggregate接口(即不是公式接口)提供变量中的列名。语法略有不同,但仍然很容易,并且不需要粘贴:

agg.data2 <- function(df, colname) {
  aggregate(df[["mpg"]], list(df[["gear"]], df[[colname]]), FUN=sum) 
}
agg.data2(mtcars, "cyl")
#  Group.1 Group.2     x
#1       3       4  21.5
#2       4       4 215.4
#3       5       4  56.4
#4       3       6  39.5
#5       4       6  79.0
#6       5       6  19.7
#7       3       8 180.6
#8       5       8  30.8

这是dplyr的等价物:

library(dplyr)
agg.data.dplyr <- function(df, colname) {
  df %>%
    group_by_(.dots = c("gear", colname)) %>%
    summarise(sum = sum(mpg)) %>%
    ungroup()
}
agg.data.dplyr(mtcars, "cyl")

答案 3 :(得分:1)

您还可以使用deparsesubstitute

传递未加引号的列名
agg.data <- function(df, colname) {
  aggregate(df$mpg, list(df$gear, df[, deparse(substitute(colname))]), FUN=sum) 
}

agg.data(mtcars, cyl)
#   Group.1 Group.2     x
# 1       3       4  21.5
# 2       4       4 215.4
# 3       5       4  56.4
# 4       3       6  39.5
# 5       4       6  79.0
# 6       5       6  19.7
# 7       3       8 180.6
# 8       5       8  30.8

答案 4 :(得分:0)

您还可以使用ggplotwith的方式执行此操作,这样您只需按原样编写组合名,而无需使用substitute传递字符串。

agg.data3 = function (df, colname){
    colname = substitute(colname)
    colname = as.character(colname)
    aggregate(formula(paste0("mpg~gear+", colname)), data=mtcars, FUN=sum)
}

使用

agg.data3(cars, cyl)

答案 5 :(得分:0)

您可能应该在tidyverse上找到一个函数,但是,有一个我经常用于汇总汇总的函数。它嵌入了上面与formula()讨论过的大部分内容,但形式更为笼统:

get.stat = function(df,var.nm,agg.id){

  #--- df       data.frame for aggregation
  #--- var.nm   target variable to be aggregated
  #--- agg.id   index name for aggretation (single value or vector)

  #--- generate aggregation formula
  agg.formula = paste(agg.id, collapse = "+")

  #--- df with summary results
  df.res = data.frame(mean    = aggregate(formula(paste0(var.nm,"~",agg.formula)), df, mean  )[,length(agg.id)+1],
                      median  = aggregate(formula(paste0(var.nm,"~",agg.formula)), df, median)[,length(agg.id)+1],
                      sd      = aggregate(formula(paste0(var.nm,"~",agg.formula)), df, sd    )[,length(agg.id)+1],
                      min     = aggregate(formula(paste0(var.nm,"~",agg.formula)), df, min   )[,length(agg.id)+1],
                      max     = aggregate(formula(paste0(var.nm,"~",agg.formula)), df, max   )[,length(agg.id)+1],
                      sum     = aggregate(formula(paste0(var.nm,"~",agg.formula)), df, sum   )[,length(agg.id)+1],
                      count   = aggregate(formula(paste0(var.nm,"~",agg.formula)), df, length)[,length(agg.id)+1])

  #--- bind indexers
  for(c in 1:length(agg.id)){
    df.res = cbind(df.res, aggregate(formula(paste0(var.nm,"~",agg.formula)), df, mean)[,c])
    colnames(df.res)[length(colnames(df.res))] = agg.id[c]
  }

  #--- re-order col
  df.res = df.res[,c(agg.id,colnames(df.res)[1:(length(colnames(df.res)) - c)])]

  return(df.res)
}

加载该功能后,您可以简单地:

get.stat(df, "mpg",c("gear","cyl"))