1 数据准备

链接:https://pan.baidu.com/s/1SpDi0oT-6jqKmAT4JwC0TA

提取码:6ylx

数据:ads.csv currency.csv

代码:time_series.py

首先引入相关的 statsmodels,包含统计模型函数(时间序列)。

# 引入相关的统计包
import warnings  # 忽略警告
warnings.filterwarnings('ignore')import numpy as np  # 矢量和矩阵
import pandas as pd  # 表格和数据操作
import matplotlib.pyplot as plt
import seaborn as sns
from dateutil.relativedelta import relativedelta  # 有风格地处理日期
from scipy.optimize import minimize  # 函数优化
import statsmodels.formula.api as smf  # 统计与经济计量
import statsmodels.tsa.api as smt
import scipy.stats as scs
from itertools import product
from tqdm import tqdm_notebook
import statsmodels.api as sm

用真实的手机游戏数据作为样例,研究每小时观看的广告数和每日所花的游戏币。

# 1 如真实的手机游戏数据,将调查每小时观看的广告和每天花费的游戏币
ads = pd.read_csv(r'./test/ads.csv', index_col=['Time'], parse_dates=['Time'])
currency = pd.read_csv(r'./test/currency.csv', index_col=['Time'], parse_dates=['Time'])

2 稳定性

建模前,先来了解一下稳定性(stationarity)。

如果一个过程是平稳的,这意味着它不会随时间改变其统计特性,如均值和方差等等。

方差的恒常性称为同方差,协方差函数不依赖于时间,它只取决于观测值之间的距离。

非平稳过程是指分布参数或者分布规律随时间发生变化。也就是说,非平稳过程的统计特征是时间的函数(随时间变化)。

下面的红色图表不是平稳的:

  • 平均值随时间增加

- 方差随时间变化- 随着时间的增加,距离变得越来越近。因此,协方差不是随时间而恒定的

为什么平稳性如此重要呢?通过假设未来的统计性质与目前观测到的统计性质不会有什么不同,可以很容易对平稳序列进行预测。

大多数的时间序列模型,以这样或那样的方式,试图预测那些属性(例如均值或方差)。

如果原始序列不是平稳的,那么未来的预测将是错误的。

大多数时间序列都是非平稳的,但可以(也应该)改变这一点。

平稳时间序列的类型:

  • 平稳过程(stationary process):产生平稳观测序列的过程。

  • 平稳模型(stationary model):描述平稳观测序列的模型。

  • 趋势平稳(trend stationary):不显示趋势的时间序列。

  • 季节性平稳(seasonal stationary):不表现出季节性的时间序列。

  • 严格平稳(strictly stationary):平稳过程的数学定义,特别指观测值的联合分布不受时移的影响。

若时序中有明显的趋势和季节性,则对这些成分建模,将它们从观察中剔除,然后用残差训练建模。

平稳性检查方法(可以检查观测值和残差):

  • 看图:绘制时序图,看是否有明显的趋势或季节性,如绘制频率图,看是否呈现高斯分布(钟形曲线)。

  • 概括统计:看不同季节的数据或随机分割或检查重要的差分,如将数据分成两部分,计算各部分的均值和方差,然后比较是否一样或同一范围内

  • 统计测试:选用统计测试检查是否有趋势和季节性

若时序的均值和方差相差过大,则有可能是非平稳序列,此时可以对观测值取对数,将指数变化转变为线性变化。然后再次查看取对数后的观测值的均值和方差以及频率图。

上面前两种方法常常会欺骗使用者,因此更好的方法是用统计测试 sm.tsa.stattools.adfuller()。

接下来将学习如何检测稳定性,从白噪声开始。

# 5经济计量方法(Econometric approach)
# ARIMA 属于经济计量方法
# 创建平稳序列
white_noise = np.random.normal(size=1000)
with plt.style.context('bmh'):plt.figure(figsize=(15,5))plt.plot(white_noise)

标准正态分布产生的过程是平稳的,在0附近振荡,偏差为1。现在,基于这个过程将生成一个新的过程,其中每个后续值都依赖于前一个值。

def plotProcess(n_samples=1000,rho=0):x=w=np.random.normal(size=n_samples)for t in range(n_samples):x[t] = rho*x[t-1]+w[t]with plt.style.context('bmh'):plt.figure(figsize=(10,5))plt.plot(x)plt.title('Rho {}\n Dickey-Fuller p-value: {}'.format(rho,round(sm.tsa.stattools.adfuller(x)[1],3)))#-------------------------------------------------------------------------------------
for rho in [0,0.6,0.9,1]:plotProcess(rho=rho)

第一张图与静止白噪声一样。

第二张图,ρ增加至0.6,大的周期出现,但整体是静止的。

第三张图,偏离0均值,但仍然在均值附近震荡。

第四张图,ρ= 1,有一个随机游走过程即非平稳时间序列。当达到临界值时, 不返回其均值。从两边减去 ,得到  ,左边的表达式被称为一阶差分

如果 ρ= 1,那么一阶差分等于平稳白噪声 。这是Dickey-Fuller时间序列平稳性测试(测试单位根的存在)背后的主要思想。

如果可以用一阶差分从非平稳序列中得到平稳序列,称这些序列为1阶积分该检验的零假设是时间序列是非平稳的,在前三个图中被拒绝,在最后一个图中被接受。

1阶差分并不总是得到一个平稳的序列,因为这个过程可能是 d 阶的积分,d > 1阶的积分(有多个单位根)。在这种情况下,使用增广的Dickey-Fuller检验,它一次检查多个滞后时间。

可以使用不同的方法来去除非平稳性:各种顺序差分、趋势和季节性去除、平滑以及转换如Box-Cox或对数转换。

3 SARIMA

接下来开始建立ARIMA模型,在建模之前需要将非平稳时序转换为平稳时序。

3.1 去除非稳定性

# 摆脱非平稳性,建立SARIMA(Getting rid of non-stationarity and building SARIMA)def tsplot(y,lags=None,figsize=(12,7),style='bmh'):"""Plot time series, its ACF and PACF, calculate Dickey-Fuller testy:timeserieslags:how many lags to include in ACF,PACF calculation"""if not isinstance(y, pd.Series):y = pd.Series(y)with plt.style.context(style):fig = plt.figure(figsize=figsize)layout=(2,2)ts_ax = plt.subplot2grid(layout, (0,0), colspan=2)acf_ax = plt.subplot2grid(layout, (1,0))pacf_ax = plt.subplot2grid(layout, (1,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(ads.Ads, lags=60)

从图中可以看出,Dickey-Fuller检验拒绝了单位根存在的原假设(p=0);序列是平稳的,没有明显的趋势,所以均值是常数,方差很稳定。

唯一剩下的是季节性,必须在建模之前处理它。可以通过季节差分去除季节性,即序列本身减去一个滞后等于季节周期的序列。

ads_diff = ads.Ads-ads.Ads.shift(24) # 去除季节性
tsplot(ads_diff[24:], lags=60)ads_diff = ads_diff - ads_diff.shift(1) # 去除趋势
tsplot(ads_diff[24+1:], lags=60) # 最终图

第一张图中,随着季节性的消失,自回归好多了,但是仍存在太多显著的滞后,需要删除。首先使用一阶差分,用滞后1从自身中减去时序。

第二张图中,通过季节差分和一阶差分得到的序列在0周围震荡。Dickey-Fuller试验表明,ACF是平稳的,显著峰值的数量已经下降,可以开始建模了。

3.2 建 SARIMA 模型

SARIMA:Seasonal Autoregression Integrated Moving Average model。

是简单自回归移动平均的推广,并增加了积分的概念。

  • AR(p): 利用一个观测值和一些滞后观测值之间的依赖关系的模型。模型中的最大滞后称为p。要确定初始p,需要查看PACF图并找到最大的显著滞后,在此之后大多数其他滞后都变得不显著。

  • I(d): 利用原始观测值的差值(如观测值减去上一个时间步长的观测值)使时间序列保持平稳。这只是使该系列固定所需的非季节性差分的数量。在例子中它是1,因为使用了一阶差分。

  • MA(q):利用观测值与滞后观测值的移动平均模型残差之间的相关性的模型。目前的误差取决于前一个或前几个,这被称为q。初始值可以在ACF图上找到,其逻辑与前面相同

每个成分都对应着相应的参数。

SARIMA(p,d,q)(P,D,Q,s) 模型需要选择趋势和季节的超参数。

趋势参数,趋势有三个参数,与ARIMA模型的参数一样:

  • p: 模型中包含的滞后观测数,也称滞后阶数。

  • d: 原始观测值被差值的次数,也称为差值度。

  • q:移动窗口的大小,也叫移动平均的阶数

季节参数

  • S(s):负责季节性,等于单个季节周期的时间步长

  • P:模型季节分量的自回归阶数,可由PACF推导得到。但是需要看一下显著滞后的次数,是季节周期长度的倍数。如果周期等于24,看到24和48的滞后在PACF中是显著的,这意味着初始P应该是2。P=1将利用模型中第一次季节偏移观测,如t-(s*1)或t-24;P=2,将使用最后两个季节偏移观测值t-(s * 1), t-(s * 2)

  • Q:使用ACF图实现类似的逻辑

  • D:季节性积分的阶数(次数)。这可能等于1或0,取决于是否应用了季节差分。

A seasonal ARIMA model uses differencing at a lag equal to the number of seasons (s) to remove additive seasonal effects. As with lag 1 differencing to remove a trend, the lag s differencing introduces a moving average term. The seasonal ARIMA model includes autoregressive and moving average terms at lag s. — Page 142, Introductory Time Series with R, 2009.

可以通过分析ACF和PACF图来选择趋势参数,查看最近时间步长的相关性(如1,2,3)。

同样,可以分析ACF和PACF图,查看季节滞后时间步长的相关性来指定季节模型参数的值。

现在知道了如何设置初始参数,查看最终图并设置参数。

上面倒数第一张图就是最终图:

  • p:最可能是4,因为它是PACF上最后一个显著的滞后,在这之后,大多数其他的都不显著。

  • d:为1,因为我们计算了一阶差分

  • q:应该在4左右,就像ACF上看到的那样

  • P:可能是2,因为24和48的滞后对PACF有一定的影响

  • D:为1,因为计算了季节差分

  • Q:可能1,ACF的第24个滞后显著,第48个滞后不显著。

下面看不同参数的模型表现如何:

# 建模 SARIMA
# setting initial values and some bounds for them
ps = range(2,5)
d=1
qs=range(2,5)
Ps=range(0,2)
D=1
Qs=range(0,2)
s=24 #season length# creating list with all the possible combinations of parameters
parameters=product(ps,qs,Ps,Qs)
parameters_list = list(parameters)
print(parameters)
print(parameters_list)
print(len(parameters_list))
# 36
def optimizeSARIMA(parameters_list, d,D,s):"""Return dataframe with parameters and corresponding AICparameters_list:list with (p,q,P,Q) tuplesd:integration order in ARIMA modelD:seasonal integration orders:length of season"""results = []best_aic = float('inf')for param in tqdm_notebook(parameters_list):# we need try-exccept because on some combinations model fails to convergetry:model = sm.tsa.statespace.SARIMAX(ads.Ads, order=(param[0], d,param[1]),seasonal_order=(param[2], D,param[3], s)).fit(disp=-1)except:continueaic = model.aic# saving best model, AIC and parametersif aic<best_aic:best_model = modelbest_aic = aicbest_param = paramresults.append([param, model.aic])result_table = pd.DataFrame(results)result_table.columns = ['parameters', 'aic']# sorting in ascending order, the lower AIC is - the betterresult_table = result_table.sort_values(by='aic',ascending=True).reset_index(drop=True)return result_table
%%time
result_table = optimizeSARIMA(parameters_list, d,D,s)result_table.head()# set the parameters that give the lowerst AIC
p,q,P,Q = result_table.parameters[0]
best_model = sm.tsa.statespace.SARIMAX(ads.Ads, order=(p,d,q),seasonal_order=(P,D,Q,s)).fit(disp=-1)
print(best_model.summary()) # 打印拟合模型的摘要。这总结了所使用的系数值以及对样本内观测值的拟合技巧。# inspect the residuals of the model
tsplot(best_model.resid[24+1:], lags=60)
parameters          aic
0  (2, 3, 1, 1)  3888.642174
1  (3, 2, 1, 1)  3888.763568
2  (4, 2, 1, 1)  3890.279740
3  (3, 3, 1, 1)  3890.513196
4  (2, 4, 1, 1)  3892.302849Statespace Model Results
==========================================================================================
Dep. Variable:                                Ads   No. Observations:                  216
Model:             SARIMAX(2, 1, 3)x(1, 1, 1, 24)   Log Likelihood               -1936.321
Date:                            Sun, 08 Mar 2020   AIC                           3888.642
Time:                                    23:06:23   BIC                           3914.660
Sample:                                09-13-2017   HQIC                          3899.181- 09-21-2017
Covariance Type:                              opg
==============================================================================coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
ar.L1          0.7913      0.270      2.928      0.003       0.262       1.321
ar.L2         -0.5503      0.306     -1.799      0.072      -1.150       0.049
ma.L1         -0.7316      0.262     -2.793      0.005      -1.245      -0.218
ma.L2          0.5651      0.282      2.005      0.045       0.013       1.118
ma.L3         -0.1811      0.092     -1.964      0.049      -0.362      -0.000
ar.S.L24       0.3312      0.076      4.351      0.000       0.182       0.480
ma.S.L24      -0.7635      0.104     -7.361      0.000      -0.967      -0.560
sigma2      4.574e+07   5.61e-09   8.15e+15      0.000    4.57e+07    4.57e+07
===================================================================================
Ljung-Box (Q):                       43.70   Jarque-Bera (JB):                10.56
Prob(Q):                              0.32   Prob(JB):                         0.01
Heteroskedasticity (H):               0.65   Skew:                            -0.28
Prob(H) (two-sided):                  0.09   Kurtosis:                         4.00
===================================================================================
Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).
[2] Covariance matrix is singular or near-singular, with condition number 8.82e+31. Standard errors may be unstable.

