1.分解一个time series
分解一个time series 可以用additive decomposition或者multiplicative decomposition。我们可以通过目测大概知道一个series是additive还是multiplicative的。
- 如果曲线是linear的,frequency和amplitude不变,那就是additive的。
- 如果曲线是curved的,显示出exponential增长,并且frequency和amplitude是变化的,那就是multiplicative的。
我们手动来构造一个需要用additive decomposition的time series吧:
from random import randrange
from pandas import Series
from matplotlib import pyplot
from statsmodels.tsa.seasonal import seasonal_decompose
series = [i+randrange(10) for i in range(1,100)]
pyplot.plot(series);
pyplot.grid()
这是我们的series,这里用randrange()加入了一些noise;
add_result=seasonal_decompose(series,model='additive',freq=1)
add_result.plot();
因为series是个list,所以要加上frequency=1。如果是Pandas.Series就用了。
我们可以看到,trend和seasonal是对的,但是在residual中没有测到noise。
当然,你也可以手动构造一个multiplicative decomposition的实例。
我们最好看个现实例子。
这里面有从1949到1960年的每月乘客数据,将其保存为csv文件就行了。
from pandas import read_csv
from matplotlib import pyplot
series = read_csv('airline-passengers.csv', header=0, index_col=0)
series.plot();
虽然看起来图像是linear的,但是amplitude明显在变大,所以我们还是选择multiplicative decomposition。
result=seasonal_decompose(series.values,model='multiplicative',freq=12)
result.plot();
看看residual,它在前期和后期都有大幅度波动,说明数据非常受到随机变量的影响。trend和seasonal非常合理。
2. Stationarity
对于一些变量 x 1 , x 2 , … , x n x_1,x_2,\dots,x_n x1,x2,…,xn,它们的independence是非常重要的。independence是唯一的,dependence却有很多种情况。
stationarity就是对dependence structure建模的一种方法。
像大数定律、中心极限定理这些对independent random variable成立的定理,对于stationary random variable也成立。
同样的,stationarity也是唯一定义,而non-stationarity却有多种,当time series为stationary,我们就能用ARMA模型。
问题在于,如何检测一个time series的stationarity?
数据:
daily-total-female-births.csv
将数据保存为csv文件。
首先,我们看一下它的大致情况。
from pandas import read_csv
from matplotlib import pyplot
series = read_csv('daily-total-female-births.csv', header=0, index_col=0)
series.plot();
随着time的推移,mean和variance都不怎么变动,图形没有受到trend和seasonality的影响,这就是典型的stationarity。
这只是肉眼观测,那我们怎么才能说一个time series是stationary的呢?
- ADF test
Augmented Dickey-Fuller test是一种单位根检测。
单位根检测就是看一个time series多大程度上由trend主导。
这里的null hypothesis是time series可以由unit root表示,它是non-stationary的,有着time-dependent structure。
null hypothesis(H0):如果无法拒绝,就说明time series有unit root,意味着它是non-stationary的。
alternative hypothesis(H1):null hypothesis被拒绝了,没有单位根,意味着stationary。
至于要不要reject H0,我们可以看p-value。
p-value>.05:不能拒绝H0,non-stationary。
p_value<=.05:拒绝H0,stationary。
有时p-value的阈值是.01。
from pandas import read_csv
from statsmodels.tsa.stattools import adfuller
df=read_csv('daily-total-female-births.csv', header=0, index_col=0)
X=df.Births.values
result = adfuller(X)
print('ADF Statistic: %f' % result[0])
print('p-value: %f' % result[1])
print('Critical Values:')
for key, value in result[4].items():
print('\t%s: %.3f' % (key, value))
从p-value我们就知道它是stationary的。
我们看到ADF statistics为-4.808291,负的越厉害,我们就越要reject H0。
-4.808291<-3.499,我们可以reject H0,也就是认为time series是stationary的,但有1%的可能犯错。
3. Autocorrelation
correlation指的是两个变量在多大程度上有涨落的相关性。比如收入涨了,开销也随之涨,这就是正相关。
autocorrelation是自己和自己的关联,只是其中一个时间序列保持原样,另一个时间序列有滞后(lag)。比如今天下雨,那么明天也很可能下雨(如果今天晴朗,那么明天下雨的概率会更低)。
举个小例子:
关于autocorrelation,我想找个实例说明,不过在这之前,我们先来了解一下 numpy.correlate()。
numpy.correlate()
correlate就是cross correlation。它衡量的就是 Y ( t ) Y(t) Y(t)与 Y ( t − d ) Y(t-d) Y(t−d)的relation,其中 d d d就是lag。
我们来构造一个以时间为index的Series
from datetime import datetime
from datetime import timedelta
import pandas as pd
s_a=[0,0,0,1,2,3,2,1]
index=np.repeat(datetime(2018,1,1),len(s_a))+[timedelta(days=e) for e in range(len(s_a))]
series1=pd.Series(data=s_a,index=index)
series1
然后再构造相隔两天的series2:
series2=series1.shift(periods=2,freq=timedelta(days=-1))
series2
将它们合起来看看:
画出图像:
from matplotlib import pyplot as plt
plt.plot(con.index,con[0],con[1]);
plt.xticks(rotation=30)
plt.show()
再看一眼两个序列吧:
默认的mode是validate。
我们还可以用‘full’这个mode:
那这个19或者是用‘full’mode的array是怎么算出来的呢?
series1保持不动,series2向右滑动。两个序列做dot product。
比如上图中最后的一个series2与series1做dot product:
0
×
0
+
0
×
0
+
0
×
1
+
1
×
2
+
2
×
3
+
3
×
2
+
2
×
1
+
1
×
0
=
16
0 \times 0+0\times0+0\times1+1\times2+2\times3+3\times2+2\times1+1\times0=16
0×0+0×0+0×1+1×2+2×3+3×2+2×1+1×0=16
空缺的地方补0。
当两个序列重合时,就是最大的19。
当然,我们也可以看到,result是具有对称性的。
数据:
链接:https://pan.baidu.com/s/1eWx0WYfnwcD3Jtquj3KbOA
提取码:b8wt
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import io
import requests
import zipfile
zipfile.ZipFile('babies.zip').extractall('babies')
读取压缩文件。
%ls babies
这里面有从1884年到1921年的美国婴儿出生资料(名字、性别、数量)。
以年份为key,以婴儿出生资料为value(DataFrame)构造一个dict。
files=[file for file in os.listdir('babies') if file.startswith('yob')]
years=np.array(sorted(int(file[3:7]) for file in files))
data={year:pd.read_csv('babies/yob%d.txt' %year,index_col=0,header=None,names=['First name','Gender','Number']) for year in years}
def get_value(name, gender, year):
"""给定一个年份,宝宝姓名和性别,返回宝宝数量"""
dy = data[year]
try:
return dy[dy['Gender'] == gender] \
['Number'][name]
except KeyError:
return 0
def get_evolution(name, gender):
"""返回从1884年到1921年给定姓名和性别的宝宝的数量变化"""
return np.array([get_value(name, gender, year)
for year in years])
从get_evolution那里,我们就能拿到一个time series了。
def autocorr(x):
result = np.correlate(x, x, mode='full')
return result[result.size // 2:]
这是计算自相关的函数,我上面举过例子了。
因为result对称,所以我们只要一半。
def autocorr_name(name, gender, color, axes=None):
x = get_evolution(name, gender)
z = autocorr(x)
# 名字的进化.
axes[0].plot(years, x, '-o' + color,
label=name)
axes[0].set_title("Baby names")
axes[0].legend()
# 自相关.
axes[1].plot(z / float(z.max()),
'-' + color, label=name)
axes[1].legend()
axes[1].set_title("Autocorrelation")
搞一个画图函数。这里的autocorrelation是经过归一化处理的。
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
autocorr_name('Olivia', 'F', 'k', axes=axes)
autocorr_name('Maria', 'F', 'y', axes=axes)
Maria在二十世纪涨得快,导致她的自相关性也降得快。