Dplyr条件窗口

时间:2015-11-04 15:16:31

标签: r dplyr

尝试转换以下R data.frame:

    structure(list( Time=c("09:30:01"  ,"09:30:29"  ,"09:35:56",  "09:37:17"  ,"09:37:21"  ,"09:37:28"  ,"09:37:35"  ,"09:37:51"  ,"09:42:11"  ,"10:00:31"),
            Price=c(1,2,3,4,5,6,7,8,9,10),
            Volume=c(100,200,300,100,200,300,100,200,600,100)),
      .Names = c("Time", "Price", "Volume"),
      row.names = c(NA,10L),
      class = "data.frame")

           Time Price Volume
    1  09:30:01     1    100
    2  09:30:29     2    200
    3  09:35:56     3    300
    4  09:37:17     4    100
    5  09:37:21     5    200
    6  09:37:28     6    300
    7  09:37:35     7    100
    8  09:37:51     8    200
    9  09:42:11     9    600
    10 10:00:31    10    100

进入这个

       Time Price  Volume Bin
1  09:30:01     1     100   1
2  09:30:29     2     200   1
3  09:35:56     3     200   1
4  09:35:56     3     100   2
5  09:37:17     4     100   2
6  09:37:21     5     200   2
7  09:37:28     6     100   2
8  09:37:28     6     200   3
9  09:37:35     7     100   3
10 09:37:51     8     200   3
11 09:42:11     9     500   4
12 09:42:11     9     100   5
13 10:00:31    10     100   5

基本上,它是计算累积的总量和每次突破500时的分箱。因此,bin 1为100 + 200 + 200,音量在09:35:56分成200/100并插入一个新行,bin计数器递增。

对于基础R来说,这是相对简单的,但我想知道dplyr是否有更优雅,更有希望更快的方式。

干杯

更新

谢谢@Frank和@AntoniosK。

为了解决您的问题,音量值的范围是从1到10k的所有正整数值。

我微观标记了这两种方法,而dplyr在一个类似于上面的约200k行的数据集上稍微快一点但没有多少。

真的很感激快速的回应和帮助

3 个答案:

答案 0 :(得分:4)

不确定这是最好还是最快的方式,但对于那些Volume值似乎很快。理念很简单。根据{{​​1}}值,您可以使用Volume创建多行TimePrice。然后,每次有新的500批次时,让Volume = 1添加数字和标记。使用这些标志来创建cumsum值。

Bin

答案 1 :(得分:3)

这很难“直截了当”。

使用data.table,它仍然有很多行代码:

library(data.table)
setDT(DF)
DF[, c("cV","cVL") := shift(cumsum(Volume), 0:1, type="lag", fill=0) ]
DF[, end   := ( cV %/% 500 ) - ( cV %% 500 == 0 ) ]
DF[, start := shift(end, type = "lag", fill = -1) + ( cVL %% 500 == 0 ) ]

badcols = c("Volume","cV","cVL","start","end")
DF[,{
  V = 
    if (start==end) Volume
    else c((start+1)*500-cVL, rep(500, max(end-start-2,0)), cV - end*500) 

  c(.SD[, !badcols, with=FALSE], list(Volume = V, Bin = 1+start:end))
}, by=.(r=seq(nrow(DF)))][,!"r",with=FALSE]

给出了

        Time Price Volume Bin
 1: 09:30:01     1    100   1
 2: 09:30:29     2    200   1
 3: 09:35:56     3    200   1
 4: 09:35:56     3    100   2
 5: 09:37:17     4    100   2
 6: 09:37:21     5    200   2
 7: 09:37:28     6    100   2
 8: 09:37:28     6    200   3
 9: 09:37:35     7    100   3
10: 09:37:51     8    200   3
11: 09:42:11     9    500   4
12: 09:42:11     9    100   5
13: 10:00:31    10    100   5

答案 2 :(得分:3)

以下是使用data.table滚动连接功能的一种方式:

require(data.table) # v1.9.6+
setDT(df)[, csum := cumsum(Volume)]
ans = rbind(df, df[.(csum=500 * seq_len(max(csum)%/% 500L)), roll=-Inf, on="csum"])
setorder(ans, Price, csum)
ans = ans[, `:=`(Volume = c(csum[1L], diff(csum)), 
                 id     = (csum-1L) %/% 500L + 1L, 
                 csum   = NULL)][Volume > 0L]

第一步添加一个新列,累计总和为Volume

第二步也许是最重要的一步。让我们看看第二部分。对于500max(csum)的每个倍数,它会加到第一个值> = df$csum上的500的倍数。这是一个滚动的NOCB连接(下一个观察向后移动)。有了这个,我们得到:

#        Time Price Volume csum
# 1: 09:35:56     3    300  500
# 2: 09:37:28     6    300 1000
# 3: 09:37:51     8    200 1500
# 4: 09:42:11     9    600 2000

这些是需要添加到原始data.table的断点。这就是我们对rbind()所做的事情。

然后,我们所做的就是按Price, csum订购,生成返回Volume列。从那里,可以使用id列生成csum列,如图所示。