很明显,残差是平稳的,不存在明显的自相关。可用我们的模型来预测。

def plotSARIMA(series, model, n_steps):"""plot model vs predicted valuesseries:dataset with timeseriesmodel:fitted SARIMA modeln_steps:number of steps to predict in the future"""# adding model valuesdata = series.copy()data.columns = ['actual']data['arima_model']=model.fittedvalues# making a shift on s+d steps, because these values were unobserved by the model due the differentiatingdata['arima_model'][:s+d]=np.nan# forecasting on n_steps forwardforecast = model.predict(start=data.shape[0],end=data.shape[0]+n_steps)forecast = data.arima_model.append(forecast)# calculate error, again having shifted on s+d steps from the beginningerror = mean_absolute_percentage_error(data['actual'][s+d:], data['arima_model'][s+d:])plt.figure(figsize=(15,7))plt.title('Mean Absolute Percentage Error: {0:.2f}%'.format(error))plt.plot(forecast,color='r',label='model')plt.axvspan(data.index[-1],forecast.index[-1], alpha=0.5,color='lightgrey')plt.plot(data.actual,label='actual')plt.legend()plt.grid(True)#-------------------------------------------------------------------------------------
plotSARIMA(ads, best_model, 50)

最后,得到了非常充分的预测。模型平均误差是3.94%,非常好。但准备数据、使序列平稳和选择参数的总成本可能不值得这么精确。

这一过程的步骤总结如下:

  • 模式识别:使用图表和汇总统计数据来确定趋势、季节性和自回归元素,从而了解差分的大小和所需的滞后的大小。

  • 参数估计:利用拟合程序求出回归模型的系数。

  • 模型检查:利用剩余误差的图和统计检验来确定模型没有捕捉到的时间结构的数量和类型。

4 线性模型

通常,在工作中必须以快速、好作为指导原则来建立模型。

一些模型不能拿来就用,因为它们需要太多的数据准备时间(如SARIMA),或者需要对新数据进行频繁的训练(SARIMA),或者很难调参(SARIMA)。因此,从现有的时间序列中选择几个特征并构建一个简单的线性回归模型,或者一个随机森林,通常要容易得多。

这种方法没有理论支持,并且打破了几个假设(例如高斯-马尔科夫定理,尤其是误差不相关的情况下),但是在实践中非常有用,经常在机器学习竞赛中使用。

4.1 特征提取

模型需要特征,但只有一维的时间序列。可以提取那些特征?

  • 时间序列窗口统计的滞后:一个窗口中序列的最大值/最小值、一个窗口中值、窗口方差等。

  • 日期和时间特征:一小时的第几分钟,一天的第几小时,一周的第几天等等

  • 特别的活动:将其表示为布尔特征(注意,这种方式可能失去预测的速度)。让我们运行一些方法,看看我们可以从ads时间序列数据中提取什么。

接下来用这些方法提取特征。

4.1.1 滞后特征

将序列向后移动 n个时间点,得到一个特征列,其中时间序列的当前值与其在时间 t-n 时的值一致。

若进行一个 1 延迟移位,并对该特征训练模型,则该模型能够在观察到该序列的当前状态后提前预测1步。

增加滞后,比如增加到6,将允许模型提前6步做出预测,将使用6步前观察到的数据。

如果在这段未观察到的时间内,有什么东西从根本上改变了这个序列,那么模型就不会捕捉到这些变化,并且会返回带有很大误差的预测。因此,在初始滞后选择过程中,必须在最优预测质量与预测长度之间找到一个平衡点。

# 其他模型:线性...
# Creating a copy of the initial dataframe to make various transformations
data = pd.DataFrame(ads.Ads.copy())
data.columns=['y']# Adding the lag of the target variable from 6 setps back up to 24
for i in range(6,25):data['lag_{}'.format(i)]=data.y.shift(i)# take a look at the new dataframe
data.tail(7)
y     lag_6    ...       lag_23    lag_24
Time                                     ...
2017-09-21 17:00:00  151790  132335.0    ...     146215.0  139515.0
2017-09-21 18:00:00  155665  146630.0    ...     142425.0  146215.0
2017-09-21 19:00:00  155890  141995.0    ...     123945.0  142425.0
2017-09-21 20:00:00  123395  142815.0    ...     101360.0  123945.0
2017-09-21 21:00:00  103080  146020.0    ...      88170.0  101360.0
2017-09-21 22:00:00   95155  152120.0    ...      76050.0   88170.0
2017-09-21 23:00:00   80285  151790.0    ...      70335.0   76050.0
[7 rows x 20 columns]
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score# for time-series cross-validation set 5 folds
tscv = TimeSeriesSplit(n_splits=5)def timeseries_train_test_split(X,y,test_size):"""Perform train-test split with respect to time series structure"""# get the index after which test set startstest_index = int(len(X)*(1-test_size))X_train = X.iloc[:test_index]y_train = y.iloc[:test_index]X_test = X.iloc[test_index:]y_test = y.iloc[test_index:]return X_train,X_test,y_train,y_test#-------------------------------------------------------------------------------------
y = data.dropna().y
X = data.dropna().drop(['y'],axis=1)# reserve 30% of data for testing
X_train, X_test, y_train, y_test =timeseries_train_test_split(X,y,test_size=0.3)#-------------------------------------------------------------------------------------
# machine learning in two lines
lr = LinearRegression()
lr.fit(X_train, y_train)#-------------------------------------------------------------------------------------
def plotModelResults(model, X_train=X_train, X_test=X_test, plot_intervals=False, plot_anomalies=False):"""Plot modelled vs fact values, prediction intervals and anomalies"""prediction = model.predict(X_test)plt.figure(figsize=(15,7))plt.plot(prediction,'g',label='prediction', linewidth=2.0)plt.plot(y_test.values, label='actual', linewidth=2.0)if plot_intervals:cv = cross_val_score(model, X_train, y_train, cv=tscv, scoring='neg_mean_absolute_error')mae = cv.mean() *(-1)deviation = cv.std()scale=1.96lower = prediction-(mae + scale * deviation)upper = prediction + (mae + scale *deviation)plt.plot(lower, 'r--', label='upper bond / lower bond', alpha=0.5)plt.plot(upper, 'r--', alpha=0.5)if plot_anomalies:anomalies = np.array([np.nan]*len(y_test))anomalies[y_test<lower] = y_test[y_test<lower]anomalies[y_test>upper] = y_test[y_test>upper]plt.plot(anomalies, 'o', markersize=10, label='Anomalies')error = mean_absolute_percentage_error(prediction, y_test)plt.title('Mean absolute percentage error {0:.2f}%'.format(error))plt.legend(loc='best')plt.tight_layout()plt.grid(True)def plotCoefficients(model):"""PLots sorted coefficient values of the model"""coefs = pd.DataFrame(model.coef_, X_train.columns)print()coefs.columns = ['coef']coefs['abs'] = coefs.coef.apply(np.abs)coefs = coefs.sort_values(by='abs', ascending=False).drop(['abs'],axis=1)plt.figure(figsize=(15, 7))coefs.coef.plot(kind='bar')plt.grid(True, axis='y')plt.hlines(y=0,xmin=0, xmax=len(coefs), linestyles='dashed')#-------------------------------------------------------------------------------------
plotModelResults(lr, plot_intervals=True)
plotCoefficients(lr)

简单的滞后和线性回归的预测在质量方面与SARIMA差不多。有许多不必要的特征,稍后将进行特征选择。

接下来提取时间特征。

4.1.2 时间特征

# 提取时间特征 hour、day of week、is_weekend
data.index = pd.to_datetime(data.index)
data['hour'] = data.index.hour
data['weekday'] = data.index.weekday
data['is_weekend'] = data.weekday.isin([5,6])*1
data.tail()#-------------------------------------------------------------------------------------
# 可视化特征
plt.figure(figsize=(16,5))
plt.title('Encoded features')
data.hour.plot()
data.weekday.plot()
data.is_weekend.plot()
plt.grid(True)
y     lag_6     ...      weekday  is_weekend
Time                                      ...
2017-09-21 19:00:00  155890  141995.0     ...            3           0
2017-09-21 20:00:00  123395  142815.0     ...            3           0
2017-09-21 21:00:00  103080  146020.0     ...            3           0
2017-09-21 22:00:00   95155  152120.0     ...            3           0
2017-09-21 23:00:00   80285  151790.0     ...            3           0
[5 rows x 23 columns]

因为现在的变量中有不同的尺度,滞后特征有数千,分类特征有数十,我们需要将它们转换成相同的尺度,以探索特征的重要性,然后是正则化。

4.2 归一化

# 特征的尺度不一样,需要归一化from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()y = data.dropna().y
X = data.dropna().drop(['y'], axis=1)
X_train, X_test, y_train, y_test =timeseries_train_test_split(X,y,test_size=0.3)
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)lr = LinearRegression()
lr.fit(X_train_scaled, y_train)#-------------------------------------------------------------------------------------
plotModelResults(lr, X_train=X_train_scaled, X_test=X_test_scaled, plot_intervals=True)
plotCoefficients(lr)

4.3 目标编码

def code_mean(data, cat_feature, real_feature):"""Returns a dictionary where keys are unique categories of the cat_feature,and values are means over real_feature"""return dict(data.groupby(cat_feature)[real_feature].mean())average_hour = code_mean(data, 'hour', 'y')
plt.figure(figsize=(7,5))
plt.title('Hour averages')
pd.DataFrame.from_dict(average_hour, orient='index')[0].plot()
plt.grid(True)

把所有的变换放在一个函数中:

# 将所有的数据准备结合到一起
def prepareData(series, lag_start, lag_end, test_size, target_encoding=False):"""series: pd.DataFrame or dataframe with timeserieslag_start: int, initial step back in time to slice target variableexample-lag_start=1 means that the model will see yesterday's values to predict todaylag_end: int, finial step back in time to slice target variableexample-lag_end=4 means that the model will see up to 4 days back in time to predict todaytest_size:float, size of the test dataset after train/test split as percentage of datasettarget_encoding:boolean, if True -  add target averages to dataset"""# copy of the initial datasetdata = pd.DataFrame(series.copy())data.columns = ['y']# lags of seriesfor i in range(lag_start, lag_end):data['lag_{}'.format(i)]=data.y.shift(i)# datatime featuresdata.index = pd.to_datetime(data.index)data['hour'] = data.index.hourdata['weekday'] =data.index.weekdaydata['is_weekend']=data.weekday.isin([5,6])*1if target_encoding:# calculate averages on train set onlytest_index = int(len(data.dropna())*(1-test_size))data['weekday_average'] = list(map(code_mean(data[:test_index], 'weekday', 'y').get, data.weekday))data['hour_average'] = list(map(code_mean(data[:test_index], 'hour', 'y').get, data.hour))# drop encoded variablesdata.drop(['hour', 'weekday'], axis=1, inplace=True)# train-test splity = data.dropna().yX = data.dropna().drop(['y'], axis=1)X_train, X_test, y_train, y_test = timeseries_train_test_split(X, y, test_size=test_size)return X_train, X_test, y_train, y_test#-------------------------------------------------------------------------------------
X_train, X_test, y_train, y_test = prepareData(ads.Ads, lag_start=6, lag_end=25, test_size=0.3, target_encoding=True)X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)lr = LinearRegression()
lr.fit(X_train_scaled, y_train)plotModelResults(lr, X_train=X_train_scaled, X_test=X_test_scaled, plot_intervals=True, plot_anomalies=True)
plotCoefficients(lr)
# plt.xticks(rotation=45, fontsize=7)

