再谈谈时间序列

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的实例。

我们最好看个现实例子。

数据:
airline-passengers

这里面有从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 ,它们的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 d ) Y(t-d) 的relation,其中 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。

当两个序列重合时,就是最大的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在二十世纪涨得快,导致她的自相关性也降得快。

发布了25 篇原创文章 · 获赞 26 · 访问量 1151

猜你喜欢

转载自blog.csdn.net/weixin_43810802/article/details/103093360