复杂的汇总功能 - 是否可以使用R data.table包解决?

Mee*_*eep 20 r data.table

我正在重写一些分析大量数据(大约1700万行)的R脚本,我想我会尝试通过使用data.table包来提高它的内存效率(我只是学习它!).

代码的一部分让我感到困惑.我不能发布我的原始解决方案,因为(1)它是废话(慢!),(2)它对数据非常细微差别,并且只会使这个问题复杂化.

相反,我已经制作了这个玩具示例(它确实是一个玩具示例):

ds <- data.table(ID=c(1,1,1,1,2,2,2,3,3,3),
Obs=c(1.5,2.5,0.0,1.25,1.45,1.5,2.5,0.0,1.25,1.45), 
Pos=c(1,3,5,6,2,3,5,2,3,4))
Run Code Online (Sandbox Code Playgroud)

看起来像这样:

    ID  Obs Pos
 1:  1 1.50   1
 2:  1 2.50   3
 3:  1 0.00   5
 4:  1 1.25   6
 5:  2 1.45   2
 6:  2 1.50   3
 7:  2 2.50   5
 8:  3 0.00   2
 9:  3 1.25   3
10:  3 1.45   4
Run Code Online (Sandbox Code Playgroud)

为了便于解释,我假装我们正在观察列车(每列火车都有自己的ID),穿过线性单向轨道,观察(有些价值,而不是问题的重要性)关于正在制作的列车在设定的位置(POS沿着轨道,在这里1-6).预计火车不会使它成为整个轨道的长度(可能在它到达后6之前爆炸),有时观察者会错过观察......位置是连续的(因此,如果我们错过了观察火车在4号位,但我们在5号位观察到它,我们知道它必须通过位置4).

从上面的data.table,我需要生成一个这样的表:

   Pos Count
1:   1     3
2:   2     3
3:   3     3
4:   4     3
5:   5     2
6:   6     1
Run Code Online (Sandbox Code Playgroud)

对于我的data.table ds中的每个唯一位置,我计算了在轨道上(或更远)进入该位置的列车数量,无论观察是否在轨道上的该位置进行.

如果有人对如何解决这个问题有任何想法或建议,我将不胜感激.不幸的是,我对data.table不太熟悉,知道是否可以这样做!或者它可能是非常简单的问题要解决,我只是慢:)

Mat*_*wle 15

好问题!! 示例数据构造得特别好,并且得到了很好的解释.

首先,我将展示这个答案,然后我将逐步解释.

> ids = 1:3   # or from the data: unique(ds$ID)
> pos = 1:6   # or from the data: unique(ds$Pos)
> setkey(ds,ID,Pos)

> ds[CJ(ids,pos), roll=-Inf, nomatch=0][, .N, by=Pos]
   Pos N
1:   1 3
2:   2 3
3:   3 3
4:   4 3
5:   5 2
6:   6 1
> 
Run Code Online (Sandbox Code Playgroud)

这对您的大数据也应该非常有效.

一步步

首先我尝试了Cross Join(CJ); 即,对于每个位置的每列火车.

> ds[CJ(ids,pos)]
    ID Pos  Obs
 1:  1   1 1.50
 2:  1   2   NA
 3:  1   3 2.50
 4:  1   4   NA
 5:  1   5 0.00
 6:  1   6 1.25
 7:  2   1   NA
 8:  2   2 1.45
 9:  2   3 1.50
10:  2   4   NA
11:  2   5 2.50
12:  2   6   NA
13:  3   1   NA
14:  3   2 0.00
15:  3   3 1.25
16:  3   4 1.45
17:  3   5   NA
18:  3   6   NA
Run Code Online (Sandbox Code Playgroud)

我看到每列火车有6排.我看到3列火车.我按照预期排了18行.我看到NA没有观察到那列火车的地方.好.校验.交叉连接似乎正在起作用.我们现在构建查询.

如果列车在位置n处观察到它必须通过以前的位置,你写道.我马上想roll.我们来试试吧.

