在列中定义的窗口内求和

时间:2016-05-07 10:36:27

标签: r performance sum data.table window-functions

我想为组中的每个sum(x)行实现N个下一行的data.table,其中N是来自window列的值。

生成样本数据的代码:

set.seed(100)
ids <- 1:100
x <- floor(runif(100, 1, 100))
groups <- floor(runif(100, 1, 10)) * 10
window <- floor(runif(100, 1, 5))

library('data.table')
data <- data.table(ids, x, groups, window)
setkey(data, groups, ids)

顶行:

 ids  x groups window
 1:   3 55     10      4
 2:   9 55     10      1
 3:  13 28     10      1
 4:  16 67     10      3
 5:  26 17     10      3
 6:  30 28     10      2
 7:  36 89     10      2
 8:  38 63     10      3
 9:  42 86     10      3
10:  48 88     10      1
11:  49 21     10      1
12:  59 60     10      3
13:  65 45     10      4
14:  67 46     10      2
15:  88 25     10      4
16:  19 36     20      2

因此,对于第一行,结果值将根据当前行和后4行的总和计算:res = 55 + 55 + 28 + 67 + 17 = 222

对于组结束的第15行,我只需要当前行的值:res = 25 + 0(无行)= 25。

这是此逻辑的伪代码:

res <- data[, .(ids, groups, x, window , 
            result = sum(.SD[.CurrentRow:(.CurrentRow + Window)], na.rm = T)), 
            by = groups, .SDcols = c("x")]

我希望这可以通过data.table实施。我想避免for循环实现。

2 个答案:

答案 0 :(得分:3)

我不确定如果不迭代所有行就可以这样做,所以这是一个这样的解决方案:

data[, end := pmin(.I + window, .I[.N]), by = groups][
     , res := sum(data$x[.I:end]), by = 1:nrow(data)][1:16]
#    ids  x groups window end res
# 1:   3 55     10      4   5 222
# 2:   9 55     10      1   3  83
# 3:  13 28     10      1   4  95
# 4:  16 67     10      3   7 201
# 5:  26 17     10      3   8 197
# 6:  30 28     10      2   8 180
# 7:  36 89     10      2   9 238
# 8:  38 63     10      3  11 258
# 9:  42 86     10      3  12 255
#10:  48 88     10      1  11 109
#11:  49 21     10      1  12  81
#12:  59 60     10      3  15 176
#13:  65 45     10      4  15 116
#14:  67 46     10      2  15  71
#15:  88 25     10      4  15  25
#16:  19 36     20      2  18 173

正如alexis_laz指出的那样,你可以通过计算cumsum一次然后减去额外的部分来做得更好,从而避免明确地迭代行:

data[, res := { cs <- cumsum(x); 
                cs[pmin(1:.N + window, .N)] - shift(cs, fill = 0)}
     , by = groups]

我将尝试解释这里发生的事情:

  • res := {...}在我们的data.table中添加一列,括号内有R计算;
  • cs = cumsum(x)计算组内所有行的运行总和;
  • cs[pmin(1:.N + window, .N)]获取窗口末尾或该组最后一行的总和值;
  • shift(cs, fill = 0)获取前一行的运行总和;
  • 两者的差异给出了窗口内项目的总和。

由于这个问题有几个可行的答案,我认为值得提供基准测试:

library(microbenchmark)
m <- microbenchmark(
               "tapply(rawr)" = tapplyWay(dd),
               "data.table(eddi)" = data[, end := pmin(.I + window, .I[.N]), by = groups][
                   , res := sum(data$x[.I:end]), by = 1:nrow(data)],
               "data.table(alexis_laz)" = data[, res := {cs = cumsum(x); cs[pmin(1:.N + window, .N)] - shift(cs, fill = 0)}
                                               , by = groups],
               times = 10)
print(m)
boxplot(m)

10 ^ 5行样本的结果:

Unit: milliseconds
            expr     min     lq      mean    median      uq    max        neval
       tapply(rawr) 2575.12 2761.365 2898.63 2905.77  3041.08  3127.86    10
   data.table(eddi) 1418.92 1570.230 1758.70 1656.14  1977.59  2358.85    10
     dt(alexis_laz) 6.82    7.73     8.78    8.09     10.37    12.37119    10

benchmarking of solutions

答案 1 :(得分:1)

首先我们加载from flask_admin.contrib.sqla.filters import FilterGreater class FarmerAdmin(ModelView): FilterGreater(Farmer.amount, u'Amount') 包并将base转换为data.table

data.frame

基本上将行绑定到一个更大的数据框架中,我们可以使用您最喜欢的聚合方法进行汇总

set.seed(100)
ids <- 1:100
x <- floor(runif(100, 1, 100))
groups <- floor(runif(100, 1, 10)) * 10
window <- floor(runif(100, 1, 5))

library('data.table')
data <- data.table(ids, x, groups, window)
setkey(data, groups, ids)

dd <- as.data.frame(data)