【问题标题】:Grouping in data.table: how to get more than 1 column of results?在 data.table 中分组:如何获得超过 1 列的结果?
【发布时间】:2012-06-29 07:07:21
【问题描述】:

我有一个像这样的data.table 对象

library(data.table)

a <- structure(list(PERMNO = c(10006L, 10006L, 10015L, 10015L, 20000L, 20000L), 
                    SHROUT = c(1427L, 1427L, 1000L, 1001L, 200L, 200L), 
                    PRC = c(6.5, 6.125, 0.75, 0.5, 3, 4), 
                    RET = c(0.005, -0.005, -0.001, 0.05, -0.002, 0.0031)),
                   .Names = c("PERMNO", "SHROUT", "PRC", "RET"), 
               class = c("data.table", "data.frame"), row.names = c(NA, -6L))

setkey(a,PERMNO)

我需要通过PERMNO 执行一些计算,但在此示例中,我们假设它们只有 2 个:

mktcap <- a[ , tail(SHROUT,n=1)*tail(PRC,n=1),by=PERMNO]
sqret <- a[, sum(RET^2),by=PERMNO]

生产的

> mktcap
     PERMNO       V1
[1,]  10006 8740.375
[2,]  10015  500.500
[3,]  20000  800.000

> sqret
     PERMNO        V1
[1,]  10006 5.000e-05
[2,]  10015 2.501e-03
[3,]  20000 1.361e-05

我想将这两个函数合二为一,生成一个包含 3 列的矩阵(或 data.table、data.frame 等),第一个带有 PERMNOs,第二个带有 mktcap 和第三个是sqrt

问题是这个分组函数(即variable[ , function(), by= ])似乎只产生两列的结果,一列有键,一列有结果。

这是我的尝试(其中之一)来生产我想要的东西:

comb.fun <- function(datai) {
     mktcap <- as.matrix(tail(datai[,1],n=1)*tail(datai[,2],n=1),ncol=1)
     sqret <- as.matrix(sum(datai[,3]^2),ncol=1)
     return(c(mktcap,sqret))
}   

myresults <- a[, comb.fun(cbind(SHROUT,PRC,RET)), by=PERMNO]

产生

     PERMNO           V1
[1,]  10006 8.740375e+03
[2,]  10006 5.000000e-05
[3,]  10015 5.005000e+02
[4,]  10015 2.501000e-03
[5,]  20000 8.000000e+02
[6,]  20000 1.361000e-05

(结果都在那里,但它们被强制放入一列)。无论我尝试什么,我都无法通过分组来返回包含多于两列(或多于一列结果)的矩阵。

data.table中分组是否可以获得两列或更多列结果?

【问题讨论】:

  • +1 获得了很好的重现性示例,并从 Matthew Dowle 那里引出了对“宏观表达式”的如此清晰的解释。谢谢。

标签: r data.table


【解决方案1】:

答案(使用list() 收集几个所需的汇总统计信息)位于?data.table 帮助文件的优秀示例部分。 (从底部向上大约有 20 行)。

out <- a[ , list(mktcap = tail(SHROUT,n=1)*tail(PRC,n=1),
                 sqret  = sum(RET^2)),
         by=PERMNO]

out
#    PERMNO   mktcap     sqret
# 1:  10006 8740.375 5.000e-05
# 2:  10015  500.500 2.501e-03
# 3:  20000  800.000 1.361e-05

编辑:

在下面的 cmets 中,Matthew Dowle 描述了一种清理代码的简单方法,其中像 x[i,j,by] 这样的调用中的 j 参数变得非常长。

在上述电话中实施他的建议,您可以改为:

## 1) Use quote() to make an expression object out of the statement passed to j
mm <- quote(list(mktcap = tail(SHROUT,n=1)*tail(PRC,n=1),
                 sqret  = sum(RET^2)))

## 2) Use eval() to evaluate it as if it had been typed directly in the call
a[ , eval(mm), by=PERMNO]
#    PERMNO   mktcap     sqret
# 1:  10006 8740.375 5.000e-05
# 2:  10015  500.500 2.501e-03
# 3:  20000  800.000 1.361e-05

【讨论】:

  • 这很好,但我可以拥有 list(function()) 吗?我这样问是因为我给出的例子是我需要做的一个非常简化的版本。我想要一个返回 5 个结果的函数,并且计算不是像我提供的那样单行计算...
  • 您的意思是a[,{r &lt;- range(PRC); list(min=r[1], max=2[2])}, by=PERMNO]a[,{setNames(as.list(range(PRC)), c("min", "max"))}, by=PERMNO]
  • @Vivi 只是提到另一个构造作为进一步的选择。而不是j=myfunction()(如果不费力地传递所有参数,它将无法工作)您创建一个表达式(使用quote()而不是function())。它有点像一个。然后是j=eval(mymacro) 而不是j=myfunction()。有关详细示例,请参阅常见问题解答 1.6。这可以比函数调用更有效,也更方便。当data.table 看到j=eval(mymacro) 时,它知道在调用范围内找到mymacro,因此如果列名也恰好被称为mymacro,则不会被绊倒。
  • @MatthewDowle -- 这是一个很好的解释。对我来说,它比 FAQ 1.6 更有效,我一遍又一遍地阅读却没有完全掌握。我认为与函数并行(这是我们在这种情况下自然会达到的)真的很有帮助。谢谢。
  • @JoshO'Brien 感谢您为 Matthew 的评论添加示例,这真的很有帮助。
【解决方案2】:

怎么样

comb.fun <- function(a) {
 mktcap <- a[ , tail(SHROUT,n=1)*tail(PRC,n=1),by=PERMNO]
 sqret <- a[, sum(RET^2),by=PERMNO]

 return(merge(mktcap,sqret))
} 

【讨论】:

  • 我的问题是我在分组中有一个分组。您的案例有效,但是当它进入上述级别的分组时,它会再次转换为 2 列...
  • 而且,一个by 查询比两个by 查询效率更高。
  • 酷。最后的几个小错误被证明很难解决,但希望 1.8.1 很快就会出现在 CRAN 上......
  • @ttmaccer 重读我的问题,我同意我问的不是很清楚。老实说,我也不确定我想要的内容是否适用于我的嵌套分组,但这就是我想要做的。我不知道合并(我总是使用 cbind、rbind 或 c),所以为此欢呼。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-19
  • 2017-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-24
  • 1970-01-01
相关资源
最近更新 更多