时间序列数据的移动平均


专注系列化高质量的R语言教程

推文索引 | 联系小编 | 付费合集


移动平均(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()函数除filtersides外的所有参数,含义和用法不变;

  • 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()函数同名,并且后者使用的频率很高。一旦用户加载dplyrtidyverse包,前者就会被后者覆盖:

library(dplyr)
5981c0db5133a41036cc9b8031170517.png

此时,再调用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
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值