【问题标题】:Aggregate by string column name in R在R中按字符串列名聚合
【发布时间】:2015-03-04 19:26:47
【问题描述】:

我想将 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 的值传递给聚合?

【问题讨论】:

    标签: r


    【解决方案1】:

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

    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
    

    【讨论】:

    • 您可能希望在函数内更改为data=df,然后更改为agg.data(mtcars, "cyl"),因为cars 是完全不同的数据集。
    【解决方案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 参数提供一个字符向量。

    【讨论】:

      【解决方案3】:

      您还可以使用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】:

        您可以轻松地使用“普通”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")
        

        【讨论】:

          【解决方案5】:

          您也可以使用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)
          

          【讨论】:

            【解决方案6】:

            你可能应该在 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"))

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-07-27
              • 2018-08-02
              • 2018-10-29
              • 1970-01-01
              • 1970-01-01
              • 2014-06-01
              • 1970-01-01
              • 2020-04-02
              相关资源
              最近更新 更多