计算数据集的居中移动平均值,忽略 NA,输出与输入长度相同?

Calculate centered moving average of dataset, ignoring NAs, output same length as input?

最终我想为我的数据集创建一个 15 小时的移动平均线。我正在处理的数据每 15 分钟就有一个日期和时间。我需要 window 居中(所以 30 个时间步 before/after 我正在查看的行)。我还需要能够 link 将移动平均数据与我的日期时间数据一起返回,因此我需要它的长度与我的原始数据帧相同。

我已经尝试使用许多函数(movag、MAVE、rollmean 等),但未能找到正确的组合来进行居中平均并用适当的数字填充结果NA 结果。

您可以定义一个函数来计算您选择的任何 window 大小的移动平均值,如果您只希望滚动平均值包含完整 window的价值。如果您希望它对部分 window 内的值取平均值,您也可以指定它。

它将处理输入中的任何 NAs,如果特定 window 中的所有输入都是 NA,它将 return 和 NA在输出向量的适当点。

这个函数可能会短很多,但为了清楚起见,我已经这样写了,并且包含了基本的错误检查和解释性注释。

moving_average <- function(vec, window, full.window.only = TRUE)
{
  # Define the size of the window on either side
  half_window <- window %/% 2

  # Ensure the vector is long enough to have at least one window
  stopifnot(length(vec) > window) 

  # Get the indices we want to average
  indices <- lapply(seq_along(vec), 
                    function(y) {
                      z <- y + 0:(2 * half_window) - half_window;
                      z[z > 0 & z <= length(vec)]})

  # Get the rolling mean at each of our indices, handling NAs as we go
  result <- sapply(indices, 
                   function(x){
                     if(all(is.na(vec[x]))) return(NA)
                     else return(mean(vec[x], na.rm = TRUE))})

  # Insert NAs if we don't want partial means at either end
  if(full.window.only) 
  {
    result[1:half_window] <- NA
    result[(length(vec) - half_window + 1):length(vec)] <- NA
  }

  return(result)
}

我将在此处展示一个示例,尝试根据您的描述重新创建一些示例数据:

set.seed(1) # Ensures the random numbers are reproducible

df <- data.frame(times  = as.POSIXct("2019-12-25 09:00:00") + 1:20 * 900,
                 values = rnorm(20, 20, 4))

数据框如下所示:

                 times   values
1  2019-12-25 09:20:00 17.49418
2  2019-12-25 09:40:00 20.73457
3  2019-12-25 10:00:00 16.65749
4  2019-12-25 10:20:00 26.38112
5  2019-12-25 10:40:00 21.31803
6  2019-12-25 11:00:00 16.71813
7  2019-12-25 11:20:00 21.94972
8  2019-12-25 11:40:00 22.95330
9  2019-12-25 12:00:00 22.30313
10 2019-12-25 12:20:00 18.77845
11 2019-12-25 12:40:00 26.04712
12 2019-12-25 13:00:00 21.55937
13 2019-12-25 13:20:00 17.51504
14 2019-12-25 13:40:00 11.14120
15 2019-12-25 14:00:00 24.49972
16 2019-12-25 14:20:00 19.82027
17 2019-12-25 14:40:00 19.93524
18 2019-12-25 15:00:00 23.77534
19 2019-12-25 15:20:00 23.28488
20 2019-12-25 15:40:00 22.37561

出于此示例的目的,我将 window 大小设置为 5(值及其前后的两个测量值)。你会想要将你的设置为 30(或者可能是 60,我不确定你的问题)。我所要做的就是:

df$rolling_average <- moving_average(df$values, 5)

现在 df 看起来像这样:

                 times   values rolling_average
1  2019-12-25 09:15:00 17.49418              NA
2  2019-12-25 09:30:00 20.73457              NA
3  2019-12-25 09:45:00 16.65749        20.51708
4  2019-12-25 10:00:00 26.38112        20.36187
5  2019-12-25 10:15:00 21.31803        20.60490
6  2019-12-25 10:30:00 16.71813        21.86406
7  2019-12-25 10:45:00 21.94972        21.04846
8  2019-12-25 11:00:00 22.95330        20.54054
9  2019-12-25 11:15:00 22.30313        22.40634
10 2019-12-25 11:30:00 18.77845        22.32827
11 2019-12-25 11:45:00 26.04712        21.24062
12 2019-12-25 12:00:00 21.55937        19.00824
13 2019-12-25 12:15:00 17.51504        20.15249
14 2019-12-25 12:30:00 11.14120        18.90712
15 2019-12-25 12:45:00 24.49972        18.58229
16 2019-12-25 13:00:00 19.82027        19.83435
17 2019-12-25 13:15:00 19.93524        22.26309
18 2019-12-25 13:30:00 23.77534        21.83827
19 2019-12-25 13:45:00 23.28488              NA
20 2019-12-25 14:00:00 22.37561              NA

为了直观地检查结果,让我们将滚动平均值绘制为点上的一条线:

plot(df$times, df$values, xlab = "Time", ylab = "Value", main = "Moving average")
lines(df$times, df$rolling_average, col = "red")

看起来像这样: