移动平均(Moving Average)是对时间序列数据常用的一种处理办法,目的是减弱数据因偶然因素造成的波动性,便于分析数据的变化趋势。
本篇推文目录如下:
- 理论基础
- R语言的函数
- filter函数
- 定义新函数
- 多列数据求移动平均
- 特别注意
理论基础
阶简单移动平均的方法是:时间点的移动平均值是它与前面个时间点(滞后期)的原始数据的平均数。即
对于简单移动平均来说,参与平均的每期系数都相同,即。如果不相同,则是一般化的加权移动平均。即
通常来说,加权系数之和应为1:
此外,还有居中移动平均。它是以时间点及其前、后各个时间点的(加权)平均数作为移动平均值。以简单居中移动平均为例:
其中为奇数。
R语言的函数
filter函数
在R语言中,可以使用基础统计包stats
中的filter()
函数进行各种移动平均。它的语法结构如下:
filter(x, filter, method = c("convolution", "recursive"),
sides = 2, circular = FALSE, init)
其中,
x
为原始时间序列数据;filter
为加权系数;向量形式,向量长度是移动平均的阶数;method
的默认值convolution
表示移动平均方法,另一个取值recursive
表示自回归方法;side = 1
时为(加权)移动平均;side = 2
(默认值)时为居中(加权)移动平均。
下面以1到20的自然数为原始时间序列数据,使用5阶移动平均举几个例子。
对于简单移动平均来说,filter
参数是长度为5,元素均为1/5的向量;sides
参数为1:
x <- 1:20
## 普通移动平均
filter(x, filter = rep(0.2, 5),
sides = 1)
## [1] NA NA NA NA 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
简单居中移动平均的filter
参数同上,sides
参数为2:
## 居中移动平均
filter(x, filter = rep(0.2, 5),
sides = 2)
## [1] NA NA 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 NA NA
加权移动平均的每期系数不同,通常来说倾向于离点越近,权重越大。比如,设定权重值按线性增长:
w <- 1:5/(sum(1:5))
w
## [1] 0.06666667 0.13333333 0.20000000 0.26666667 0.33333333
filter(x, filter = w, sides = 1)
## [1] NA NA NA NA 2.333333 3.333333 4.333333
## [8] 5.333333 6.333333 7.333333 8.333333 9.333333 10.333333 11.333333
## [15] 12.333333 13.333333 14.333333 15.333333 16.333333 17.333333
定义新函数
通过前面的例子可以看到,移动平均序列的前个值和居中移动平均的前、后个值都是缺失值,这是因为这些位置缺乏足够多的滞后期来计算移动平均值。
一种解决方法是使用这些位置的累积平均值作为它们的移动平均值:
ma <- filter(x, filter = rep(0.2, 5), sides = 1)
ma[1:4] <- cumsum(x[1:4])/1:4
ma
## [1] 1.0 1.5 2.0 2.5 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0
## [16] 14.0 15.0 16.0 17.0 18.0
为了方便,可以编写如下一个新函数:
filter.new <- function(x, ..., filter = NULL, sides = 1,
n = NULL, cum = T) {
if (is.null(n)) n = length(filter) else filter = rep(1/n, n)
ma <- stats::filter(x, ..., filter = filter, sides = sides)
if (cum) ma[1:n] <- cumsum(x[1:n])/1:n
return(ma)
}
用法说明:
...
表示filter()
函数除filter
和sides
外的所有参数,含义和用法不变;filter
参数含义和用法同filter()
函数,但是当新增参数n
不为空时可忽略;sides
参数含义和用法同filter()
函数,但默认值为1;n
表示移动平均的阶数;cum = TRUE
时表示使用累积平均值表示前n-1
个时间点的移动平均值。
filter.new(x, n = 5)
## [1] 1.0 1.5 2.0 2.5 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0
## [16] 14.0 15.0 16.0 17.0 18.0
filter.new(x, filter = rep(0.2, 5))
## [1] 1.0 1.5 2.0 2.5 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0
## [16] 14.0 15.0 16.0 17.0 18.0
多列数据求移动平均
filter()
函数是支持多列矩阵同时计算移动平均值的:
y <- matrix(1:30, ncol = 3)
filter(y, rep(0.2, 5), sides = 1)
## [,1] [,2] [,3]
## 1 NA NA NA
## 2 NA NA NA
## 3 NA NA NA
## 4 NA NA NA
## 5 3 13 23
## 6 4 14 24
## 7 5 15 25
## 8 6 16 26
## 9 7 17 27
## 10 8 18 28
但不支持多列数据框:
y <- data.frame(y)
filter.new(y, n = 5)
## Error in `[.data.frame`(x, 1:n) : 选择了未定义的列
自定义的filter.new()
函数也一样。
对于多列数据框而言,我们可以先将其转化为矩阵;或者使用apply()
函数进行向量化运算:
apply(y, 2, filter.new, n = 5)
## [,1] [,2] [,3]
## [1,] 1.0 11.0 21.0
## [2,] 1.5 11.5 21.5
## [3,] 2.0 12.0 22.0
## [4,] 2.5 12.5 22.5
## [5,] 3.0 13.0 23.0
## [6,] 4.0 14.0 24.0
## [7,] 5.0 15.0 25.0
## [8,] 6.0 16.0 26.0
## [9,] 7.0 17.0 27.0
## [10,] 8.0 18.0 28.0
特别注意
需要特别注意的是,这里使用的filter()
函数与dplyr
包中进行样本筛选的filter()
函数同名,并且后者使用的频率很高。一旦用户加载dplyr
或tidyverse
包,前者就会被后者覆盖:
library(dplyr)
此时,再调用filter()
函数就会默认为后者,导致本文前面一些代码不能正常运行:
filter(x, filter = rep(0.2, 5), sides = 1)
## Error in UseMethod("filter") :
## no applicable method for 'filter' applied to an object of class ## "c('integer', 'numeric')"
解决办法是在filter()
函数前加上工具包名称加以区别:
stats::filter(x, filter = rep(0.2, 5), sides = 1)
## [1] NA NA NA NA 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18