【问题标题】:Using variable in data.table group by clause在 data.table group by 子句中使用变量
【发布时间】:2015-10-14 13:03:24
【问题描述】:

我有一个我想要总结的 data.table。这是我的方法

library(data.table)

dtIris <-data.table(iris)
dt1 <- dtIris[, list(AvgSepalWidth = mean(Sepal.Width)), 
              by=list(TrimSpecies = substr(Species,1,3),Petal.Length)]

我希望能够使用一个变量来识别要分组的项目之一,但我无法用它来评估列表中的变量。它只是将其视为字符串并引发错误。

myvar <- "Petal.Length"
dt1 <- dtIris[, list(AvgSepalWidth = mean(Sepal.Width)), 
              by=list(TrimSpecies = substr(Species,1,3),myvar)]

我试过noquote()eval()parse(text=) 都无济于事。任何指导将不胜感激。

【问题讨论】:

    标签: r list data.table


    【解决方案1】:

    当我想同时按超过 1 个变量进行分组时,事情变得有点棘手。继续的方法是创建一个变量myvar,其中包含应该进行分组的列的名称(在本例中为2);然后可以通过以下方式使用get 两次:

    dtIris[, list(AvgSepalWidth = mean(Sepal.Width)), by = list(get(myvar[1]), get(myvar[2]))]
    

    【讨论】:

      【解决方案2】:

      您可以使用eval(parse(text=myvar))get(myvar),尽管这会将您的分组列分别命名为parseget(然后您可以重命名它)。

      myvar <- "Petal.Length"
      dtIris[, list(AvgSepalWidth = mean(Sepal.Width)), 
                    by=list(TrimSpecies = substr(Species,1,3), eval(parse(text=myvar)))]
      
      dtIris[, list(AvgSepalWidth = mean(Sepal.Width)), 
                    by=list(TrimSpecies = substr(Species,1,3), get(myvar))]
      

      我不确定如何以您想要的方式保留名称。 (编辑:by=setNames(list(...), c('TrimSpecies', myvar)) - 谢谢@thelatemail!)


      编辑 - 出于兴趣,回应下面的一些 cmets。

      library(rbenchmark)
      benchmark(
          eval=dtIris[, list(AvgSepalWidth = mean(Sepal.Width)), 
                    by=list(TrimSpecies = substr(Species,1,3), eval(parse(text=myvar)))],
          get=dtIris[, list(AvgSepalWidth = mean(Sepal.Width)), 
                    by=list(TrimSpecies = substr(Species,1,3), get(myvar))],
          chain=dtIris[, TrimSpecies := substr(Species,1,3)][,list(AvgSepalWidth = mean(Sepal.Width)),by=c("TrimSpecies",myvar)][,TrimSpecies:=NULL][]
      )
         test replications elapsed relative user.self sys.self user.child sys.child
      3 chain          100   0.151    1.987     0.250        0          0         0
      1  eval          100   0.079    1.039     0.097        0          0         0
      2   get          100   0.076    1.000     0.094        0          0         0
      

      geteval(parse(text=..))) 快,后者比定义 TrimSpecies 快,使用 by 的字符形式然后将其删除(链接 dts)。

      【讨论】:

      • dtIris[, list(AvgSepalWidth = mean(Sepal.Width)), by=setNames(list(substr(Species,1,3), get(myvar)),c("TrimSpecies",myvar))]
      • 这是我最喜欢的功能,用于在名称未通过时进行整理。
      • 出于兴趣,我就这么做了
      • 哈哈,是的,np,我删除了答案,这似乎太荒谬了,stackoverflow.com/questions/31577893/…
      • 对于任意数量的列,mget() 函数会创建列表并为您命名,例如byfields &lt;- c("a", "b"); DT &lt;- data.table(x=1:20, a=rep(1:2, each=10), b=rep(1:4, each=5)); summary &lt;- DT[, sum(x), by=mget(byfields)]
      【解决方案3】:

      by 语句不接受字符串。它确实接受变量引用。

      因此,您可以创建一个变量来复制要聚合的列的数据,而不是引用列名:

      myvar <- dtIris[,Petal.Length]
      dt2 <- dtIris[, list(AvgSepalWidth = mean(Sepal.Width)), 
                by=list(TrimSpecies = substr(Species,1,3),myvar)]
      all(dt2==dt1)
      #TRUE
      

      【讨论】:

      • by 可以接受字符串,例如dtIris[, list(AvgSepalWidth = mean(Sepal.Width)), by = "Petal.Length"]。您的解决方案有效,但不能解决名称问题,更不用说它会不必要地复制整列。
      猜你喜欢
      • 1970-01-01
      • 2011-09-25
      • 2010-11-14
      • 2019-10-03
      • 2018-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-14
      相关资源
      最近更新 更多