基于Prophet的高铁乘客数量纯时序预测---TOP10方案(非排行版提交方案) - 知乎
赛题地址:AI研习社 - 研习AI产学研新知,助力AI学术开发者成长。
赛题简介:给出某条高铁线路2012-08-25至2014-06-25的客流量数据,要求预测2014-06-25至2014-09-25上午某时刻或者下午某时刻的乘客数量,预测上午乘客数量时,下午数据会给出,预测下午数据时,上午数据会给出。
赛题评分:100-MAE
数据理解:从赛题简介中的黑色粗体部分可以看出该题数据存在未来(预测时间段内)数据泄露(或者说训练数据穿越)的问题,所以实际应用意义不大。
线上分数:42.6881
上分技巧:
1.历史数据总体趋势呈递增,而且差距很大,采用太久远的历史数据训练反而会降低模型的预测能力。本方案中仅采用了2014年6月10号以后的数据进行训练建模。
2.由于训练数据存在缺失上午或下午的数据,采用“上午”、“下午”分别建模预测效果会更好。给上下午打引号,是因为实际操作的时候,选择了上午8点至晚上20点作为“上午”,剩下的时间段作为“下午”。之所以采取这样的分段是因为,8点至22点正好是一个单调递增到峰值之后再递减的一个相对完整的过程(用大佬 小学四年级学生 的话说就是,服从泊松分布)。具体可以自行画图理解,或者找出更优的划分方式。
3.经过1-2步骤的操作+参数优化,分数能达到39+。在此基础之上将label做boxcox变换能再上3分多,达到42分+。
以下是代码讲解部分:
导入相关包
import warnings
warnings.filterwarnings("ignore")
import numpy as np
import pandas as pd
from fbprophet import Prophet
import matplotlib.pyplot as plt
from scipy import stats
from scipy.special import inv_boxcox
数据加载及处理
#读取训练数据
rese_df = pd.read_csv('../data/train.csv', names =['ds', 'y'], header=0)
rese_df['ds'] = rese_df['ds'].astype('datetime64[ns]')
rese_df['y'] = rese_df['y'].astype(int)
#读取测试数据
test = pd.read_csv('../data/test.csv', names =['id', 'ds'], header=0)
test['ds'] = test['ds'].astype('datetime64[ns]')
#构造预测时间点
test_df = test[['ds']].copy()
#构造线上提交数据
subs = test[['id']].copy()
数据拆分
hours = np.arange(8, 21)
#训练数据拆分
rese_df_mor = rese_df[rese_df['ds'].dt.hour.isin( hours) ]
rese_df_aft = rese_df[~rese_df['ds'].dt.hour.isin(hours) ]
#预测时间点拆分
test_df_mor = test_df[test_df['ds'].dt.hour.isin( hours) ]
test_df_aft = test_df[~test_df['ds'].dt.hour.isin(hours) ]
分开建模
#分上午下午建模
#选择20140610以后的数据作为训练数据
cut_off = pd.date_range(start='06/10/2014', freq='1M', periods=1)
#"上午"部分建模预测
tail_df = rese_df_mor[ rese_df_mor['ds']>cut_off[len(cut_off)-1] ].copy()
#y做boxcox变换
tail_df.loc[tail_df['y']==0, 'y'] = tail_df['y'].mean()
xt, fitted_lambda_mor = stats.boxcox(tail_df['y'])
tail_df['y'] = xt
m = Prophet(yearly_seasonality=False
, daily_seasonality=True
, weekly_seasonality=True
, seasonality_mode='multiplicative'
, interval_width=0.95
, changepoint_range=0.95
, changepoint_prior_scale=0.1
)
m.fit(tail_df)
preds_mor = m.predict(test_df_mor)['yhat']
#"下午"部分建模预测
tail_df = rese_df_aft[ rese_df_aft['ds']>cut_off[len(cut_off)-1] ].copy()
#y做boxcox变换
tail_df.loc[tail_df['y']==0, 'y'] = tail_df['y'].mean()
xt, fitted_lambda_aft = stats.boxcox(tail_df['y'])
tail_df['y'] = xt
m = Prophet(yearly_seasonality=False
, daily_seasonality=True
, weekly_seasonality=True
, seasonality_mode='multiplicative'
, interval_width=0.95
, changepoint_range=0.95
, changepoint_prior_scale=0.1
)
m.fit(tail_df)
preds_aft = m.predict(test_df_aft)['yhat']
生成线上提交文件
#预测值做boxcox逆变换
test_df_mor['y'] = inv_boxcox( np.array( preds_mor ), fitted_lambda_mor)
test_df_aft['y'] = inv_boxcox( np.array( preds_aft ), fitted_lambda_aft)
#合并结果
result = pd.concat([test_df_mor, test_df_aft]).sort_values(by=['ds'], ascending=True)
#生成线上提交文件
subs['y'] = result['y']
subs.to_csv('../subs/prophet39_boxcox.csv', index=None,header=False)
发布于 2020-11-16