文章目录

  • 1 引言
  • 2 时间序列的特性
    • 2.1 自相关
    • 2.2 季节性
    • 2.3 平稳性
  • 3 时间序列建模
    • 3.1 移动平均法
    • 3.2 指数平滑法
    • 3.3 双指数平滑法
    • 3.4 三重指数平滑法
    • 3.5 周期性差分自动平滑回归模型(SARIMA)
  • 4 实例——股票价格的预测
  • 5 结论

1 引言

本篇主要帮助大家理解移动平均,指数平滑,平稳性,自相关,SARIMA,通过案例和Python编程实现时间序列的预测技术。

无论我们是预测金融市场或股票趋势,或是电能耗费的趋势,时间都是我们模型中必须考虑的一个重要因素。例如,预测一天中什么时间会出现的电能消耗高峰,并以此来调整价格或发电量。

时间序列简单来说就是按时间顺序排列的数据点。在时间序列中,时间通常是独立变量,目标则是对未来进行预测。

当然,在处理时间序列时,其他许多因素同样会有影响。

  • 时间序列是平稳的吗?
  • 时间序列具有季节性吗?
  • 目标变量是自相关的吗?

在本文中,我们将介绍时间序列的不同特征,以及如何进行建模以获得尽可能准确的预测。当然,预测未来是十分困难的。

2 时间序列的特性

2.1 自相关

简单来说,自相关是观察值之间的相似性,并作为它们之间是时间延迟的函数。

上图就是一个自相关的图例。仔细观察,我们发现第1个值和第24个值具有很高的自相关性。同样,第12 个观测和第36个观测也是高度相关的。这意味着,每24个单位时间,我们就可以找到一个相似的值。

注意,这个图与正弦函数很相似,这就是对序列季节性的暗示,在上图中,我们可以按照24个单位时间一个周期,找到相似的值。

2.2 季节性

季节性指的是周期性的波动。例如,白天用电量高,晚上低,或是网购量在圣诞节期间增加,然后再次放缓。

正如上图所示,每一天都有明显的季节性趋势。每一天,傍晚时都会出现一个高峰,而最低点则是每天的开始和结束。
如果自相关图像是正弦的,那么也可以认为其具有季节性。只要简单的看一下它的周期,就可以得到季节的长度。

2.3 平稳性

平稳性是时间序列的一个重要特征。如果一个时间序列的统计特征不随时间而改变,那么他就是平稳的。换句话说,它的均值和方差是常数,并且协方差与时间无关

同一张图,可以看到上面的过程是平稳的。均值和方差不随时间变化。

通常情况下,股票价格不是一个平稳过程,因为我们可能会看到增长的趋势,或者它的波动性会随着时间而增加(方差的变化)。

理想情况下,我们希望对一个平稳的时间序列建模。当然,并不是所有的时间序列都是平稳的,但我们可以通过不同的变换使它们变为平稳序列。

如何检验一个过程是否平稳

你可能已经注意到,上图标题中的迪基-福勒Dickey-Fuller。迪基-福勒检验就是用来在统计学领域检验时间序列是否平稳的。

不讨论迪基-福勒检验的技术细节,简单来说,它检验了原假设:单位根存在。

  • 如果为真,则p>0,这个过程是不平稳的。
  • 否则,p=0, 拒绝原假设,过程被认为是平稳的。

以下图为例,该过程是不平稳的,平均值随时间变化,不为常数。

3 时间序列建模

有许多方法可以对时间序列进行建模,并作出预测。本文,我们将介绍:

  • 移动平均法
  • 指数平滑法
  • ARIMA

3.1 移动平均法

移动平均模型可能是所有时间序列模型中最初级的方法:下一个观测值是之前所有观测值的平均值。尽管十分简单,但这个模型却出人意料的有效,也是一个很好的起点。

移动平均模型可以用于识别数据中有趣的趋势。我们可以定义一个窗口,应用移动平均模型对时间序列进行平滑处理,以突出不同的趋势。

在上图中,我们将移动平均应用在一个窗口为24小时的模型。图中绿线是时间序列的平滑,我们可以看到在24小时内存在两个峰值。

当然,时间窗口越长,趋势就越平稳。下图是一个小窗口的移动平均模型的案例。

