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

  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个数值做简单的算术平均。

  1. import pandas as pd
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. data = pd.read_csv('data/非季节性时间序列.csv', encoding='utf8', engine='python')
  5. original_data = data['公司A']
  6. def sma_demo():
  7. # 将非季节性的时间序列,分解为趋势部分和不规则部分
  8. # SMA 简单移动平均
  9. sma_data = original_data.rolling(3).mean()
  10. plt.plot(
  11. data.index, original_data, 'k-',
  12. data.index, sma_data, 'g-'
  13. )
  14. plt.show()
  15. # 平滑后的曲线,波动性基本被消除
  16. # 加入随机误差
  17. random_error = original_data - sma_data
  18. plt.plot(
  19. data.index, original_data, 'k-',
  20. data.index, sma_data, 'g-',
  21. data.index, random_error, 'r-'
  22. )
  23. plt.show()
  24. # 随机误差在0附件波动,均值趋向于0
  25. if __name__ == '__main__':
  26. sma_demo()

image.pngimage.png

WMA(Weighted Moving Average)

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

  1. import pandas as pd
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. data = pd.read_csv('data/非季节性时间序列.csv', encoding='utf8', engine='python')
  5. original_data = data['公司A']
  6. def wma_demo():
  7. # WMA 加权移动平均
  8. # 定义窗口大小
  9. wl = 3
  10. # 定义每个窗口值的权重
  11. ww = [1 / 6, 2 / 6, 3 / 6]
  12. def wma(window):
  13. return np.sum(window * ww)
  14. sma_data = original_data.rolling(wl).aggregate(wma)
  15. random_error = original_data - sma_data
  16. plt.plot(
  17. data.index, original_data, 'k-',
  18. data.index, sma_data, 'g-',
  19. data.index, random_error, 'r-'
  20. )
  21. plt.show()
  22. if __name__ == '__main__':
  23. wma_demo()

image.png

1.2 季节性时间序列

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

  1. import pandas as pd
  2. import statsmodels.api
  3. import matplotlib.pyplot as plt
  4. plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
  5. data = pd.read_csv('data/季节性时间序列.csv', encoding='utf8', engine='python')
  6. total_sales = data['总销量'].values
  7. # 执行季节性时间序列分解
  8. '''
  9. statsmodels.api.tsa.seasonal_decompose(x, freq=None)
  10. x:要分解的季节性时间序列
  11. freq:时间序列的周期,需要自己根据业务来判断数据的周期
  12. '''
  13. tsr = statsmodels.api.tsa.seasonal_decompose(
  14. total_sales, period=7
  15. )
  16. # 获取趋势部分
  17. trend = tsr.trend
  18. # 获取季节性部分
  19. seasonal = tsr.seasonal
  20. # 获取随机误差部分
  21. random_error = tsr.resid
  22. # 生成2*2的子图
  23. fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
  24. ax1.set_title('总销量')
  25. ax1.plot(
  26. data.index, total_sales, 'k-'
  27. )
  28. ax2.set_title('趋势部分')
  29. ax2.plot(
  30. data.index, trend, 'g-'
  31. )
  32. ax3.set_title('季节性部分')
  33. ax3.plot(
  34. data.index, seasonal, 'r-'
  35. )
  36. ax4.set_title('随机误差')
  37. ax4.plot(
  38. data.index, random_error, 'b-'
  39. )
  40. plt.show()

image.png

二、序列预测

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

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

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

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

差分(Integrated)

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

单位根检验法

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

  1. import pandas as pd
  2. import matplotlib.pyplot as plt
  3. # 单位根检验法
  4. import statsmodels.tsa.stattools as ts
  5. plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
  6. '''
  7. 一个平稳的时间序列指的是,未来的时间序列,它的均值、方差和协方差必定与现在的时间序列相等。
  8. 若时间序列不平稳,则需要通过差分等技术手段,把它转换成平稳的时间序列,然后再进行预测。
  9. '''
  10. data = pd.read_csv('data/时间序列预测.csv', encoding='utf8', engine='python')
  11. # 设置索引为时间格式
  12. data.index = pd.to_datetime(data.date, format='%Y%m%d')
  13. # 删除date列,已经保存到索引中了
  14. del data['date']
  15. plt.figure()
  16. plt.plot(data, 'r')
  17. plt.show()
  18. # 使用单位根检验法检验是否是平稳的时间序列
  19. # 封装一个方法,方便解读adfuller函数的结果
  20. def tagADF(t):
  21. result = pd.DataFrame(index=[
  22. "Test Statistic Value",
  23. "p-value", "Lags Used",
  24. "Number of Observations Used",
  25. "Critical Value(1%)",
  26. "Critical Value(5%)",
  27. "Critical Value(10%)"
  28. ], columns=['value']
  29. )
  30. result['value']['Test Statistic Value'] = t[0]
  31. result['value']['p-value'] = t[1]
  32. result['value']['Lags Used'] = t[2]
  33. result['value']['Number of Observations Used'] = t[3]
  34. result['value']['Critical Value(1%)'] = t[4]['1%']
  35. result['value']['Critical Value(5%)'] = t[4]['5%']
  36. result['value']['Critical Value(10%)'] = t[4]['10%']
  37. return result
  38. # 使用ADF单位根检验法,检验时间序列的稳定性
  39. adf_data = ts.adfuller(data.value)
  40. # 解读ADF单位根检验结果
  41. adfResult = tagADF(adf_data)
  42. print(adfResult)
  43. # value
  44. # Test Statistic Value -1.16364
  45. # p-value 0.689038
  46. # Lags Used 12
  47. # Number of Observations Used 77
  48. # Critical Value(1%) -3.518281
  49. # Critical Value(5%) -2.899878
  50. # Critical Value(10%) -2.587223
  51. # 68%的概率不是一个平稳的时间序列,故通过差分转换成平稳的时间序列
  52. # 对数据进行差分,删除第一个位置的空值
  53. diff = data.value.diff(1).dropna()
  54. plt.figure()
  55. plt.plot(diff, 'r')
  56. plt.show()
  57. # 使用ADF单位根检验法,检验时间序列的稳定性
  58. adf_diff = ts.adfuller(diff)
  59. # 解读ADF单位根检验结果
  60. adfResult = tagADF(adf_diff)
  61. print(adfResult)
  62. # value
  63. # Test Statistic Value -4.993859
  64. # p-value 0.000023
  65. # Lags Used 12
  66. # Number of Observations Used 76
  67. # Critical Value(1%) -3.519481
  68. # Critical Value(5%) -2.900395
  69. # Critical Value(10%) -2.587498
  70. # 0.000023<0.05,认为差分后的时间序列是平稳的时间序列

