时间序列基础-序列分解与序列预测

时间序列分析就是发现时间序列的变动规律并使用该规律来预测的统计技术。时间序列分析基于以下三个假设:

  1. 假设事物发展规律趋势会延续到未来。
  2. 预测所依据的数据没有不规则性。
  3. 不考虑事物发展之间的因果关系。

时间序列分析主要包括两方面内容:第一是序列分解;第二是序列预测

代码及数据地址:https://github.com/SeafyLiang/machine_learning_study/tree/master/time_series

一、时间序列分解

一个时间序列包含三种影响因素:

  1. 长期趋势:在一个相当长的时间内表现为一种近似直线的持续向上、向下或平稳的趋势。
  2. 季节变动:受季节变化影响所形成的一种长度和幅度固定的短期周期波动,“季节”指的是周期,可以是周、月、年等。
  3. 不规则变动:受偶然因素的影响所形成的不规则波动,如股票市场受利好或者利空信息的影响,使得股票价格产生的波动。

时间序列按照季节性来分类,分为季节性时间序列和非季节性时间序列。

1.1 非季节性时间序列分解

MA(Moving Average)

移动平均是一种简单平滑技术,它通过在时间序列上逐项推移取一定项数的均值,来表现指标的长期变化和发展趋势。

SMA(Simple Moving Average)

简单移动平均将时间序列上前n个数值做简单的算术平均。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

data = pd.read_csv('data/非季节性时间序列.csv', encoding='utf8', engine='python')

original_data = data['公司A']


def sma_demo():
    # 将非季节性的时间序列,分解为趋势部分和不规则部分
    # SMA 简单移动平均
    sma_data = original_data.rolling(3).mean()
    plt.plot(
        data.index, original_data, 'k-',
        data.index, sma_data, 'g-'
    )
    plt.show()
    # 平滑后的曲线,波动性基本被消除

    # 加入随机误差
    random_error = original_data - sma_data
    plt.plot(
        data.index, original_data, 'k-',
        data.index, sma_data, 'g-',
        data.index, random_error, 'r-'
    )
    plt.show()
    # 随机误差在0附件波动,均值趋向于0


if __name__ == '__main__':
    sma_demo()

在这里插入图片描述

WMA(Weighted Moving Average)

加权移动平均在基于简单移动平均的基础上,对时间序列上前n期的每一期数值赋予对应的权重。
它的基本思想是,提升近期的数据并减弱远期数据对当前预测值的影响,使平滑值更贴近最近的变化趋势。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

data = pd.read_csv('data/非季节性时间序列.csv', encoding='utf8', engine='python')

original_data = data['公司A']

def wma_demo():
    # WMA 加权移动平均
    # 定义窗口大小
    wl = 3
    # 定义每个窗口值的权重
    ww = [1 / 6, 2 / 6, 3 / 6]

    def wma(window):
        return np.sum(window * ww)

    sma_data = original_data.rolling(wl).aggregate(wma)
    random_error = original_data - sma_data
    plt.plot(
        data.index, original_data, 'k-',
        data.index, sma_data, 'g-',
        data.index, random_error, 'r-'
    )
    plt.show()


if __name__ == '__main__':
    wma_demo()

在这里插入图片描述

1.2 季节性时间序列

季节性时间序列,是指在一个时间序列中,若经过n个时间间隔后,呈现出相似的波动,则称该序列具有以n为周期的季节性特性。

import pandas as pd
import statsmodels.api
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']


data = pd.read_csv('data/季节性时间序列.csv', encoding='utf8', engine='python')

total_sales = data['总销量'].values

# 执行季节性时间序列分解
'''
statsmodels.api.tsa.seasonal_decompose(x, freq=None)
x:要分解的季节性时间序列
freq:时间序列的周期,需要自己根据业务来判断数据的周期
'''
tsr = statsmodels.api.tsa.seasonal_decompose(
    total_sales, period=7
)
# 获取趋势部分
trend = tsr.trend
# 获取季节性部分
seasonal = tsr.seasonal
# 获取随机误差部分
random_error = tsr.resid

# 生成2*2的子图
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)

ax1.set_title('总销量')
ax1.plot(
    data.index, total_sales, 'k-'
)

ax2.set_title('趋势部分')
ax2.plot(
    data.index, trend, 'g-'
)

ax3.set_title('季节性部分')
ax3.plot(
    data.index, seasonal, 'r-'
)

ax4.set_title('随机误差')
ax4.plot(
    data.index, random_error, 'b-'
)
plt.show()

在这里插入图片描述

二、序列预测

时间序列预测,是根据时间序列反映出来的发展过程、方向和趋势,进行类推或延伸,借以预测下一段时间或以后若干周期内可能达到的水平。

时间序列预测使用时间序列的均值、方差和协方差这三个统计量来刻画时间序列的本质特征。若这些统计量的值在过去和未来仍保持不变,则为平稳的时间序列。

2.1 不平稳时间序列转换成平稳的时间序列

如果时间序列不平稳,则需要通过差分等技术手段,把它转换成平稳的时间序列,然后再进行预测。

差分(Integrated)

在pandas模块中,使用diff函数,即可对数据进行差分。

单位根检验法

可以使用单位根检验法来检验一个时间序列的平稳性。其中的p-value代表有多大的概率不是一个平稳的时间序列。

import pandas as pd
import matplotlib.pyplot as plt
# 单位根检验法
import statsmodels.tsa.stattools as ts

plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
'''
一个平稳的时间序列指的是,未来的时间序列,它的均值、方差和协方差必定与现在的时间序列相等。
若时间序列不平稳,则需要通过差分等技术手段,把它转换成平稳的时间序列,然后再进行预测。
'''
data = pd.read_csv('data/时间序列预测.csv', encoding='utf8', engine='python')

# 设置索引为时间格式
data.index = pd.to_datetime(data.date, format='%Y%m%d')
# 删除date列,已经保存到索引中了
del data['date']
plt.figure()
plt.plot(data, 'r')
plt.show()


# 使用单位根检验法检验是否是平稳的时间序列
# 封装一个方法,方便解读adfuller函数的结果
def tagADF(t):
    result = pd.DataFrame(index=[
        "Test Statistic Value",
        "p-value", "Lags Used",
        "Number of Observations Used",
        "Critical Value(1%)",
        "Critical Value(5%)",
        "Critical Value(10%)"
    ], columns=['value']
    )
    result['value']['Test Statistic Value'] = t[0]
    result['value']['p-value'] = t[1]
    result['value']['Lags Used'] = t[2]
    result['value']['Number of Observations Used'] = t[3]
    result['value']['Critical Value(1%)'] = t[4]['1%']
    result['value']['Critical Value(5%)'] = t[4]['5%']
    result['value']['Critical Value(10%)'] = t[4]['10%']
    return result


# 使用ADF单位根检验法,检验时间序列的稳定性
adf_data = ts.adfuller(data.value)

# 解读ADF单位根检验结果
adfResult = tagADF(adf_data)
print(adfResult)
#                                 value
# Test Statistic Value         -1.16364
# p-value                      0.689038
# Lags Used                          12
# Number of Observations Used        77
# Critical Value(1%)          -3.518281
# Critical Value(5%)          -2.899878
# Critical Value(10%)         -2.587223

# 68%的概率不是一个平稳的时间序列,故通过差分转换成平稳的时间序列
# 对数据进行差分,删除第一个位置的空值
diff = data.value.diff(1).dropna()

plt.figure()
plt.plot(diff, 'r')
plt.show()

# 使用ADF单位根检验法,检验时间序列的稳定性
adf_diff = ts.adfuller(diff)

# 解读ADF单位根检验结果
adfResult = tagADF(adf_diff)
print(adfResult)
#                                 value
# Test Statistic Value        -4.993859
# p-value                      0.000023
# Lags Used                          12
# Number of Observations Used        76
# Critical Value(1%)          -3.519481
# Critical Value(5%)          -2.900395
# Critical Value(10%)         -2.587498

# 0.000023<0.05,认为差分后的时间序列是平稳的时间序列

在这里插入图片描述

2.2 自回归模型-AR

自回归模型本质是一个线性回归模型,“自”描述的是当前值与历史值之间的关系。通过一个超参数p,把时间序列格式的样本转换为线性回归模型的样本。

2.3 移动平均模型-MA

移动平均模型描述的是自回归模型部分的累计误差。
移动平均模型的意义在于,找到较少参数的移动平均模型,来替代较多参数的自回归模型。

2.4 自回归移动平均模型-ARMA

ARMA模型就是AR模型和MA模型的组合,将因变量仅对它的滞后值以及随机误差项的现值和滞后值进行回归所建立的模型。
在这里插入图片描述

2.5 时序预测代码-AR, MA, ARMA

import statsmodels.api as sm
import pandas as pd
import matplotlib.pyplot as plt
import warnings

warnings.filterwarnings("ignore")
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']

data = pd.read_csv('data/时间序列预测.csv', encoding='utf8', engine='python')

# 设置索引为时间格式
data.index = pd.to_datetime(data.date, format='%Y%m%d')
# 删除date列,已经保存到索引中了
del data['date']

diff = data.value.diff(1).dropna()

plt.figure()
plt.plot(diff, 'r')
plt.show()


def ar_demo():
    '''
    自回归
    :return:
    '''
    # 使用 AR 建模
    arModel = sm.tsa.AR(
        diff
    )

    # 使用 AR 模型的 select_order 方法
    # 自动根据 AIC 指标,选择最优的 p 值
    p = arModel.select_order(
        maxlag=30,
        ic='aic'
    )
    print(p)

    # 使用最优的 p 值进行建模
    arModel = arModel.fit(maxlag=p)

    # 对时间序列模型进行评估
    delta = arModel.fittedvalues - diff
    score = 1 - delta.var() / diff.var()
    print(score)


def sma_demo():
    '''
    简单移动平均
    :return:
    '''
    sma_data = diff.rolling(3).mean()
    plt.plot(
        diff.index, diff, 'k-',
        diff.index, sma_data, 'g-'
    )
    plt.show()


def arma_demo():
    # ARMA模型,使用AIC指标
    # AR模型从1-15中选择最优的p
    # MA模型从1-15中选择最优的q
    # 执行时间非常长,作者执行了10个小时左右
    # ic = sm.tsa.arma_order_select_ic(
    #     diff,
    #     max_ar=15,
    #     max_ma=15,
    #     ic='aic'
    # )
    # 选择最优参数
    order = (6, 6)

    armaModel = sm.tsa.ARMA(
        diff, order
    ).fit()
    # 评估模型得分
    delta = armaModel.fittedvalues - diff
    score = 1 - delta.var() / diff.var()
    print(score)

    # 预测接下来10天的值
    p = armaModel.predict(
        start='2016-03-31',
        end='2016-04-10'
    )

    # 封装一个对差分的数据进行还原的函数
    def revert(diffValues, *lastValue):
        for i in range(len(lastValue)):
            result = []
            lv = lastValue[i]
            for dv in diffValues:
                lv = dv + lv
                result.append(lv)
            diffValues = result
        return diffValues

    # 对差分的数据进行还原
    r = revert(p, 10395)

    plt.figure()
    plt.plot(
        diff, 'r',
        label='Raw'
    )
    plt.plot(
        armaModel.fittedvalues, 'g',
        label='ARMA Model'
    )
    plt.plot(
        p, 'b', label='ARMA Predict'
    )
    plt.legend()
    plt.show()

    r = pd.DataFrame({
        'value': r
    }, index=p.index
    )
    plt.figure()
    plt.plot(
        data.value, c='r',
        label='Raw'
    )
    plt.plot(
        r, c='g',
        label='ARMA Model'
    )
    plt.legend()
    plt.show()


if __name__ == '__main__':
    ar_demo()
    sma_demo()
    arma_demo()
  • 2
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值