3.2 指数平滑法

指数平滑法使用与移动平均法相似的逻辑,但在此方法中,将赋予每个观测值不同的递减权重。换句话说,随着观察值离现在的时间点越来越远,观察值的重要性就越来越小。

在数学上,指数平滑表示为:

α是一个平滑因子,值在0到1之间。它决定了观测值权重下降的速度。

从上图可以看出:

  • 深蓝线表示使用0.3平滑因子对时间序列进行指数平滑的结果;
  • 橙线则是使用0.05的指数平滑结果。
  • 正如我们看到的,平滑因子越小,结果就越平滑。这很容易理解,因为当平滑因子接近0时,模型就越接近移动平均模型。

3.3 双指数平滑法

当时间序列中存在趋势时,我们可以采用双指数平滑法。这种方法简单来说,是指数平滑法的递归使用

数学上:

上式中,β是趋势平滑因子,值在0-1之间。

预测公式为:

下图中,我们可以看到不同值如何影响处理后时间序列的形状。

3.4 三重指数平滑法

此方法通过增加季节平滑因子,扩展了双指数平滑法。因此,当你发现时间序列具有季节性时,此方法就十分有效。

数学上:

其中 γ 是季节平滑因子,L是季节的长度。

3.5 周期性差分自动平滑回归模型(SARIMA)

SARIMA模型实际上是一些简单模型的组合,并组成复杂的模型,此模型可以对非平稳的具有季节性的时间序列进行建模。

我们先来看自回归模型AR§,它可以说是对时间序列自身的回归。我们假设当前值依赖于有些许滞后的先前值,并使用参数p表示最大滞后值。为了找到它,我们可以观察偏自相关图像,找到滞后值开始不显著的点,即最大滞后值。

下图的例子中,该p值为4。

接下来,我们加入移动平均模型MA(q)。参数q代表最大滞后值,在此后的其他滞后在相关性图中不显著。下图中,q值为4。

之后,我们再加入单整阶数I(d)。参数d表示是序列平稳所需进行的差分次数(阶数)。

现在,让我们添加最后一个组成部分:季节性s(P, D, Q, s),其中s为季节的长度,参数P和Q是与p和q相似的季节性部分。最后,D是季节性单整阶数,代表从序列中去除季节性所需的差分次数。

综合这些,我们得到了SARIMA(p, d, q)(P,D, Q, s)模型。

主要结论:在使用SARIMA建模前,我们必须对时间序列进行变换,以消除季节性,并使序列平稳。

这一大堆理论可能让我们难以理解,下面我们将在项目中应用上面所讨论的技术,
我们将预测某一公司的股票价格。当然,预测股票价格在现在是几乎不可能的,但是这仍然是个有趣的联系,也是一个很好实践所学的方式。

4 实例——股票价格的预测

我们利用新德国基金(GF)的历史股价来预测未来五个交易日的收盘价。
我们可以在https://github.com/marcopeix/stock-prediction找到数据集和笔记本。

启动您的编辑器,让我们开始吧。

(1)导入数据

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()from sklearn.metrics import r2_score, median_absolute_error, mean_absolute_error
from sklearn.metrics import median_absolute_error, mean_squared_error, mean_squared_log_errorfrom scipy.optimize import minimize
import statsmodels.tsa.api as smt
import statsmodels.api as smfrom tqdm import tqdm_notebookfrom itertools import productdef mean_absolute_percentage_error(y_true, y_pred):return np.mean(np.abs((y_true - y_pred) / y_true)) * 100import warnings
warnings.filterwarnings('ignore')%matplotlib inlineDATAPATH = 'data/stock_prices_sample.csv'data = pd.read_csv(DATAPATH, index_col=['DATE'], parse_dates=['DATE'])
data.head(10)

首先,我们导入一些在分析过程中可能会用到的Python包。此外,定义了平均百分比误差(MAPE),这将是误差的评判标准。之后,我们导入数据集并预览了前十条数据:

我们可以发现,有一些条目并不属于新德国基金,并且有些条目为交易日内信息,并非我们所需的收盘信息。因此我们需要对数据进行一定的清理。

(2)数据清理
我们首先删除不符合要求的条目,然后删除不需要的列,只留下我们关注的股票收盘价。

