15--时序的平滑化和季节性分解


1 时序的平滑化和季节性分解

对时序数据建立复杂模型之前也需要对其进行描述和可视化。在本节中,我们将对时序进行平滑化以探究其总体趋势,并对其进行分解以观察时序中是否存在季节性因素。

1.1 通过简单移动平均进行平滑处理

时序数据集中通常有很显著的随机或误差成分。为了辨明数据中的规律,我们总是希望能够撇开这些波动,画出一条平滑曲线。画出平滑曲线的最简单办法是简单移动平均。比如每个数据点都可用这一点和其前后两个点的平均值来表示,这就是居中移动平均处理。

时序数据的第一步是画图。这里介绍Nile数据集。这一数据集是埃及阿斯旺市在1871年至1970年间所记录的尼罗河的年度流量R中有几个函数都可以做简单移动平均,包括TTR包中的SMA()函数,zoo包中的rollmean()函数,forecast包中的ma()函数。这里我们用R中自带的ma()函数来对Nile时序数据进行平滑处理。

如下代码给出了时序数据的原始数据图,以及平滑后的图(对应k=3715),生成的图像如下图所示

opar <- par(no.readonly=TRUE)

par(mfrow=c(2,2))      #把画布分成2*2四个区域

ylim <- c(min(Nile), max(Nile))     #ylim()分别指定刻度的下限和上限

plot(Nile, main="Raw time series")

plot(ma(Nile, 3), main="Simple Moving Averages (k=3)", ylim=ylim)

plot(ma(Nile, 7), main="Simple Moving Averages (k=7)", ylim=ylim)

plot(ma(Nile, 15), main="Simple Moving Averages (k=15)", ylim=ylim)

par(opar)

 

结果分析:图左上角画出了这一数据集的原始数据信息。从上图来看,数据总体呈下降趋势,但不同年份的变动非常大。

从图像来看,随着k的增大,图像变得越来越平滑。因此我们需要找到最能画出数据中规律的k,避免过平滑或者欠平滑。这里并没有什么特别的科学理论来指导k的选取,我们只是需要先尝试多个不同的k,再决定一个最好的k。从本例的图像来看,尼罗河的流量从1892年到1900年有明显下降;其他的变动则并不是太好解读,比如1941年到1961年水量似乎略有上升,但这也可能只是一个随机波动。

1.2 季节性分解

对于间隔大于1的时序数据(即存在季节性因子),我们需要了解的就不仅仅是总体趋势了。此时,我们需要通过季节性分解帮助我们探究季节性波动以及总体趋势。

存在季节性因素的时间序列数据(如月度数据、季度数据等)可以被分解为趋势因子、季节性因子和随机因子。趋势因子(trend component)能捕捉到长期变化;季节性因子(seasonal component)能捕捉到一年内的周期性变化;而随机(误差)因子(irregular/error component)则能捕捉到那些不能被趋势或季节效应解释的变化。

此时,可以通过相加模型,也可以通过相乘模型来分解数据。在相加模型中,各种因子之和应等于对应的时序值,即:

 

其中时刻t的观测值即这一时刻的趋势值、季节效应以及随机影响之和。

而相乘模型则将时间序列表示为:(即趋势项、季节项和随机影响相乘)

 

这里通过一个小例子进一步说明相加模型与相乘模型的区别。假设我们有一个时序,记录了10年来摩托车的月销量。在可加模型中,11月和12月(圣诞节)的销量一般会增加500,而1月(一般是销售淡季)的销量则会减少200。此时季节性的波动量和当时的销量无关。在相乘模型中,11月和12月的销量则会增加20%1月的销量减少10%,即季节性的波动量和当时的销量是成比例的。这也使得在很多时候,相乘模型比相加模型更现实一些。

将时序分解为趋势项、季节项和随机项的常用方法是用LOESS光滑做季节性分解。这可以通R中的stl()函数实现:

stl(ts, s.window=, t.window=)

#其中ts是将要分解的时序,参数s.window控制季节效应变化的速度,t.window控制趋势项变化的速度。较小的值意味着更快的变化速度。令s.windows="periodic"可使得季节效应在各年间都一样。这一函数中,参数tss.windows是必须提供的。

虽然stl()函数只能处理相加模型,但这也不算一个多严重的限制,因为相乘模型总可以通

过对数变换转换成相加模型:

log(Yt) = log(Trendt * Seasonalt * Irregulart)

 = log(Trendt) + log(Seasonalt) + log(Irregulart)

用经过对数变换的序列拟合出的相加模型也总可以再转化回原始尺度。下面给出一个例子。

R中自带的AirPassengers序列描述了19491960年每个月国际航班的乘客(单位:千)。

序列图中的第一幅图。从图像来看,序列的波动随着整体水平的增长而增长,即相乘模型更适合这个序列。

序列图中的第二幅图是经过对数变换后的序列。这样序列的波动就稳定了下来,对数变换后的序列就可以用相加模型来拟合了。

stl()函数做季节性分解,代码如下:
查看数据集:

plot(AirPassengers)         #查看数据集原始图像

 

lAirPassengers <- log(AirPassengers)

plot(lAirPassengers, ylab="log(AirPassengers)")        #画出取对数log之后的图像

 

 fit <- stl(lAirPassengers, s.window="period")         #分解时间序列

#stl()函数做季节性分解,s.window="period"用于季节性分解提取

plot(fit)     #画出季节性分解图

结果分析:上图给出了19491960年的时序图、季节效应图、趋势图以及随机波动项。注意此时将季节效应限定为每年都一样(即设定s.window="period")。序列的趋势为单调增长,季节效应表明夏季乘客数量更多(可能因为假期)。每个图的y轴尺度不同,因此我们通过图中右侧的灰色长条来指示量级,即每个长条代表的量级一样。

fit$time.series      #每个观测值各分解项的值

# stl()函数返回的对象中有一项是time.series,它包括每个观测值中的趋势、季节以及随机效应的具体组成。此时,直接用fit$time.series则返回对数变换后的时序,

 

exp(fit$time.series)         #对每个观测值各分解项的值取指数,将结果转化为原始尺度

 

结果分析:观察季节效应可发现,7月的乘客数增长了24%(即乘子为1.24),而11月的乘客数减少了20%(即乘子为0.8

我们还可以通过两幅图来对季节分解进行可视化,即用R中自带的monthplot()函数和forecast包中的seasonplot()函数来画图:

opar <- par(no.readonly=TRUE)

par(mfrow=c(2,1))     #把画布分成两个区域

library(forecast)

monthplot(AirPassengers, xlab="", ylab="")

# monthplot()函数绘制一个时间序列的季节性(或其他)子序列,对于每个季节(或其他类别),都会绘制出一个时间序列

seasonplot(AirPassengers, year.labels="TRUE", main="")

# seasonplot()绘制季节图,这就像一个时间图,只是数据是根据不同年份的季节来绘制的。

par(opar)

 

结果分析:这是AirPassengers序列的月度图(上)和季度图(下),从两幅图中都可以看出总体的增长趋势以及相似的季节模式。第一幅图是月度图,表示的是每个月份组成的子序列(连接所有1月的点、连接所有2月的点,以此类推),以及每个子序列的平均值。从这幅图来看,每个月的增长趋势几乎是一致的。另外,我们还可以看到7月和8月的乘客数量最多。第二幅图是季节图(season plot),这幅图以年份为子序列。从这幅图中我们也可以观测到同样的趋势性和季节效应。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM