我需要为每个不同的数据组创建一个仅考虑该变量先前观察值的变量的移动平均值。
我曾经使用过一个函数,然后稍微修改一下变量以使其正常工作。让我在下面解释。
我从stackoverflow获得了此功能:
mav <- function(x,n) if(length(x) >= n)stats::filter(x,rep(1/n,n), sides=1) else NA_real_
让我们以两个观测值的移动平均值为例:
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的第三次观测值是第二次和第三次观测值的平均值。
因此,我创建了另一个变量“ 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")
在我看来,与测试数据的唯一区别在于变量的名称和观测值
答案 0 :(得分:1)
如何移位结果而不是输入值?这样的事情(使用来自软件包Region Customer OrderCount Funding
South A 3 2394
South B 2 4323
South C 1 1234
South D 2 3423
的{{1}}):
rollmean
您还可以轻松地使其适应多列(请参见https://stackoverflow.com/a/31482551/6871135)
答案 1 :(得分:0)
我认为您可以通过将用于计算平均值的函数进行平移来加快速度,例如
mav_shift <- function(x,n) if(length(x) >= n)stats::filter(shift(x),rep(1/n,n), sides=1) else NA_real_
通过我的快速测试,这稍微增加了运行函数的时间,并省去了创建新变量的步骤。请进行测试以确保它能按预期工作,但是示例数据的结果看起来是相同的。
编辑和更快的解决方案:
mav_shift <- function(x,n) {
if(length(x) >= n) {
stats::filter(shift(x),rep(1/n,n), sides=1)
} else NA_real_
result <- by(test$values, test$category, mav_shift, n=2, simplify=T)
test$new <- as.vector(unlist(result))
答案 2 :(得分:0)
您可以分别在data.table和zoo软件包中组合功能shift
和rollmeanr
,如下所示。
library(data.table)
library(zoo)
test = data.table(values = 1:12, category = rep(1:2, each = 6))
test[, mg2 := shift(rollmeanr(values, 2, fill = NA)), category]
values category mg2
1: 1 1 NA
2: 2 1 NA
3: 3 1 1.5
4: 4 1 2.5
5: 5 1 3.5
6: 6 1 4.5
7: 7 2 NA
8: 8 2 NA
9: 9 2 7.5
10: 10 2 8.5
11: 11 2 9.5
12: 12 2 10.5