data = data[data.TICKER != 'GEF']
data = data[data.TYPE != 'Intraday']drop_cols = ['SPLIT_RATIO', 'EX_DIVIDEND', 'ADJ_FACTOR', 'ADJ_VOLUME', 'ADJ_CLOSE', 'ADJ_LOW', 'ADJ_HIGH', 'ADJ_OPEN', 'VOLUME', 'FREQUENCY', 'TYPE', 'FIGI']data.drop(drop_cols, axis=1, inplace=True)data.head()

预览处理后的数据集,之后我们就可以进行下一步的探索性数据分析了。

(3)探索性数据分析(EDA)

# Plot closing priceplt.figure(figsize=(17, 8))
plt.plot(data.CLOSE)
plt.title('Closing price of New Germany Fund Inc (GF)')
plt.ylabel('Closing price ($)')
plt.xlabel('Trading day')
plt.grid(False)
plt.show()

我们绘制了数据集中,所有时间段内的收盘价,可以得到:

很明显,这不是一个平稳的时间序列,也很难判断它是否具有季节性。

(4)移动平均

我们使用移动平均模型来使我们的时间序列平滑化。为此,我们会使用一个辅助函数,他会在指定的时间窗运行移动平均模型,并绘制结果得到的平滑曲线:

def plot_moving_average(series, window, plot_intervals=False, scale=1.96):rolling_mean = series.rolling(window=window).mean()plt.figure(figsize=(17,8))plt.title('Moving average\n window size = {}'.format(window))plt.plot(rolling_mean, 'g', label='Rolling mean trend')#Plot confidence intervals for smoothed valuesif plot_intervals:mae = mean_absolute_error(series[window:], rolling_mean[window:])deviation = np.std(series[window:] - rolling_mean[window:])lower_bound = rolling_mean - (mae + scale * deviation)upper_bound = rolling_mean + (mae + scale * deviation)plt.plot(upper_bound, 'r--', label='Upper bound / Lower bound')plt.plot(lower_bound, 'r--')plt.plot(series[window:], label='Actual values')plt.legend(loc='best')plt.grid(True)#Smooth by the previous 5 days (by week)
plot_moving_average(data.CLOSE, 5)#Smooth by the previous month (30 days)
plot_moving_average(data.CLOSE, 30)#Smooth by previous quarter (90 days)
plot_moving_average(data.CLOSE, 90, plot_intervals=True)

使用时间窗为5天时,我们可以得到:

可以看到,时间窗为5时,它太接近实际曲线了,我们几乎看不到任何趋势。那么,使用前一个月或前一个季度时间窗的平滑结果是怎样的呢?


这时候,我们可以看出,这两种时间窗所得到的平滑结果,在结束时是呈下降趋势的。这可能意味着股价在未来几天有可能下跌。

(5)指数平滑法

现在,我们使用指数平滑法来看看它是否会得到更好的趋势。

def exponential_smoothing(series, alpha):result = [series[0]]    #first value is same as seriesfor n in range(1, len(series)):result.append(alpha * series[n] + (1 - alpha) * result[n-1])return resultdef plot_exponential_smoothing(series, alphas):plt.figure(figsize=(17, 8))for alpha in alphas:plt.plot(exponential_smoothing(series, alpha), label = 'Alpha {}'.format(alpha))plt.plot(series.values, "c", label="Actual")plt.legend(loc="best")plt.axis('tight')plt.title("Exponential Smoothing")plt.grid(True)plot_exponential_smoothing(data['CLOSE'], [0.05, 0.3])
plt.show()

我们使用0.05和0.3作为平滑因子的值。当然,也可以尝试其他值,看看结果如何。

(6)Holt指数平滑法

def double_exponential_smoothing(series, alpha, beta):result = [series[0]]for n in range(1, len(series)+1):if n == 1:level, trend = series[0], series[1] - series[0]if n >= len(series): # forecastingvalue = result[-1]else:value = series[n]last_level, level = level, alpha * value + (1 - alpha) * (level + trend)trend = beta * (level - last_level) + (1 - beta) * trendresult.append(level + trend)return resultdef plot_double_exponential_smoothing(series, alphas, betas):plt.figure(figsize=(17, 8))for alpha in alphas:for beta in betas:plt.plot(double_exponential_smoothing(series, alpha, beta), label="Alpha {}, beta {}".format(alpha, beta))plt.plot(series.values, label = "Actual")plt.legend(loc="best")plt.axis('tight')plt.title("Double Exponential Smoothing")plt.grid(True)plot_double_exponential_smoothing(data.CLOSE, alphas=[0.9, 0.02], betas=[0.9, 0.02])