从图中可以看出,存在过拟合。Hour_average在训练数据集中系数过大,以至于模型决定都集中在它上面。结果,预测的质量下降了。

这个问题可以用多种方法解决:例如,我们可以计算目标编码而不是整个训练集,而是针对某个窗口。这样,来自最后一个观察到的窗口的编码将很可能更好地描述当前的序列状态。或者,可以手动删除它,因为我们确信在这种情况下它只会使事情变得更糟。

X_train, X_test, y_train, y_test = prepareData(ads.Ads, lag_start=6, lag_end=25, test_size=0.3, target_encoding=False)X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

4.4 正则化

并不是所有的特征都是有用的,有些可能会导致过拟合,有些应该被删除。

除了人工检查,还可以采用正则化。

两个有名的正则化回归模型是岭回归和套索回归[2]。它们都给损失函数增加了一些约束。

在岭回归的情况下,这些约束是系数乘正则化系数的平方和。一个特征的系数越大,损失就越大。因此,尽量优化模型,同时保持系数相当低。L2正则化的结果,将有更高的偏差和更低的方差,因此模型泛化性能更好。

第二个回归模型是Lasso回归,它将损失函数添加到系数的绝对值中,而不是平方。因此,在优化过程中,不重要的特征的系数可能变成零,从而允许自动选择特征。这种正则化类型称为L1。

首先,确定有要删除的特征,并且数据具有高度相关的特征。

# 若过拟合,对特征进行正则化
# 先看一下特征之间的相关性
y = data.dropna().y
X = data.dropna().drop(['y'], axis=1)
X_train, X_test, y_train, y_test =timeseries_train_test_split(X,y,test_size=0.3)
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)plt.figure(figsize=(10,8))
sns.heatmap(X_train.corr())

# 开始正则化
# 岭回归
from sklearn.linear_model import LassoCV, RidgeCVridge = RidgeCV(cv=tscv)
ridge.fit(X_train_scaled,y_train)
plotModelResults(ridge, X_train=X_train_scaled,X_test=X_test_scaled, plot_intervals=True, plot_anomalies=True)
plotCoefficients(ridge)

可以清楚地看到,随着它们在模型中的重要性下降,一些系数正越来越接近于零(尽管它们从未真正达到零)。

# 套索回归
lasso = LassoCV(cv=tscv)
lasso.fit(X_train_scaled,y_train)
plotModelResults(lasso, X_train=X_train_scaled,X_test=X_test_scaled, plot_intervals=True, plot_anomalies=True)
plotCoefficients(lasso)

套索回归被证明是更保守的。它从最重要的特征中去除了23的延迟,并完全去掉了5个特征,这提高了预测的质量。

下面尝试用XGBoost建模。

5 Boosting

# 预测模型 Boosting
from xgboost import XGBRegressorxgb = XGBRegressor()
xgb.fit(X_train_scaled, y_train)
plotModelResults(xgb, X_train=X_train_scaled,X_test=X_test_scaled, plot_intervals=True, plot_anomalies=True)

这是到目前为止测试过的所有模型中测试集上误差最小的。但是,这具有欺骗性。得到时间序列数据,不适合先用xgboost。

通常,与线性模型相比,基于树的模型处理数据趋势的能力较差。在这种情况下,您必须先停止使用序列,或者使用一些技巧来实现这个魔术。

理想情况下,您可以使该系列平稳,然后使用XGBoost。例如,可以使用线性模型预测趋势,然后加上xgboost的预测以获得最终预测。

参考资料

[1]

时间序列分析和预测: https://blog.csdn.net/mengjizhiyou/article/details/82683448

[2]

岭回归和套索回归: https://blog.csdn.net/mengjizhiyou/article/details/103048479

建议阅读:

高考失利之后,属于我的大学本科四年

【资源分享】对于时间序列,你所能做的一切.

【时空序列预测第一篇】什么是时空序列问题?这类问题主要应用了哪些模型?主要应用在哪些领域?

【AI蜗牛车出品】手把手AI项目、时空序列、时间序列、白话机器学习、pytorch修炼

公众号:AI蜗牛车保持谦逊、保持自律、保持进步个人微信
备注:昵称+学校/公司+方向
如果没有备注不拉群!
拉你进AI蜗牛车交流群

