【问题标题】:Apply different functions to different columns programmatically in data.table R在data.table R中以编程方式将不同的函数应用于不同的列
【发布时间】:2020-03-22 09:24:57
【问题描述】:

我需要使用data.table 以编程方式将不同的函数应用于不同的列和分组依据。

如果已知列和函数,我会这样做:

library(data.table)
DT = data.table(id = rep(letters[1:3], each=3),
                v1 = rep(c(2, 3, 4), each=3),
                v2 = rep(c(5, 10, 15), each=3))
DT
#>    id v1 v2
#> 1:  a  2  5
#> 2:  a  2  5
#> 3:  a  2  5
#> 4:  b  3 10
#> 5:  b  3 10
#> 6:  b  3 10
#> 7:  c  4 15
#> 8:  c  4 15
#> 9:  c  4 15
DT[, .(v1=mean(v1), v2=sum(v2)), keyby=.(id)]
#>    id v1 v2
#> 1:  a  2 15
#> 2:  b  3 30
#> 3:  c  4 45

但我想通过传递列名及其特定功能来做到这一点:

aggregate_functions = list(v1=mean, v2=sum)
col_selection = c('v1', 'v2')

我写了这样的东西,因为我想不出一种将列名传递给lapply的方法:

DT[, lapply(.SD, 
            aggregate_functions[[col_name]] # some way of selecting the right function from aggregate_functions
            ), 
   .SDcols = col_selection, 
   by=id]

我也尝试过meltdcast,但后者将所有功能应用于所有列:

library(data.table)
DT = data.table(id = rep(letters[1:3], each=3),
                v1 = rep(c(2, 3, 4), each=3),
                v2 = rep(c(5, 10, 15), each=3))
DTm = melt(DT, meaure.vars=col_selection, id.vars='id')
DTm
#>     id variable value
#>  1:  a       v1     2
#>  2:  a       v1     2
#>  3:  a       v1     2
#>  4:  b       v1     3
#>  5:  b       v1     3
#>  6:  b       v1     3
#>  7:  c       v1     4
#>  8:  c       v1     4
#>  9:  c       v1     4
#> 10:  a       v2     5
#> 11:  a       v2     5
#> 12:  a       v2     5
#> 13:  b       v2    10
#> 14:  b       v2    10
#> 15:  b       v2    10
#> 16:  c       v2    15
#> 17:  c       v2    15
#> 18:  c       v2    15
DTc = dcast(DTm, id~variable, fun.aggregate=list(sum, mean))
DTc
#>    id value_sum_v1 value_sum_v2 value_mean_v1 value_mean_v2
#> 1:  a            6           15             2             5
#> 2:  b            9           30             3            10
#> 3:  c           12           45             4            15

我可以以编程方式选择和重命名相关列(在本例中为 3 和 4),但这看起来不是一种有效的方法。

当然我可以有一个循环来完成这项工作并合并结果,但我正在寻找data.table 方式。

感谢您的回答,并感谢data.table 的团队。

reprex package (v0.3.0) 于 2019 年 11 月 26 日创建

【问题讨论】:

    标签: r data.table grouping apply mapply


    【解决方案1】:

    一个选项是使用mapply:

    DT[, mapply(function(f,x) as.list(f(x)), aggregate_functions, .SD), id, 
        .SDcols=col_selection]
    

    需要注意col_selectionaggregate_functions 的顺序,以便将正确的函数应用于正确的列。

    输出:

       id v1 v2
    1:  a  2 15
    2:  b  3 30
    3:  c  4 45
    

    从 OP 编辑​​

    只是为了完成这个出色的解决方案。 此解决方案效果很好,如果我们将col_selection 替换为names(aggregate_functions),则订购没有问题。另外它会自动丢弃所有不在列表中的列:

    library(data.table)
    DT = data.table(id = rep(letters[1:3], each=3),
                    v1 = rep(c(2, 3, 4), each=3),
                    v2 = rep(c(5, 10, 15), each=3),
                    id2 = c(rep(c('cc', 'dd'), 4), 'dd')
                    )
    aggregate_functions = list(v1=mean, v2=sum)
    DT[, mapply(function(f,x) as.list(f(x)), aggregate_functions, .SD), id, 
       .SDcols=names(aggregate_functions)]
    #>    id v1 v2
    #> 1:  a  2 15
    #> 2:  b  3 30
    #> 3:  c  4 45
    

    也可以通过传递一个列表来使用多个变量进行聚合:

    DT[, mapply(function(f,x) as.list(f(x)), aggregate_functions, .SD), list(id, id2), 
       .SDcols=names(aggregate_functions)]
    #>    id id2 v1 v2
    #> 1:  a  cc  2 10
    #> 2:  a  dd  2  5
    #> 3:  b  dd  3 20
    #> 4:  b  cc  3 10
    #> 5:  c  cc  4 15
    #> 6:  c  dd  4 30
    

    reprex package (v0.3.0) 于 2019 年 11 月 27 日创建

    【讨论】:

    • 这太棒了!我在原始解决方案中添加了一些内容,如果您不喜欢,可以将其删除。谢谢
    【解决方案2】:

    在我发布问题后,@Uwethis 答案的链接出现在右侧框中,其中包含我正在寻找的结果。我对其进行了调整以匹配我的示例:

    library(magrittr)
    library(data.table)
    DT = data.table(id = rep(letters[1:3], each=3),
                    v1 = rep(c(2, 3, 4), each=3),
                    v2 = rep(c(5, 10, 15), each=3))
    aggregate_functions = list(v1='mean', v2='sum')
    col_selection = c('v1', 'v2')
    aggregate_functions %>%
      names() %>% 
      lapply(
        function(col_selection) lapply(
          aggregate_functions[[col_selection]], 
          function(.fct) sprintf("%s = %s(%s)", col_selection, .fct, col_selection))) %>% 
      unlist() %>% 
      paste(collapse = ", ") %>% 
      sprintf("DT[, .(%s), by = id]", .) %>% 
      parse(text = .) %>% 
      eval()
    #>    id v1 v2
    #> 1:  a  2 15
    #> 2:  b  3 30
    #> 3:  c  4 45
    

    我仍然会对“all in data.table”解决方案感兴趣。

    reprex package (v0.3.0) 于 2019 年 11 月 26 日创建

    【讨论】:

      猜你喜欢
      • 2019-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-03
      • 2014-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多