Time is relative. Your body hasn't even hit the floor yet. I've spent so many years...peering through time...looking at this exact moment. But I can't see past it.
简介:(新手向)
时间序列模型在生活中的各个部分都参了一脚,从股票走势到商品销售额的预测,都无法离开时间序列模型,于是,在参加完厦门大学时间序列模型预测课程的大作业后,写下一些帮助时间序列模型建设的blog,谨此纪念第一次正式的建模。
本文章不注重于深究模型是如何建立,更加注重于模型的实战运用与分析
如下为训练集,所有测试都放在kaggle上,所以暂无测试集数据,可以通过sklearn的timeseriessplit进行交叉验证观察模型的拟合效果,需要预测未来三天的走势
链接:https://pan.baidu.com/s/1wR7-w3vGKv03FzQPR3KqhA?pwd=Data
提取码:Data
任务与如何建模
任务:建立一个时间序列模型并预测接下来三天的数据
听起来是不是非常简单的样子?只要建立模型,再根据模型把接下来的三天都预测下来,这不就结束了嘛,甚至我自己大概看看数据的走向都能做成这样的事~
当然,这只是表层的,使用读者自己的神经网络实现的模型预测
但是当数据量很大的时候自己的神经网络够用去计算这些所有的值?显然会先让自己的🧠过载力。于是乎,这种计算量很大的事情自然要交给计算机来算力
建模の步骤
那么又得如何交给计算机处理呢,这其中就大有门路力
1. 数据清洗,进行数据预处理
就像我们煮饭做菜一般,在拿到食材之后,要先看看食材新不新鲜,将坏掉的食材🚮。那么在我们的数据集中,数据预处理可以认为是对食材的初步处理,可以看土豆长不长芽来判断有没有毒,那么在数据集中就是判断有没有NaN从数据集中长了出来。
如果NaN长出来了怎么办呢,这里的解决方法可以是,插值法,删除法
插值法:根据缺失数据的时间节点往前推,取某个区间的平均值或者插入上一个值,例如:周三数据缺失,可以使用上周三数据填充,或者过去一个月的周三平均数据填充等。
删除法:头疼砍头的毫方法,根据实际数据需要决定是否这么做
感谢老师的手下留情,文中这份数据集已经经过数据清洗,请放心食用
数据预处理方面主要包括训练集验证集划分,按需要取就可以,可以自己敲截取,也可以使用函数。注意:时间序列模型无法通过sklearn的traintestsplit,只能使用timeseriessplit,因为要按照时间顺序截取,不能打乱
2. 特征工程
特征工程就犹如刀工,好的刀工在食材处理中起着不可或缺的作用
对于传统ARIMA,SARIMA则不需要特征处理
而在机器学习中,特征工程就非常非常滴重要,本篇文章也会在下面详细讲述特征工程
3. 建模
建模就是选择合适的模型进行预测,常用模型有
传统模型:AR(自回归),MA(移动平均),ARIMA(自回归差分移动平均),SARIMA(ARIMA + 季节性),简单线性回归
机器学习:LightGBM,XGBoost, 决策树,随机森林,LSTM(深度学习)
在建模过程中多尝试一些模型总是好的,多试试才知道最适合的模型,这里我使用xgboost进行建模
4. 预测
预测的方式有,逐步预测(单模型预测,预测到后期会导致残差叠加),直接预测(多模型直接预测,在后期的数据少,预测效果差)
建模の关键
机器学习建模的关键在于特征工程
特征工程
1.时间戳特征:
时间戳特征是所有时间序列的通用特征,通常可以调用接口实现,以下为代码实例,grid_df 可以认为是特征列表
# 提取时间戳对象属性,记住要想用时间接口,需要先把时间列转换为datetime类型
temp_dt = grid_df['timestamp'].dt # 这个dt是高级接口,可以通过temp_dt获得所有的时间戳特征,不过它只是接口,无法print
# 以下为统一处理,这里和上面的为预测值创建NaN一样,是字典添加元素的想法
grid_df['month'] = temp_dt.month # 月, int
grid_df['day'] = temp_dt.day # 日, int
grid_df['dayofweek'] = temp_dt.dayofweek # 星期几, int
grid_df['is_month_start'] = temp_dt.is_month_start # 是否是本月第一天, bool
grid_df['is_month_end'] = temp_dt.is_month_end # 是否是本月最后一天, bool
2.时变特征:
时变特征是时间序列的重要组成部分,本质上来说,时变特征是以过去的时间数据作为自己预测的依据
A:滚动窗口(data.shift().rolling(n), 防止数据泄露,不能取自己,取n天的滑动窗口)
滚动窗口是指一个窗口在数据集中滑动截取数据,如果有学过算法的小伙伴应该很熟悉一种滑动窗口,最小矩阵乘积(通过滑动窗口+动态规划确定乘积最小)。这里更简单一点,此时的窗口只需要沿着时间一条轴向后滑动就可以
通常一个窗口是一个小的时间序列,于是可以包括平均值,总和,标准差,最大最小值这几个时变特征
B:滞后项(data.shift(n), n为滞后的第几天)
滞后项通常是指将过去的时间点数据作为现在的特征,就像一个周期为7天的时间序列模型,在周三的时候就可以将上周三的数据作为我的一个预测特征(毕竟刚好过去一个周期)
滞后项可以取过去三天,或者某个特定时间的(例如只取过去第七天)
C:拓展窗口:(data.shift().expanding(n),思想同上的滑动窗口,不过expanding是一次将窗口延展n天)
拓展窗口和滚动窗口有相同的思想,可以认为是窗口的左侧(时间最小端)不进行移动,只是将窗口的右端不断向右边延申
一次延申的长度也有讲究,一般采用7天时间端会比较好,对窗口处理的方法和滚动窗口一摸一样
D:季节性趋势项(选用)(可采用statsmodel的STL,seasonal_decompose函数进行分解,前者使用Loess平滑可对付噪音,后者适合强季节性稳定序列分解,按需取用)
对于一些时间序列模型,其可能会有季节性,可能有的人会问,季节性是什么呀?季节性可以认为是将一段区间分为许多小的区间,所有小的区间都展示出一样的变化趋势,小区间的范围固定,也就是时间频率固定(相同的周期)。
那么季节性和周期性是不是同一个东西呢?答案是❌的,周期性是没有一个固定的时间频率,而季节性有(这也是为什么春夏秋冬季节各3个月,这就是季节 性)
模型选用:
ARIMA模型通用代码,请注意修改部分内容以不会报错,这里是通用代码。
import statsmodels.api as sm
import numpy as np
import pandas as pd
from itertools import product
data = pd.DataFrame() # 中间放入你的数据集,对于一个传统ARIMA模型有以下步骤
# ADF检验,ARIMA需要序列平稳,不平稳则通过差分平稳
adf = sm.tsa.adfuller(data) # 返回值会是一个列表,直接取出p值即可
p = adf[1]
while p > 0.05:
# p > 0.05代表原假设成立,说明原序列不平稳,进行差分
data = data.diff()
p = sm.tsa.adfuller(data)[1]
# 进行ARIMA模型的参数选取, ARIMA参数的判断主要是通过看图认为判断拖尾截尾
# 但是电脑没办法代替我们的眼睛看拖尾截尾,于是只能暴力破解出最优组合,通过bic确定最优解
p = d = q = range(2)
pdq = list(product(p, d, q))
# 数据量较小情况下,我们可以对所有不同类型的数据都建立模型,以达到最精准的状态
for n in range(): # 放入你的不同类型数量
best_bic = np.inf # 记住,bic越小说明模型越好
best_param = []
for i in pdq:
try:
model = sm.tsa.ARIMA(data, order=i)
bic = model.fit().bic # 先拟合后获取bic
if bic < best_bic: # 更优质的组合代替原有内容
best_bic = bic
best_param = i
except:
continue
model = sm.tsa.ARIMA(data, order=best_param).fit()
model.predict() # 放入你的测试集
print(model.summary()) # 此为描述你的模型特征
XGBoost模型代码
from sklearn.metrics import mean_squared_error
from xgboost import XGBRegressor, plot_importance # plot_importance 用于观察特征的重要性,对特征筛选起着重要作用
from sklearn.model_selection import GridSearchCV
import numpy as np
import pandas as pd
data = pd.DataFrame() # 中间放入你的数据集,对于一个传统ARIMA模型有以下步骤
feat = pd.DataFrame() # 请进行你的特征工程,机器学习中特征工程是必须的
X_train = pd.DataFrame() # X_train是你划分的训练集,其中的内容来自于特征工程!!!!!!!!!
Y_train = pd.DataFrame() # Y_train是销售额用于模型拟合
X_val = pd.DataFrame() # Y_train是验证集特征
Y_val = pd.DataFrame() # Y_val是验证集的值,负责与预测值对比查看模型拟合精度,越小越好
# 这个是参数列表,用于求出最优解,分别是学习率,树深度,样本采样率
param_grid = {
"learning_rate": [0.01, 0.1, 0.2, 0.05],
"max_depth": [3, 5, 7],
"subsample": [0.8, 0.9, 1.0]
}
grid_search = GridSearchCV(XGBRegressor(), param_grid, cv=3) # 使用网格暴力搜索得到最优参数
grid_search.fit(X_train, Y_train) # 模型拟合
best_params = grid_search.best_params_ # 提取最优参数
xgb_model = XGBRegressor(**best_params) # 模型实例化
xgb_model.fit(X_train, Y_train) # 建立模型
predictions = xgb_model.predict(X_val) # 预测
rmse = np.sqrt(mean_squared_error(Y_val, predictions))
print(rmse) # 相对的,也有mse这种检测方法,有很多种方法可以用于观察模型的拟合程度,小例子就是rmse和mse
# 均方根误差
Summary
数据清洗,特征工程,模型选择,进行预测,掌握了这四个步骤,那么也就大概入门机器学习啦~
这只是我们的开胃前菜,快准备把我们的实战时间序列模型预测端上来罢