【时间序列】时序分析实战之SARIMA、Linear model...相关推荐

  1. Plotly绘制时间序列图实战:简单时序图、时间范围限制的时序图

    Plotly绘制时间序列图实战:简单时序图.时间范围限制的时序图 # 简单时间序列图: import plotly as py import plotly.graph_objs as gofrom d ...

  2. Plotly绘制金融时间序列图实战:配置滑动控件

    Plotly绘制金融时间序列图实战:配置滑动控件 # 可视化金融时间序列数据并设置时间粒度组件: import plotly as py import plotly.graph_objs as go ...

  3. 混合线性模型+mixed linear model+GEEs+GLMM+LMM

    混合线性模型+mixed linear model+GEEs+GLMM+LMM 线性回归 广义线性回归 混合线性模型/线性混合模型 的区别是什么? spss中遇见线性混合模型 价值,意义,目的是什么? ...

  4. voom: precision weights unlock linear model analysis tools for RNA-seq read counts

    voom: precision weights unlock linear model analysis tools for RNA-seq read counts 标准化方式 首先在定义cpm的时候 ...

  5. Google 深度学习笔记 - Limit of Linear Model

    https://www.toutiao.com/a6679981747167298051/ 实际要调整的参数很多 如果有N个Class,K个Label,需要调整的参数就有(N+1)K个 Linear ...

  6. R语言对数线性模型loglm函数_使用R语言进行混合线性模型(mixed linear model) 分析代码及详解...

    1.混合线性模型简介 混合线性模型,又名多层线性模型(Hierarchical linear model).它比较适合处理嵌套设计(nested)的实验和调查研究数据.此外,它还特别适合处理带有被试内 ...

  7. 【李宏毅2020 ML/DL】补充:Structured Learning: Introduction Structured Linear Model

    我已经有两年 ML 经历,这系列课主要用来查缺补漏,会记录一些细节的.自己不知道的东西. 本次笔记补充视频 BV1JE411g7XF 的缺失部分.在另一个UP主上传的2017课程BV13x411v7U ...

  8. Machine Learning——Linear Model

    本系列博客是我学习周志华的<机器学习(西瓜书)>的自学笔记. 我是零基础学习,因此所写只是书上的知识,肯定不全面,以后随着学习的深入,慢慢补充吧. 基本形式 给定由ddd个属性描述的示例x ...

  9. 线性模型(Linear Model)

    线性模型(Linear Model) 对于给定样本x⃗ \mathbf{\vec{x}},假定其有n维特征,则,x⃗ =(x1,x2,x3,-,xn)T\mathbf{\vec{x}}=(x_1, x ...

最新文章

  1. 【转载】C#扫盲之:==/Equals /ReferenceEquals 异同的总结,相等性你真的知道吗?
  2. Ubuntu下使用Evernote
  3. idea-jvm参数设置(有注释)
  4. ob服务器维修视频,教你如何使用OB系统 还在看转播?你OUT了!
  5. usaco3.33Camelot(BFS)
  6. 在基于图论的Java程序中基于DSL的输入图数据的方法
  7. C/C++获取高精度时间
  8. 容器入门(3) - docker
  9. python 手机自动化脚本_iOS python自动化出包脚本
  10. hash(哈希)是什么
  11. 记录自己的UCF—Crime代码debug
  12. Mybatis-plus的Service
  13. 62_LP-3DCNN: Unveiling Local Phase in 3D Convolutional Neural Networks 2019 论文笔记
  14. Ehcache基本使用
  15. ARFoundation入门到精通 - 1.8 远程调试
  16. AWT,SWT缩小图片消除锯齿的平滑处理
  17. C4D透视图设置背景图,实景合成小技巧。
  18. js判断客户端是PC端还是移动端访问
  19. 创始股东四招掌握公司控制权
  20. mysql导入数据命令Load data详解及示例

热门文章

  1. python中常见的错误提示_python常见异常提示
  2. ASP WebShell 后门脚本与免杀
  3. 【ETH链游】阿蟹Axie Infinity模拟器运行及多开
  4. ppt模板目录页如何排版设计?
  5. 创新之道,亚马逊创新之旅背后的故事
  6. Python快速生成gif图
  7. 互联网日报 | 小米市值突破6600亿港元;水滴筹上线“水滴行者”风控系统;2020世界互联网大会开幕...
  8. mysql查询练习tblstudent_通过Navicat进行MySQL的基础查询练习
  9. poi 大量数据写入
  10. 1.1哈恩巴纳赫定理