image.pngimage.png

2.2 自回归模型-AR

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

2.3 移动平均模型-MA

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

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

ARMA模型就是AR模型和MA模型的组合,将因变量仅对它的滞后值以及随机误差项的现值和滞后值进行回归所建立的模型。
image.pngimage.png

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

  1. import statsmodels.api as sm
  2. import pandas as pd
  3. import matplotlib.pyplot as plt
  4. import warnings
  5. warnings.filterwarnings("ignore")
  6. plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
  7. data = pd.read_csv('data/时间序列预测.csv', encoding='utf8', engine='python')
  8. # 设置索引为时间格式
  9. data.index = pd.to_datetime(data.date, format='%Y%m%d')
  10. # 删除date列,已经保存到索引中了
  11. del data['date']
  12. diff = data.value.diff(1).dropna()
  13. plt.figure()
  14. plt.plot(diff, 'r')
  15. plt.show()
  16. def ar_demo():
  17. '''
  18. 自回归
  19. :return:
  20. '''
  21. # 使用 AR 建模
  22. arModel = sm.tsa.AR(
  23. diff
  24. )
  25. # 使用 AR 模型的 select_order 方法
  26. # 自动根据 AIC 指标,选择最优的 p 值
  27. p = arModel.select_order(
  28. maxlag=30,
  29. ic='aic'
  30. )
  31. print(p)
  32. # 使用最优的 p 值进行建模
  33. arModel = arModel.fit(maxlag=p)
  34. # 对时间序列模型进行评估
  35. delta = arModel.fittedvalues - diff
  36. score = 1 - delta.var() / diff.var()
  37. print(score)
  38. def sma_demo():
  39. '''
  40. 简单移动平均
  41. :return:
  42. '''
  43. sma_data = diff.rolling(3).mean()
  44. plt.plot(
  45. diff.index, diff, 'k-',
  46. diff.index, sma_data, 'g-'
  47. )
  48. plt.show()
  49. def arma_demo():
  50. # ARMA模型,使用AIC指标
  51. # AR模型从1-15中选择最优的p
  52. # MA模型从1-15中选择最优的q
  53. # 执行时间非常长,作者执行了10个小时左右
  54. # ic = sm.tsa.arma_order_select_ic(
  55. # diff,
  56. # max_ar=15,
  57. # max_ma=15,
  58. # ic='aic'
  59. # )
  60. # 选择最优参数
  61. order = (6, 6)
  62. armaModel = sm.tsa.ARMA(
  63. diff, order
  64. ).fit()
  65. # 评估模型得分
  66. delta = armaModel.fittedvalues - diff
  67. score = 1 - delta.var() / diff.var()
  68. print(score)
  69. # 预测接下来10天的值
  70. p = armaModel.predict(
  71. start='2016-03-31',
  72. end='2016-04-10'
  73. )
  74. # 封装一个对差分的数据进行还原的函数
  75. def revert(diffValues, *lastValue):
  76. for i in range(len(lastValue)):
  77. result = []
  78. lv = lastValue[i]
  79. for dv in diffValues:
  80. lv = dv + lv
  81. result.append(lv)
  82. diffValues = result
  83. return diffValues
  84. # 对差分的数据进行还原
  85. r = revert(p, 10395)
  86. plt.figure()
  87. plt.plot(
  88. diff, 'r',
  89. label='Raw'
  90. )
  91. plt.plot(
  92. armaModel.fittedvalues, 'g',
  93. label='ARMA Model'
  94. )
  95. plt.plot(
  96. p, 'b', label='ARMA Predict'
  97. )
  98. plt.legend()
  99. plt.show()
  100. r = pd.DataFrame({
  101. 'value': r
  102. }, index=p.index
  103. )
  104. plt.figure()
  105. plt.plot(
  106. data.value, c='r',
  107. label='Raw'
  108. )
  109. plt.plot(
  110. r, c='g',
  111. label='ARMA Model'
  112. )
  113. plt.legend()
  114. plt.show()
  115. if __name__ == '__main__':
  116. ar_demo()
  117. sma_demo()
  118. arma_demo()