我们可以得到:

同样的,我们也尝试了不同的α和β组合,以获取最好的平滑曲线。

(7)建模

正如本文之前所说,为了对时间序列进行建模,必须将它处理为一个平稳过程。因此,我们使用Dickey-Fuller测试来检验它是否为平稳过程。

def tsplot(y, lags = None, figsize=(12,7), style='bmh'):if not isinstance(y, pd.Series):y = pd.Series(y)with plt.style.context(style='bmh'):fig = plt.figure(figsize=figsize)layout = (2,2)ts_ax = plt.subplot2grid(layout, (0,0), colspan=2)acf_ax = plt.subplot2grid(layout, (1,0), colspan=1)pacf_ax = plt.subplot2grid(layout, (1,1), colspan=1)y.plot(ax=ts_ax)p_value = sm.tsa.stattools.adfuller(y)[1]ts_ax.set_title('Time Series Analysis Plots\n Dickey-Fuller: p={0:.5f}'.format(p_value))smt.graphics.plot_acf(y, lags=lags, ax = acf_ax)smt.graphics.plot_pacf(y, lags=lags, ax = pacf_ax)plt.tight_layout()tsplot(data['CLOSE'], lags=30)
plt.show()

得到以下图像:

通过Dicky-Fuller检验,p值大于0.05,时间序列明显为非平稳的。此外,相关图中并未显示出明显的季节性。

因此,为了消除高自相关性并使过程变为平稳,我们做一次差分,即简单的从时间序列本身减去一天的滞后:

# Take the first difference to remove to make the process stationary
data_diff = data['CLOSE'] - data['CLOSE'].shift(1)tsplot(data_diff[1:], lags=30)
plt.show()


很明显,现在的时间序列是平稳的,我们就可以对它进行建模。

(8)SARIMA

# Set initial values and some bounds
ps = range(0, 5)
d = 1
qs = range(0, 5)
Ps = range(0, 5)
D = 1
Qs = range(0, 5)
s = 5# Create a list with all possible combinations of parameters
parameters = product(ps, qs, Ps, Qs)
parameters_list = list(parameters)
print(len(parameters_list))# Train many SARIMA models to find the best set of parameters
def optimize_SARIMA(parameters_list, d, D, s):"""Return dataframe with parameters and corresponding AICparameters_list - list with (p, q, P, Q) tuplesd - integration orderD - seasonal integration orders - length of season"""results = []best_aic = float('inf')for param in parameters_list:try:model = sm.tsa.statespace.SARIMAX(data.CLOSE, order=(param[0], d, param[1]),seasonal_order=(param[2], D, param[3], s)).fit(disp=-1)except:continueaic = model.aicresults.append([param, aic])result_table = pd.DataFrame(results)result_table.columns = ['parameters', 'aic']# Sort in ascending order, lower AIC is betterresult_table = result_table.sort_values(by='aic', ascending=True).reset_index(drop=True)return result_tableresult_table = optimize_SARIMA(parameters_list, d, D, s)# Set parameters that give the lowest AIC (Akaike Information Criteria)
p, q, P, Q = result_table.parameters[0]best_model = sm.tsa.statespace.SARIMAX(data.CLOSE, order=(p, d, q),seasonal_order=(P, D, Q, s)).fit(disp=-1)print(best_model.summary())

对于SARIMA来说,需要首先定义一些参数及值的范围,以生成一个包好p, q, d, P, Q, D, s所有可能的组合列表。

在上面的代码中,我们得到了625种不同组合。通过尝试用每个组合训练SARIMA,涨到性能最佳的模型。因为组合较多,训练时间将取决于您计算机的处理能力,或许需要一段时间。

训练完成后,打印出最佳模型:

运用这个最佳模型对未来五个交易日的收盘价进行预测,得到MAPE为0.79%,可以认为预测十分贴近真实。

