【发布时间】:2018-12-29 14:09:24
【问题描述】:
我需要为每个不同的数据组创建一个变量的移动平均值,该平均值仅考虑该变量的先前观察结果。
我曾经使用一个函数,然后稍微修改我的变量以使其工作。下面我来解释一下。
我从 stackoverflow 得到这个函数:
mav <- function(x,n) if(length(x) >= n)stats::filter(x,rep(1/n,n), sides=1) else NA_real_
让我们以 2 个观察值的移动平均值为例:
test = data.table("values" = c(1,2,3,4,5,6,7,8, 9,10,11,12), "category" = c(1,1,1,1,1,1,2,2,2,2,2,2))
test[, ma2 := as.numeric(mav(values, n = 2)), by = category]
这会产生:
values category ma2
1 1 NA
2 1 1.5
3 1 2.5
4 1 3.5
5 1 4.5
6 1 5.5
7 2 NA
8 2 7.5
9 2 8.5
10 2 9.5
11 2 10.5
12 2 11.5
我希望 ma2 的第三个观测值是 ma2 的最后两个观测值的平均值。但是在这里,ma2 的第 3 次观测值是第 2 次和第 3 次观测值的平均值。
所以我创建了另一个变量“Vprev”,它与“Values”相同,但每次观察都采用“Values”的前一个值:
test[, vprev:= as.numeric(shift(values, 1L, type = "lag" )), by = category]
然后,我在 vprev 变量上运行移动平均线(“TRUEma2”):
test[, TRUEma2 := as.numeric(mav(vprev, n = 2)), by = category]
values category ma2 vprev TRUEma2
1 1 NA NA NA
2 1 1.5 1 NA
3 1 2.5 2 1.5
4 1 3.5 3 2.5
5 1 4.5 4 3.5
6 1 5.5 5 4.5
7 2 NA NA NA
8 2 7.5 7 NA
9 2 8.5 8 7.5
10 2 9.5 9 8.5
11 2 10.5 10 9.5
12 2 11.5 11 10.5
过去工作得很好,因为我的数据集非常小。但是现在我必须在具有大约 2 到 3 百万个观察值的多个数据集上执行此操作。我必须为每个数据集中的大约 30 个变量创建移动平均线。我描述的每个变量的过程最多需要 1 分 40 秒,所以我计算出我需要 25 小时来预处理我的所有数据集......
我看到最耗时的是我创建一个新变量的部分,该变量是另一个变量的先前观察(大约 1 分钟):
test[, vprev:= as.numeric(shift(values, 1L, type = "lag" )), by = category]
移动平均线本身并不需要很多时间来计算。
我尝试通过在移动平均代码行中放置一个 shift() 来跳过这个:
test[, TRUEma2 := as.numeric(mav(shift(values,1L,type = "lag), n = 2)), by = category]
但这并没有更快。
我也试过这样修改移动平均函数:
mav2 <- function(x,n) if(length(x) >= n+1)stats::filter(x-1,rep(1/n,n), sides=1) else NA_real_
但是x的第一个值可以取它之前的观察值,即不在同一个数据组/类别中。
values category mav2
1 1 NA
2 1 0.5
3 1 1.5
4 1 2.5
5 1 3.5
6 1 4.5
7 2 NA
8 2 6.5
9 2 7.5
10 2 8.5
11 2 9.5
12 2 10.5
所以这是我的问题:是否有可能有一个与上述第一个一样快的移动平均函数,但它只计算先前观察的平均值?
非常感谢您的帮助:)
编辑:我尝试了 lbusett 和 Icecreamtoucan 提出的解决方案,虽然它适用于测试数据,但我在真实数据上收到以下错误消息:
Error in[.data.table(toptrain2, ,:=(paste0("m3_", c("killsM")), :
Type of RHS ('double') must match LHS ('logical'). To check and coerce would impact performance too much for the fastest cases. Either change the type of the target column, or coerce the RHS of := yourself (e.g. by using 1L instead of 1)
我被要求提供实际数据的样本。这是一个 dput(只是我数据的一小部分):
structure(list(killsM = c(4L, 2L, 0L, 3L, 6L, 0L, 1L, 2L, 3L,
5L, 6L, 1L, 4L, 4L, 2L, 6L, 6L, 3L, 1L, 2L), soloKillsM = c(4L,
2L, 0L, 0L, 3L, 0L, 0L, 1L, 1L, 3L, 0L, 0L, 1L, 2L, 0L, 3L, 0L,
1L, 0L, 0L), deathsM = c(3L, 5L, 5L, 1L, 4L, 4L, 3L, 2L, 0L,
4L, 1L, 7L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 1L), assistsM = c(1L,
1L, 2L, 2L, 7L, 0L, 2L, 2L, 3L, 0L, 4L, 1L, 0L, 1L, 1L, 1L, 4L,
1L, 3L, 3L), killParticipationM = c(0.151515151515152, 0.0909090909090909,
0.125, 0.3125, 0.464285714285714, 0, 0.157894736842105, 0.210526315789474,
0.222222222222222, 0.185185185185185, 0.434782608695652, 0.0869565217391304,
0.2, 0.25, 0.130434782608696, 0.304347826086957, 0.4, 0.16, 0.181818181818182,
0.227272727272727), firstTowerKillM = c(0L, 0L, 0L, 0L, 1L, 0L,
0L, 0L, 0L, 0L, 1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L)), row.names = c(NA,
20L), class = "data.frame")
对我来说,与测试数据的唯一区别似乎是变量的名称和观察值
【问题讨论】:
-
@IceCreamToucan 感谢您的建议,不幸的是它给了我以下错误消息:
[.data.table(test, ,:=(ma, shift(rollmeanr(values, : RHS 的类型) 中的错误) 'double') 必须匹配 LHS ('logical')。对于最快的情况,检查和强制会影响性能。要么更改目标列的类型,要么强制 := 自己的 RHS(例如,使用 1L 代替1) -
我承认我不知道该怎么做:/。你会如何改写: test[, ma := shift(rollmeanr(values, 4, na.pad = T)), category] 就其本身而言,“ma”是一个新的、不同于“values”的列,不是吗?
-
我尝试了除“ma”之外的其他名称,并收到相同的错误消息。我会立即用 dput 更新主帖
-
你在真实数据集上的“类别”变量是什么?如果您在类别变量的任何组中少于 2 个观察值,我提出的解决方案似乎会失败(可能是因为 rollmean 在 1 元素数组上给出 NULL)。
-
“类别”变量是“sumchamp”,但在 dput 中不存在。 Sumchamp 只是一个 6 位数的数字,我将其用作 ID。某些组的观察次数少于 2 次是 100% 正确的。要删除它们,我通常会先计算移动平均值,然后删除所有具有“NA”值的观测值。
标签: r data.table moving-average