介绍
有多种统计模型,例如Box-Jenkins方法和用于时间序列预测的状态空间模型,但是我决定尝试使用LightGBM,它在表数据中很强大。
需要将时间序列数据输入到LightGBM的数据转换。我将不追求准确性而写有关数据处理过程的文章。
数据使用航空乘客。
- 资料说明
- 数据链接
环境
- Google Colab
程序
数据准备
我已经在
之后的部分中尝试了一些转换方法,但是假定已读取了以下库和数据。
1 2 3 4 5 6 7 8 9 10 11 | import pandas as pd import numpy as np import matplotlib.pyplot as plt import lightgbm as lgb from sklearn.model_selection import * # !wget "https://www.analyticsvidhya.com/wp-content/uploads/2016/02/AirPassengers.csv" dateparse = lambda dates: pd.datetime.strptime(dates, '%Y-%m') df = pd.read_csv('AirPassengers.csv', index_col='Month', date_parser=dateparse, dtype='float') |
第一学期的预测
这是近乎实时的预测。这意味着在训练预测变量后,我们将每个时期的观测值用作测试数据。实际上,预测→结果是否正确,最新的观察值将用于下一个预测。
值历史
我们将创建功能。
假设可以某种方式估计数据中存在的12个月周期。
假设您输入了过去12个月的历史记录并预测了第13个月。使用
1 2 | for i in range(1, 13): df['shift%s'%i] = df['#Passengers'].shift(i) |
让我们看一下数据的开头和结尾。
1 | pd.concat([df.head(13), df.tail(3)], axis=0, sort=False) |
数据将向下移动,并且数据将从右到左排列。由于已向下移动,因此没有数据的部分为NaN,数据类型也为float。 NaN部分将在以后删除。
参见第1950-01行。以下是用于培训和测试的数据。用shift1到shift12预测目标变量#Passengers作为解释变量。
衍生历史记录
在时间序列数据中,我们希望捕获随时间变化的变量的特征,因此除历史值外,我们还将差异作为特征。假设最多二阶导数是可以的。
由于数据是离散的,因此导数就是差异。因此,创建一个差异列。使用
1 | df['deriv1'] = df['shift1'].diff(1) |
让我们看一下数据。
1 | df[['#Passengers', 'deriv1']].head(5) |
例如,当预测1949-03时,只有直到1949-02的数据,因此1949-03行是1949-02减去1949-01的值118?112 = 6
学习
我会学习的。由于是实验,所以参数等是适当的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | df = df.dropna() X = df.drop('#Passengers', axis=1) y = df['#Passengers'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False) X_train, X_validation, y_train, y_validation = train_test_split(X_train, y_train, test_size=0.2, shuffle=False) lgb_train = lgb.Dataset(X_train, y_train) lgb_eval = lgb.Dataset(X_validation, y_validation, reference=lgb_train) # LightGBM parameters params = { 'task' : 'train', 'boosting':'gbdt', 'objective' : 'regression', 'metric' : {'mse'}, 'num_leaves':78, 'drop_rate':0.05, 'learning_rate':0.01, 'seed':0, 'verbose':0, 'device': 'cpu' } evaluation_results = {} gbm = lgb.train(params, lgb_train, num_boost_round=100000, valid_sets=[lgb_train, lgb_eval], valid_names=['Train', 'Valid'], evals_result=evaluation_results, early_stopping_rounds=1000, verbose_eval=100) |
预测
让我们绘制测试数据的预测。
1 2 3 4 5 6 7 8 9 | y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration) y_ = np.concatenate([np.array([None for i in range(len(y_train)+len(y_validation))]) , y_pred]) y_ = pd.DataFrame(y_, index=X.index) plt.figure(figsize=(10,5)) plt.plot(y, label='original') plt.plot(y_, '--', label='predict') plt.legend() |
。 .. ..这是一个微妙的结果。
动态预测
在一次预测中,将观测值用于测试数据,但是在动态预测中,将由第一测试数据预测的值用作下一个说明变量,然后说明预测值。我们将递归地预测变量。
在上一节中的预测之前,其过程是相同的。
预测
由于将预测值递归输入到预测变量,因此有必要根据预测值再次创建特征。
首先,让测试数据的第一行为
在测试数据的第一行中,y的历史记录部分为
为了计算微分特征,令来自测试数据的1个周期和2个周期之前的y为
1 2 3 4 5 6 | data = X_test.iloc[0, :] data_index = data.index val = X_test[['shift1', 'shift2', 'shift3', 'shift4', 'shift5', 'shift6', 'shift7', 'shift8', 'shift9', 'shift10', 'shift11', 'shift12']].iloc[0].values y_backward1 = y_validation[-1] y_backward2 = y_validation[-2] |
在预测变量中输入
通过移动val并用预测值填充NaN,y的历史将移动一个周期。
从val计算统计量。
使用
以上总结为新数据。在下一个循环中输入预测变量。重复这个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from scipy import ndimage pred_dynamic = [] for i in range(len(X_test)): y_d = gbm.predict(data, num_iteration=gbm.best_iteration) pred_dynamic.append(y_d[0]) val = ndimage.interpolation.shift(val, 1, cval=y_d) mean_val = np.mean(val) median_val = np.median(val) max_val = np.max(val) min_val = np.min(val) deriv1 = (y_d - y_backward1)[0] deriv2 = (y_d - 2*y_backward1 + y_backward2)[0] feature = np.array([deriv1, deriv2, mean_val, median_val, max_val, min_val]) data = np.hstack([val, feature]) data = pd.Series(data, index=data_index) y_backward2 = y_backward1 y_backward1 = y_d |
绘制预测值。
1 2 3 4 5 6 7 8 9 | y__ = np.concatenate([np.array([None for i in range(len(y_train)+len(y_validation))]) , np.array(pred_dynamic)]) y__ = pd.DataFrame(y__, index=X.index) plt.figure(figsize=(10,5)) plt.plot(y, label='original') plt.plot(y_, '--', label='predict') plt.plot(y__, '--', label='predict_dynamical') plt.legend() |
结论
由于
树型预测器只能在训练数据范围内进行预测,因此认为它不适合预测趋势数据。用于表格系统(例如LightGBM)的预测器可能能够通过删除具有大量数据的趋势分量和训练模式来预测时间序列数据。