时间序列,又称动态数列。顾名思义,是指将同一统计指标的数值按其发生的时间先后顺序排列而成的数列。涉及应用的领域非常广泛,譬如机器学习,统计学,数学建模等等。这篇文章将会以R中自带的康涅狄格州纽黑文地区从1912年到1971年每年的平均气温数据集(nhtemp)为例,进行时间序列的预测和计算。
在我们进行对时间序列的预测和计算之前,我们要清楚:我们手里的数据是来自数据集的,或者是通过不同的统计平台获取的,他们还不是对象,需要转化,R里的与时间序列有关的很多库函数,如HoltWinters()函数,ets()函数等对参数的要求都是有严格的标准的,必须是时序对象才能使这些函数正确运行,那么转化的代码如下:
nhtemp1<-ts(nhtemp,frequency=12,start=c(1912,2),end=c(1912,12)) %对nhtemp数据集进转化
上面这行代码就把nhtemp转化成了一个时序对象,并把其储存在nhtemp1中,frequency,start,end都是参数,根据自己的需要进行增删:
frequency:顾名思义是频率,周期的意思。直观地来说就是你使用时间序列时你自己定义的后面要进行差分,统计等等动作的周期。
start,开始,我们需要使用定义向量的函数c()来定义序列起始位置的日期,上面这个意思就是用建立向量的方式定义目标数据的开头的时间是1912年2月。
end,结束,用法和start一样。值得注意的是,start和end可以用于对数据集取子集,十分好用。
作为一门高级可视化分析的语言,工具。很明显在我们读取完数据后,很常见的操作就是绘图,这也是R语言相较于其他数学建模工具的一个很鲜明的特点:高级可视化统计分析(小编的体会)。
我们可以使用下面这些命令,对前面的数据集进行绘图(两者在效果上是等效的):
plot(nhtemp1) %对已经转化的数据,就已经是时间序列对象直接执行画图
plot.ts(nhtemp)_ %使用plot.ts()对未经转化的数据集进行一个制图
在R语言中,有着非常多的plot.___()之类的函数,它们旨在对各种各类的对象进行一个制图,不可否认,在程序开发的过程中十分简便.。效果如下:
在该小节开始前,我们要清楚:为什么要对时序对象进行平滑处理呢?
因为数据集他本身是一个真实或者仿真的数据集合,一般不存在规律性。而且数据一般是离散型,因此,在时序数据集会有很显著的随机或误差部分,这对我们后续的统计和数值分析的合理性和准确性会产生很多不必要的影响,而平滑处理就可以解决、避开这些不合理的“数据波动”。
简单移动平均是画出平滑曲线最简易的方法,其中比较简单易操作的一种移动方法就是:每个数据点都可使用这一点和前后两个点的平均值来表示,其名曰:居中移动平均。他的数学表达如下:
其中St就是时间点的平滑值,k=2q+1就是每次用来平均的观测值个数(大家可以结合数轴来想象)。使用该方法的代价就是我们将会失去时序集里面最后的(k-1)/2个观测值。
R中有几个函数都能完成这种简单平均移动,包括TTR包中的SMA()函数,zoo包中的rollmean()函数,forecast包中的ma()函数。我们这里的代码将使用ma()函数对数据集进行平滑处理。代码如下:
library(forecast) %载入forecast包,一般是使用library()函数载入,若不载入,可能无法使用包里专有的函数
opar<-par(no.readonly=TRUE) %该语句是用来更改当前变量环境, par(opar)用来还原默认变量环境。
par(mfrow=c(2,2)) %par()函数就是打开一个绘图设备,mfrow=c(2,2)意思就是产生4张图,两行两列
ylim<-c(min(nhtemp),max(nhtemp)) %以数据集nhtemp的最大值和最小值为边界绘制后面的的四张图的阈值
plot(nhtemp,main="中北大学数学建模") %画出第一张图
plot(ma(nhtemp,3),main="中北大学数学建模,光滑水平为3",ylim=ylim) %画出第二张图
plot(ma(nhtemp,5),main="中北大学数学建模,光滑水平为5",ylim=ylim) %画出第三张图
plot(ma(nhtemp,7),main="中北大学数学建模,光滑水平为7",ylim=ylim) %画出第四张图
在程序段里面ma()函数,第一个参数是要平滑处理的数据集,第二个参数是指定图像的光滑水平。从上面四张图我们可以看出:随着光滑水平K越来越大,图像会变的越来越来光滑,因此我们以后在处理平滑处理数据集的问题上找到最能画出数据中规律的K,避免过于平滑或欠平滑。由于相关科学理论还没有解决怎么公式高效化地选取K的值,因此我们只能多多尝试,看出他们的区别。
对于时间间隔大于1的时序对象,我们需了解的就不仅仅是总体趋势了,此时,我们需要通过季节性分解帮助我们探究季节性波动以及总体趋势。
首先我们要知道的是:存在季节性因素的时间序列数据(如月度,季度数据等等)可以被分解为趋势因子、季节性因子和随机因子。趋势因子能捕捉到长期变化;季节性因子能捕捉到一年内的周期性变化;而随机误差因子则能捕捉到那些不能被趋势或季节效应解释的变化。
接着,小编要先向大家普及一下加法模型和乘法模型:就是将形成时间序列变动的四类构成因素,按照它们的影响方式不同,设定的两种组合模型:
乘法模型:Y = T·S·C·I
加法模型:Y = T+S+C+I
式中:Y:时间序列的指标数值 T:长期趋势成分 S:季节变动成分 C:循环变动成分 I:不规则变动成分
季节因素的表述的不同在于:
乘法模型是假定四个因素对现象的发展的影响是相互作用的,以长期趋势成分的绝对量为基础,其余量均以比率表示,加法模型是假定四个因素的影响是相互独立的,每个成分均以绝对量表示。
那么,我们接下来分解时序数据就要用到上面提到的两种模型。
对于加法模型,各种因子之和应等于对应的时序值,即:
其中时刻t的观测值即这一时刻的趋势值、季节效应以及随机影响之和。
而乘法模型则将时间序列表示为:
即趋势项、季节项和随机影响相乘。
在进行这一步时我们必须知道,加法模型和乘法模型有什么不同,当然不考虑形式上(幼儿园同学都知道公式形式不一样)。根据小编的导师所言,两者最大的区别可概括为:加法模型与变量是无关的,乘法模型与变量是有关的。我在这里可以举个例子:假设我们有一个时序,记录了10年摩托车的月销量。在加法模型中,11月和12月(圣诞节)的销量一般会增加500,而1月(一般是销售淡季)的销量则会减少200。此时季节性波动和加法模型是无关的!在乘法模型中,11月和12月的销售量则会增加20%,1月的销量减少10%。即季节性的波动量和当时的销售是成比例的。这也使得在很多时候,乘法模型比加法模型更贴近实际。
好的,在我们知道了两者比较明显的区别之后,我们就要知道怎么两者之间怎么通过R语言达到转化的目的:
从数学思想来说,要进行乘法模型和加法模型的转化,我们很容易就想到进行对数转化的方法(对数函数有相乘到相加的转化性质),式子如下:
通过对数函数的基本性质,我们就可以简单地对两种模型进行一种转化。
而在实际的算法编写中,两者的转化工作是这样实现的:
plot(nhtemp) %画出原始的时间序列
Lnhtemp<-log(nhtemp) %做对数化,数据大小会发生变化,但用于统计的比例关系不变
plot(Lnhtemp,ylab="log(nhtemp)") %画出对数化后的时间序列图像
fit<-stl(Lnhtemp,s.windows="period") %使用stl()函数对时间序列的三个分量进行分解
plot(fit)
fit$time.series
exp(fit$time.series) %对fit的time.series分量进行指数化,就能回到对数前的数据大小
这段程序主要是想告诉大家如何对乘法模型和加法模型进行转化,其实适用季节性模型的时间序列使用加法模型和乘法模型都是可以的。使用stl()函数对时间序列对象进行分解主要是因为该函数只能处理加法模型,但这种条件现在看来其实不是很大的一种限制,因为乘法模型总可以通过对数变换转换成加法模型。
在时间序列这个科学概念中,我们要进行统计模型的拟合,简单地差分,增强时间序列对象在统计分析上的可用性,就必须要知道我们得到的时间序列是否平稳,若序列不是平稳的,我们要进行差分,差分的次数也需要确定,若序列是平稳的,那么,该时间序列我们就可以直接使用来进行后面统计的处理,很庆幸的是,基于R语言我们可以仅仅通过ndiffs()函数来判断我们的目标序列是否是平稳的,情况如下:
上面这段代码是什么意思呢?在使用ndiffs()函数的时候要记得导入forecast包进行备用,这是当你使用一些函数时必须完成的动作,如果不载入,系统就会说没有这个函数,谨记!
接着我们使用ndiffs()函数,函数的对象是时间序列对象,我们使用的是R库自备的nhtemp数据集,不需要担心对象的类型问题。我们可以看到该函数返回的值为1,就说明该时间序列不是平稳的,返回的值就是该序列需要差分的次数。
那么,到这里大家可能已经明白了这个函数的用法:
1.若返回值为0,序列是平稳的,根本不需要差分!
2.若返回值为其他值,序列是不平稳的,返回的值就是需要差分的次数。