通过索引对数据库应用滚动平均值

apply a rolling mean to a database by an index

我想通过多个 ID 计算单个数据框中数据的滚动平均值。请参阅下面的示例数据集。

date <- as.Date(c("2015-02-01", "2015-02-02", "2015-02-03", "2015-02-04", 
          "2015-02-05", "2015-02-06", "2015-02-07", "2015-02-08",  
          "2015-02-09", "2015-02-10", "2015-02-01", "2015-02-02", 
          "2015-02-03", "2015-02-04", "2015-02-05", "2015-02-06", 
          "2015-02-07", "2015-02-08", "2015-02-09", "2015-02-10"))
index <- c("a","a","a","a","a","a","a","a","a","a",
           "b","b","b","b","b","b","b","b","b","b")
x <- runif(20,1,100)
y <- runif(20,50,150)
z <- runif(20,100,200)

df <- data.frame(date, index, x, y, z)

我想通过 a 然后通过 b 计算 x、y 和 z 的滚动平均值。

我尝试了以下操作,但出现错误。

test <- tapply(df, df$index, FUN = rollmean(df, 5, fill=NA))

错误:

Error in xu[k:n] - xu[c(1, seq_len(n - k))] : 
  non-numeric argument to binary operator

似乎索引是一个字符这一事实存在问题,但我需要它来计算均值...

这应该可以使用库 dplyrzoo:

library(dplyr)
library(zoo)

df %>% 
  group_by(index) %>% 
  mutate(x_mean = rollmean(x, 5, fill = NA),
         y_mean = rollmean(y, 5, fill = NA),
         z_mean = rollmean(z, 5, fill = NA))

您可以使用 mutate_each 或其他形式的 mutate 来进一步整理它。

您还可以更改 rollmean 中的参数以满足您的需要,例如 align = "right"na.pad = TRUE

1) ave 尝试 ave 而不是 tapply 并确保它仅应用于感兴趣的列,即第 3、4 列, 5.

roll <- function(x) rollmean(x, 5, fill = NA)
cbind(df[1:2], lapply(df[3:5], function(x) ave(x, df$index, FUN = roll)))

给予:

         date index        x         y        z
1  2015-02-01     a       NA        NA       NA
2  2015-02-02     a       NA        NA       NA
3  2015-02-03     a 66.50522 127.45650 129.8472
4  2015-02-04     a 61.71320 123.83633 129.7673
5  2015-02-05     a 56.56125 120.86158 126.1371
6  2015-02-06     a 66.13340 119.93428 127.1819
7  2015-02-07     a 59.56807 105.83208 125.1244
8  2015-02-08     a 49.98779  95.66024 139.2321
9  2015-02-09     a       NA        NA       NA
10 2015-02-10     a       NA        NA       NA
11 2015-02-01     b       NA        NA       NA
12 2015-02-02     b       NA        NA       NA
13 2015-02-03     b 55.71327 117.52219 139.3961
14 2015-02-04     b 54.58450 107.81763 142.6101
15 2015-02-05     b 50.48102 104.94084 136.3167
16 2015-02-06     b 37.89790  95.45489 135.4044
17 2015-02-07     b 33.05259  85.90916 150.8673
18 2015-02-08     b 49.91385  90.04940 147.1376
19 2015-02-09     b       NA        NA       NA
20 2015-02-10     b       NA        NA       NA

2) by 另一种方法是使用byroll2 处理一组,by 将其应用于每个组,生成一个 by 列表,然后 do.call("rbind", ...) 将其放回原处。

roll2 <- function(x) cbind(x[1:2], rollmean(x[3:5], 5, fill = NA))
do.call("rbind", by(df, df$index, roll2))

给予:

           date index        x         y        z
a.1  2015-02-01     a       NA        NA       NA
a.2  2015-02-02     a       NA        NA       NA
a.3  2015-02-03     a 66.50522 127.45650 129.8472
a.4  2015-02-04     a 61.71320 123.83633 129.7673
a.5  2015-02-05     a 56.56125 120.86158 126.1371
a.6  2015-02-06     a 66.13340 119.93428 127.1819
a.7  2015-02-07     a 59.56807 105.83208 125.1244
a.8  2015-02-08     a 49.98779  95.66024 139.2321
a.9  2015-02-09     a       NA        NA       NA
a.10 2015-02-10     a       NA        NA       NA
b.11 2015-02-01     b       NA        NA       NA
b.12 2015-02-02     b       NA        NA       NA
b.13 2015-02-03     b 55.71327 117.52219 139.3961
b.14 2015-02-04     b 54.58450 107.81763 142.6101
b.15 2015-02-05     b 50.48102 104.94084 136.3167
b.16 2015-02-06     b 37.89790  95.45489 135.4044
b.17 2015-02-07     b 33.05259  85.90916 150.8673
b.18 2015-02-08     b 49.91385  90.04940 147.1376
b.19 2015-02-09     b       NA        NA       NA
b.20 2015-02-10     b       NA        NA       NA

3) 宽格式 另一种方法是将 df 从长格式转换为宽格式,在这种情况下,普通的 rollmean 就可以做到。

rollmean(read.zoo(df, split = 2), 5, fill = NA)

给予:

                x.a       y.a      z.a      x.b       y.b      z.b
2015-02-01       NA        NA       NA       NA        NA       NA
2015-02-02       NA        NA       NA       NA        NA       NA
2015-02-03 66.50522 127.45650 129.8472 55.71327 117.52219 139.3961
2015-02-04 61.71320 123.83633 129.7673 54.58450 107.81763 142.6101
2015-02-05 56.56125 120.86158 126.1371 50.48102 104.94084 136.3167
2015-02-06 66.13340 119.93428 127.1819 37.89790  95.45489 135.4044
2015-02-07 59.56807 105.83208 125.1244 33.05259  85.90916 150.8673
2015-02-08 49.98779  95.66024 139.2321 49.91385  90.04940 147.1376
2015-02-09       NA        NA       NA       NA        NA       NA
2015-02-10       NA        NA       NA       NA        NA       NA

之所以可行,是因为两组的日期相同。如果日期不同,那么它可能会引入 NA,而 rollmean 无法处理这些。在那种情况下使用

rollapply(read.zoo(df, split = 2), 5, mean, fill = NA)

注意: 由于输入在其定义中使用随机数以使其可重现,因此我们必须先发出 set.seed。我们使用了这个:

set.seed(123)
date <- as.Date(c("2015-02-01", "2015-02-02", "2015-02-03", "2015-02-04", 
          "2015-02-05", "2015-02-06", "2015-02-07", "2015-02-08",  
          "2015-02-09", "2015-02-10", "2015-02-01", "2015-02-02", 
          "2015-02-03", "2015-02-04", "2015-02-05", "2015-02-06", 
          "2015-02-07", "2015-02-08", "2015-02-09", "2015-02-10"))
index <- c("a","a","a","a","a","a","a","a","a","a",
           "b","b","b","b","b","b","b","b","b","b")
x <- runif(20,1,100)
y <- runif(20,50,150)
z <- runif(20,100,200)