(9)将预测的价格与实际数据进行比较

为了将预测与实际数据进行比较,我们从雅虎财经获取了实际数据并创建了一个数据模型。

# Make a dataframe containing actual and predicted prices
comparison = pd.DataFrame({'actual': [18.93, 19.23, 19.08, 19.17, 19.11, 19.12],'predicted': [18.96, 18.97, 18.96, 18.92, 18.94, 18.92]}, index = pd.date_range(start='2018-06-05', periods=6,))#Plot predicted vs actual priceplt.figure(figsize=(17, 8))
plt.plot(comparison.actual)
plt.plot(comparison.predicted)
plt.title('Predicted closing price of New Germany Fund Inc (GF)')
plt.ylabel('Closing price ($)')
plt.xlabel('Trading day')
plt.legend(loc='best')
plt.grid(False)
plt.show()

制作一张图表,展示预测与实际收盘价的差距:

这样看来,我们的预测有不少偏差。实际上,预测得到的价格基本为水平的,也意味着模型可能并不有效。

当然,这不一定源于模型的问题,同样,股票价格的预测基本是不可能的。

5 结论

这篇文章很长,但是内容丰富,我们学习了运用SARIMA进行时间序列分析,以及使时间序列平稳的整个过程。

这是一个冗长乏味的过程,需要大量的手工的调整。希望这篇文章对你有用,在以后遇到类似问题时,可以再次想到并进行参考。

讨论
考虑到我们的预测结果并不尽如人意,其可能原因有许多,但是我们可以想到,或许简单模型或传统统计模型,已经不能对如今复杂的数据进行有效预测。
可以进一步思考是否可以通过其他模型,使我们对股票的预测更加准确?

本文主要参考于:
知识分享 | 时间序列分析和预测不完全指南(沈浩老师)

相关笔记:

  1. Python相关实用技巧01:安装Python库超实用方法,轻松告别失败!
  2. Python相关实用技巧02:Python2和Python3的区别
  3. Python相关实用技巧03:14个对数据科学最有用的Python库
  4. Python相关实用技巧04:网络爬虫之Scrapy框架及案例分析
  5. Python相关实用技巧05:yield关键字的使用
  6. Scrapy爬虫小技巧01:轻松获取cookies
  7. Scrapy爬虫小技巧02:HTTP status code is not handled or not allowed的解决方法
  8. 数据分析学习总结笔记01:情感分析
  9. 数据分析学习总结笔记02:聚类分析及其R语言实现
  10. 数据分析学习总结笔记03:数据降维经典方法
  11. 数据分析学习总结笔记04:异常值处理
  12. 数据分析学习总结笔记05:缺失值分析及处理
  13. 数据分析学习总结笔记06:T检验的原理和步骤
  14. 数据分析学习总结笔记07:方差分析
  15. 数据分析学习总结笔记07:回归分析概述
  16. 数据分析学习总结笔记08:数据分类典型方法及其R语言实现
  17. 数据分析学习总结笔记09:文本分析
  18. 数据分析学习总结笔记10:网络分析
  19. 数据分析学习总结笔记11:空间复杂度和时间复杂度
  20. 数据分析学习总结笔记12:空间自相关——空间位置与相近位置的指标测度
  21. 数据分析学习总结笔记13:生存分析及Python实现
  22. 数据分析学习总结笔记14:A/B Test及Python实现