ds[CJ(ids,pos), roll=TRUE]
    ID Pos  Obs
 1:  1   1 1.50
 2:  1   2 1.50
 3:  1   3 2.50
 4:  1   4 2.50
 5:  1   5 0.00
 6:  1   6 1.25
 7:  2   1   NA
 8:  2   2 1.45
 9:  2   3 1.50
10:  2   4 1.50
11:  2   5 2.50
12:  2   6 2.50
13:  3   1   NA
14:  3   2 0.00
15:  3   3 1.25
16:  3   4 1.45
17:  3   5 1.45
18:  3   6 1.45
Run Code Online (Sandbox Code Playgroud)

嗯.这推动了每列火车的观察结果.它NA为火车2号和3号火车留下了一些位置,但是你说如果在2号火车上观察到它必须经过位置1.它还将列车2和3的最后一次观测推到了6号位置,但你说火车可能会爆炸.所以,我们想倒退!那是roll=-Inf.这很复杂,-Inf因为你也可以控制向后滚动的距离,但我们不需要这个问题; 我们只想无限期地倒退.让我们试着roll=-Inf看看会发生什么.

> ds[CJ(ids,pos), roll=-Inf]
    ID Pos  Obs
 1:  1   1 1.50
 2:  1   2 2.50
 3:  1   3 2.50
 4:  1   4 0.00
 5:  1   5 0.00
 6:  1   6 1.25
 7:  2   1 1.45
 8:  2   2 1.45
 9:  2   3 1.50
10:  2   4 2.50
11:  2   5 2.50
12:  2   6   NA
13:  3   1 0.00
14:  3   2 0.00
15:  3   3 1.25
16:  3   4 1.45
17:  3   5   NA
18:  3   6   NA
Run Code Online (Sandbox Code Playgroud)

那更好.差不多了.我们现在需要做的就是数数.但是,NA在第2和第3列火车爆炸后,那些讨厌的人就在那里.我们删除它们.

> ds[CJ(ids,pos), roll=-Inf, nomatch=0]
    ID Pos  Obs
 1:  1   1 1.50
 2:  1   2 2.50
 3:  1   3 2.50
 4:  1   4 0.00
 5:  1   5 0.00
 6:  1   6 1.25
 7:  2   1 1.45
 8:  2   2 1.45
 9:  2   3 1.50
10:  2   4 2.50
11:  2   5 2.50
12:  3   1 0.00
13:  3   2 0.00
14:  3   3 1.25
15:  3   4 1.45
Run Code Online (Sandbox Code Playgroud)

顺便说一下,data.table尽可能多地在一个单一内部,DT[...]因为它是如何优化查询.在内部,它不会创建NA然后删除它们; 它永远不会创造NA出来.这个概念对提高效率很重要.

最后,我们所要做的就是计算.我们可以在最后将其作为复合查询来解决.

> ds[CJ(ids,pos), roll=-Inf, nomatch=0][, .N, by=Pos]
   Pos N
1:   1 3
2:   2 3
3:   3 3
4:   4 3
5:   5 2
6:   6 1
Run Code Online (Sandbox Code Playgroud)

  • @MartinMorgan是的,很好。也许是:ds [,max(Pos),by = ID] [,rev(cumsum(rev(tabulate(V1))))]]。还为您的答案添加了更长的评论。 (2认同)

Mar*_*gan 9

data.table听起来很棒的解决方案.从订购数据的方式,可以找到每列火车的最大值

maxPos = ds$Pos[!duplicated(ds$ID, fromLast=TRUE)]
Run Code Online (Sandbox Code Playgroud)

然后将到达该位置的列车制成表格

nAtMax = tabulate(maxPos)
Run Code Online (Sandbox Code Playgroud)

并计算每个位置的列车累计总和,从结束算起

rev(cumsum(rev(nAtMax)))
## [1] 3 3 3 3 2 1
Run Code Online (Sandbox Code Playgroud)

我认为对于大数据来说这将是非常快的,尽管不完全是内存效率.