在data.table中按变量分组并继承其他变量

时间:2013-10-22 18:46:32

标签: r data.table

我正在研究财务数据集的一些摘要,我想对某个标准进行排序,但不会丢失连续的剩余汇总值。这是一个简单的例子:

set.seed(1)
tseq <- seq(Sys.time(), length.out = 36, by = "mins")
dt <- data.table(TM_STMP = tseq, COMP = rep(c(rep("A", 4), rep("B", 4), rep("C", 4)), 3), SEC = rep(letters[1:12],3), VOL = rpois(36, 3e+6))
dt2 <- dt[, list(SUM = sum(VOL), MEAN = mean(VOL)), by = list(COMP, SEC)]
dt2
   COMP SEC     SUM    MEAN
1:    A   a 9000329 3000110
2:    A   b 9001274 3000425
3:    A   c 9003505 3001168
4:    A   d 9002138 3000713

现在我希望每个COMP的SEC获得最高VOL:

dt3 <- dt2[, list(SUM = max(SUM)), by = list(COMP)]
dt3
   COMP     SUM
1:    A 9003505
2:    B 9002888
3:    C 9005042

这给了我想要的东西,但我希望将其他值保留在特定行(SEC和MEAN)中,使其看起来像这样(手工制作):

   COMP     SUM SEC    MEAN
1:    A 9003505   c 3001168
2:    B 9002888   f 3000963  
3:    C 9005042   k 3001681

我怎样才能做到这一点?

5 个答案:

答案 0 :(得分:3)

如果您正在寻找对应于最大SUM的SEC和MEAN:

 dt3 <- dt2[, list(SUM = max(SUM),SEC=SEC[which.max(SUM)],MEAN=MEAN[which.max(SUM)]), by = list(COMP)]
> dt3
   COMP     SUM SEC    MEAN
1:    A 9003110   a 3001037
2:    B 9000814   e 2999612
3:    C 9002707   i 2999741

编辑:这会更快:

dt2[dt2[, .I[which.max(SUM)], by = list(COMP)]$V1]

答案 1 :(得分:3)

另一种方法是将{。{1}}的data.table传递给:setkey,然后使用COMP, SUM,如下所示:

mult="last"

编辑:回答Simon关于此与@metrics之间速度差异的基准测试':

setkey(dt2, COMP, SUM)
dt2[J(unique(COMP)), mult="last"]
#    COMP SEC     SUM    MEAN
# 1:    A   c 9002500 3000833
# 2:    B   g 9003312 3001104
# 3:    C   i 9000058 3000019

答案 2 :(得分:1)

从您的示例输出中,您不清楚要保留/删除的内容,但您可以在j <{1}}的{​​{1}}参数中列出其他列。 >

DT[i, j, ]

答案 3 :(得分:1)

我对@Metrics中两种不同方法的表现非常感兴趣,我在下面将其表示为which.func,而@Arun表示我将其表示为innate.func。所以,我用上面问题中给出的例子做了一些基准测试。结果如下:

which.func <- function() {dt3 <- dt2[, list(SUM = max(SUM), SEC=SEC[which.max(SUM)], MENA=MEAN[which.max(SUM)]), by = list(COMP)]}
innate.func <- function() {dt3 <- dt2[J(unique(COMP)), mult = "last"]}
library(rbenchmark)
benchmark(which.func, innate.func, replications = 10e+6)
        test replications elapsed relative user.self sys.self
2     innate     10000000  24.689    1.000    24.259    0.425
1 which.func     10000000  32.664    1.323    32.216    0.446

当然,这可能对于who.func有点不公平,因为innate.func涉及对setkey的调用,这对于大型样本来说是一个时间消费者。如果我在函数中包含setkey调用,我会得到以下内容:

innate.func <- function() {setkey(dt2, COMP, SUM); dt3 <- dt2[J(unique(COMP)), mult = "last"]; setkey(dt2, NULL)}
         test replications elapsed relative user.self sys.self 
2 innate.func     10000000  25.271    1.000    24.834    0.430 
1  which.func     10000000  26.476    1.048    26.062    0.397 

看来,这两种方法的表现非常相似。 @Arun的方法可能在data.table方面更优雅,并且需要更少的代码。它的缺点可能来自于maxmin不同的聚合函数,其中@Metrics的方法表现出能够在更一般的环境中应用的特性。

我从两种方法中学习并将它们放入我的工具箱中。

答案 4 :(得分:0)

在我进一步处理这里给出的解决方案的过程中,我在上面的问题中遇到了另一个问题,我找到了一个解决方案,我想分享。

如果我想为用户提供选择

  1. 聚合函数,由aggregate
  2. 表示
  3. 应用aggregate方法的标准(摘要变量),由crit表示,
  4. 然后我遇到了问题,我必须检查,剩下哪些列(参见例如使用哪个列的@Metrics答案)。一个简单的例子:

    我们从上面的问题中获取data.table dt2。现在,用户希望在aggregate = "max"的data.table摘要中对crit = "SUM"变量应用dt2方法。这是一个我发现的解决方案,工作正常(任何讨论当然赞赏):

    aggregate = "max"
    crit = "SUM"
    user call <- expression(do.call(aggregate, list(get(crit))))
    dt2[, .SD[which(get(crit) == eval(mycall))], by = COMP]
    dt2
       COMP SEC     SUM    MEAN
    1:    A   c 9002500 3000833
    2:    B   g 9003312 3001104
    3:    C   i 9000058 3000019