数据分析学习总结笔记15:时间序列分析及Python实现
文章目录
- 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进行时间序列分析,以及使时间序列平稳的整个过程。
这是一个冗长乏味的过程,需要大量的手工的调整。希望这篇文章对你有用,在以后遇到类似问题时,可以再次想到并进行参考。
讨论
考虑到我们的预测结果并不尽如人意,其可能原因有许多,但是我们可以想到,或许简单模型或传统统计模型,已经不能对如今复杂的数据进行有效预测。
可以进一步思考是否可以通过其他模型,使我们对股票的预测更加准确?
本文主要参考于:
知识分享 | 时间序列分析和预测不完全指南(沈浩老师)
相关笔记:
- Python相关实用技巧01:安装Python库超实用方法,轻松告别失败!
- Python相关实用技巧02:Python2和Python3的区别
- Python相关实用技巧03:14个对数据科学最有用的Python库
- Python相关实用技巧04:网络爬虫之Scrapy框架及案例分析
- Python相关实用技巧05:yield关键字的使用
- Scrapy爬虫小技巧01:轻松获取cookies
- Scrapy爬虫小技巧02:HTTP status code is not handled or not allowed的解决方法
- 数据分析学习总结笔记01:情感分析
- 数据分析学习总结笔记02:聚类分析及其R语言实现
- 数据分析学习总结笔记03:数据降维经典方法
- 数据分析学习总结笔记04:异常值处理
- 数据分析学习总结笔记05:缺失值分析及处理
- 数据分析学习总结笔记06:T检验的原理和步骤
- 数据分析学习总结笔记07:方差分析
- 数据分析学习总结笔记07:回归分析概述
- 数据分析学习总结笔记08:数据分类典型方法及其R语言实现
- 数据分析学习总结笔记09:文本分析
- 数据分析学习总结笔记10:网络分析
- 数据分析学习总结笔记11:空间复杂度和时间复杂度
- 数据分析学习总结笔记12:空间自相关——空间位置与相近位置的指标测度
- 数据分析学习总结笔记13:生存分析及Python实现
- 数据分析学习总结笔记14:A/B Test及Python实现
数据分析学习总结笔记15:时间序列分析及Python实现相关推荐
- 数据分析学习总结笔记16:NLP自然语言处理与文本探索性分析
文章目录 1 引言 2 数据集 3 文本统计信息分析 4 Ngram模型探索 5 基于pyLDAvis的主题模型探索 6 绘制词云图 7 情感分析 7.1 TextBlob 7.2 Vader Sen ...
- 数据分析学习总结笔记17:文本分析入门案例实战
文章目录 1 数据准备 2 分词 3 统计词频 4 词云 5 提取特征 6 用sklearn进行训练 1 数据准备 数据样例如下, 数据总量为7.7万+: 本节通过一个实战的例子来展示文本分析的最简单 ...
- 数据分析学习总结笔记10:网络分析
数据分析学习总结笔记10:网络分析 1 网络分析概述 1.1 三大社会科学理论 1.2 网络分析内容 2 网络的基本概念与特征量 2.1 网络的发展 2.2 网络的表达形式 2.3 网络基本概念与特征 ...
- 数据分析学习总结笔记05:缺失值分析及处理
数据分析学习总结笔记05:缺失值分析及处理 1 缺失值概念 2 缺失值分析的类别 2.1 按数据缺失形式划分 2.2 按缺失机制与方式划分 3 缺失值的处理方法 3.1 删除缺失值 3.2 缺失值替代 ...
- 数据分析学习总结笔记03:数据降维经典方法
数据分析学习总结笔记03:数据降维经典方法 1. 数据降维概述 2. 数据降维的应用 3. 数据降维经典方法 3.1 主成分分析(PCA) 3.1.1 PCA概述 3.1.2 PCA原理 3.1.3 ...
- 《统计学》学习笔记之时间序列分析和预测
鄙人学习笔记 文章目录 时间序列分析和预测 时间序列及其分解 时间序列的描述性分析 时间序列预测的程序 确定时间序列成分 选择预测方法 预测方法的评估 平稳序列的预测 简单平均法 移动平均法 指数平滑 ...
- 浅尝辄止_数学建模(笔记_时间序列分析及其SPSS实现)
本文多是广泛的概念和SPSS运用,没有具体的推导过程和深入的探究 文章目录 一.时间序列分析 1.具体步骤: 二.基本知识 1.时间序列数据 2.时间序列的基本概念 3.时间序列分解 4.叠加模型和乘 ...
- 29Python时间序列分析(美国消费者信心指数及维基百科点击量EDA,含实例数据)
唐宇迪<python数据分析与机器学习实战>学习笔记 29Python时间序列分析 一.pandas生成时间序列 常见的时间序列:时间戳(timestamp):具体时间点2020.4.6的 ...
- 独家 | Python时间序列分析:一项基于案例的全面指南
作者: Selva Prabhakaran 翻译:陈超校对:王可汗本文约7500字,建议阅读20+分钟本文介绍了时间序列的定义.特征并结合实例给出了时间序列在Python中评价指标和方法. 时间序列是 ...
最新文章
- 初学js----------一些API
- OpenCV与gcc和CMake一起使用
- 信安精品课:第7章访问控制技术原理与应用精讲笔记
- 商汤科技20篇论文入选ICCV 2017,披露最新研究主线
- aes js 加盐值 解密_cryptoJS AES 加解密简单使用
- 【Fltk】Fltk1.3.3+VS2015 编程
- 计算机键盘快速指南,菜鸟必看 Windows键盘快捷键入门指南
- win10没有realtek高清晰音频管理器_Win10如何让电脑睡眠不断网?电脑睡眠状态不断网继续下载的方法...
- 春季校园招聘简历投递量已超去年同期;亚太房地产市场现逢低买入良机 | 美通企业日报...
- 模拟信号的调制与解调
- SwiftUI 教程之应用中实现 Core Spotlight搜索(教程含源码)
- 算法设计与分析第七章习题解答与学习指导(第2版)屈婉婷 刘田 张立昂 王捍贫编著 清华大学出版社
- Redis大数据应用场景
- 14. vue的插槽
- 一个苦逼程序员日常的 10 个扎心瞬间
- Shell read命令详解
- 在3G中实现LBS的定位技术和GIS系统
- 最近的热门:渣打小三事件
- Word文档的锁定与解锁
- Ubuntu 符号连接