数据分析学习总结笔记15:时间序列分析及Python实现相关推荐

  1. 数据分析学习总结笔记16:NLP自然语言处理与文本探索性分析

    文章目录 1 引言 2 数据集 3 文本统计信息分析 4 Ngram模型探索 5 基于pyLDAvis的主题模型探索 6 绘制词云图 7 情感分析 7.1 TextBlob 7.2 Vader Sen ...

  2. 数据分析学习总结笔记17:文本分析入门案例实战

    文章目录 1 数据准备 2 分词 3 统计词频 4 词云 5 提取特征 6 用sklearn进行训练 1 数据准备 数据样例如下, 数据总量为7.7万+: 本节通过一个实战的例子来展示文本分析的最简单 ...

  3. 数据分析学习总结笔记10:网络分析

    数据分析学习总结笔记10:网络分析 1 网络分析概述 1.1 三大社会科学理论 1.2 网络分析内容 2 网络的基本概念与特征量 2.1 网络的发展 2.2 网络的表达形式 2.3 网络基本概念与特征 ...

  4. 数据分析学习总结笔记05:缺失值分析及处理

    数据分析学习总结笔记05:缺失值分析及处理 1 缺失值概念 2 缺失值分析的类别 2.1 按数据缺失形式划分 2.2 按缺失机制与方式划分 3 缺失值的处理方法 3.1 删除缺失值 3.2 缺失值替代 ...

  5. 数据分析学习总结笔记03:数据降维经典方法

    数据分析学习总结笔记03:数据降维经典方法 1. 数据降维概述 2. 数据降维的应用 3. 数据降维经典方法 3.1 主成分分析(PCA) 3.1.1 PCA概述 3.1.2 PCA原理 3.1.3 ...

  6. 《统计学》学习笔记之时间序列分析和预测

    鄙人学习笔记 文章目录 时间序列分析和预测 时间序列及其分解 时间序列的描述性分析 时间序列预测的程序 确定时间序列成分 选择预测方法 预测方法的评估 平稳序列的预测 简单平均法 移动平均法 指数平滑 ...

  7. 浅尝辄止_数学建模(笔记_时间序列分析及其SPSS实现)

    本文多是广泛的概念和SPSS运用,没有具体的推导过程和深入的探究 文章目录 一.时间序列分析 1.具体步骤: 二.基本知识 1.时间序列数据 2.时间序列的基本概念 3.时间序列分解 4.叠加模型和乘 ...

  8. 29Python时间序列分析(美国消费者信心指数及维基百科点击量EDA,含实例数据)

    唐宇迪<python数据分析与机器学习实战>学习笔记 29Python时间序列分析 一.pandas生成时间序列 常见的时间序列:时间戳(timestamp):具体时间点2020.4.6的 ...

  9. 独家 | Python时间序列分析:一项基于案例的全面指南

    作者: Selva Prabhakaran 翻译:陈超校对:王可汗本文约7500字,建议阅读20+分钟本文介绍了时间序列的定义.特征并结合实例给出了时间序列在Python中评价指标和方法. 时间序列是 ...

最新文章

  1. 初学js----------一些API
  2. OpenCV与gcc和CMake一起使用
  3. 信安精品课:第7章访问控制技术原理与应用精讲笔记
  4. 商汤科技20篇论文入选ICCV 2017,披露最新研究主线
  5. aes js 加盐值 解密_cryptoJS AES 加解密简单使用
  6. 【Fltk】Fltk1.3.3+VS2015 编程
  7. 计算机键盘快速指南,菜鸟必看 Windows键盘快捷键入门指南
  8. win10没有realtek高清晰音频管理器_Win10如何让电脑睡眠不断网?电脑睡眠状态不断网继续下载的方法...
  9. 春季校园招聘简历投递量已超去年同期;亚太房地产市场现逢低买入良机 | 美通企业日报...
  10. 模拟信号的调制与解调
  11. SwiftUI 教程之应用中实现 Core Spotlight搜索(教程含源码)
  12. 算法设计与分析第七章习题解答与学习指导(第2版)屈婉婷 刘田 张立昂 王捍贫编著 清华大学出版社
  13. Redis大数据应用场景
  14. 14. vue的插槽
  15. 一个苦逼程序员日常的 10 个扎心瞬间
  16. Shell read命令详解
  17. 在3G中实现LBS的定位技术和GIS系统
  18. 最近的热门:渣打小三事件
  19. Word文档的锁定与解锁
  20. Ubuntu 符号连接

热门文章

  1. 为你解析机器学习品酒步骤(附视频)
  2. “狗屁不通文章生成器”火爆 Github,一句话生成万字论文的神器了解一下?
  3. angelica 下载_Sara Angelica
  4. jquery插件备忘录
  5. Luzj's Zte 中兴认证客户端开放源码
  6. 上周技术关注:函数式编程另类指南
  7. PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?
  8. nVisual通信网络资源管理
  9. 60帧/秒摄像头 视频帧数最佳选择!
  10. 基于微信小程序的美食点餐推荐系统的设计与实现+源码