受监督的时间序列效率改善

时间:2019-05-03 08:40:36

标签: python pandas machine-learning time-series xgboost

过去4个月中每小时记录一次我的数据。我正在构建时间序列模型,到目前为止,我已经尝试了几种方法:Arima,LSTM,Prophet,但是它们对于我的任务来说可能会很慢,因为我必须在不同位置的数千个时间序列上运行模型。因此,我认为将其转换为监督问题并使用回归可能很有趣。

我从单变量时间序列及其时间索引中提取了4个特征,即:星期几,小时,日均值和时均值。因此,目前我正在使用这4个预测变量,但可能会提取更多(例如,一天的开始,中午等,如果您在此处还有其他建议,也非常欢迎它们:))

我已使用XGBoost进行回归,这是代码的一部分:

# XGB
import xgboost as xgb 
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score

# Functions needed 
def convert_dates(x):
        x['date'] = pd.to_datetime(x['date'])
        #x['month'] = x['date'].dt.month
        #x['year'] = x['date'].dt.year
        x['dayofweek'] = x['date'].dt.dayofweek
        x['hour'] = x['date'].dt.hour
        #x['week_no'] = pd.to_numeric(x['date'].index.strftime("%V"))
        x.pop('date')
        return(x)

def add_avg(x):
        x['daily_avg']=x.groupby(['dayofweek'])['y'].transform('mean')
        x['hourly_avg'] = x.groupby(['dayofweek','hour'])['y'].transform('mean')
        #x['monthly_avg']=x.groupby(['month'])['y'].transform('mean')
        #x['weekly_avg']=x.groupby(['week_no'])['y'].transform('mean')
        return x

xgb_mape_r2_dict = {}

然后我运行一个for循环,在其中选择一个位置并为其建立模型。在这里,我将数据分为训练和测试部分。我知道上周我国的复活节假期可能会造成问题,因为那是罕见的事件,所以这就是为什么我以这种方式划分训练和测试数据。因此,我实际上将从年初到两周前的数据视为训练数据,并将其后第二周的数据视为测试数据。

for j in range(10,20):
    data = df_all.loc[df_all['Cell_Id']==top_cells[j]]
    data.drop(['Cell_Id', 'WDay'], axis = 1, inplace = True)
    data['date'] = data.index
    period = 168
    data_train = data.iloc[:-2*period,:]
    data_test = data.iloc[-2*period:-period,:]


    data_train = convert_dates(data_train)
    data_test = convert_dates(data_test)
    data_train.columns = ['y', 'dayofweek', 'hour']
    data_test.columns = ['y', 'dayofweek', 'hour']

    data_train = add_avg(data_train)
    daily_avg = data_train.groupby(['dayofweek'])['y'].mean().reset_index()
    hourly_avg = data_train.groupby(['dayofweek', 'hour'])['y'].mean().reset_index()

现在,对于测试数据,我添加了过去的平均值,即过去的7个每日平均值和过去的168个小时平均值。实际上,这是运行时间最长的部分,我想提高其效率。

    value_dict ={}
    for k in range(168):
        value_dict[tuple(hourly_avg.iloc[k])[:2]] = tuple(hourly_avg.iloc[k])[2]


    data_test['daily_avg'] = 0
    data_test['hourly_avg'] = 0
    for i in range(len(data_test)):
        data_test['daily_avg'][i] = daily_avg['y'][data_test['dayofweek'][i]]
        data_test['hourly_avg'][i] = value_dict[(data_test['dayofweek'][i], data_test['hour'][i])]

对于for循环中的每个迭代,我当前的运行时间为30秒,这太慢了,因为我用很差的方法将平均值添加到测试数据中。如果有人能指出我该如何更快地实现这一点,我将不胜感激。

我还将添加其余代码,并进行其他一些观察:

    x_train = data_train.drop('y',axis=1)
    x_test = data_test.drop('y',axis=1)
    y_train = data_train['y']
    y_test = data_test['y']

    def XGBmodel(x_train,x_test,y_train,y_test):
        matrix_train = xgb.DMatrix(x_train,label=y_train)
        matrix_test = xgb.DMatrix(x_test,label=y_test)
        model=xgb.train(params={'objective':'reg:linear','eval_metric':'mae'}
                        ,dtrain=matrix_train,num_boost_round=500, 
                        early_stopping_rounds=20,evals=[(matrix_test,'test')],)
        return model

    model=XGBmodel(x_train,x_test,y_train,y_test)


    #submission = pd.DataFrame(x_pred.pop('id'))
    y_pred = model.predict(xgb.DMatrix(x_test), ntree_limit = model.best_ntree_limit)

    #submission['sales']= y_pred

    y_pred = pd.DataFrame(y_pred)
    y_test = pd.DataFrame(y_test)
    y_test.reset_index(inplace = True, drop = True)
    compare_df = pd.concat([y_test, y_pred], axis = 1)
    compare_df.columns = ['Real', 'Predicted']
    compare_df.plot()

    mape = (np.abs((y_test['y'] - y_pred[0])/y_test['y']).mean())*100
    r2 = r2_score(y_test['y'], y_pred[0])
    xgb_mape_r2_dict[top_cells[j]] = [mape,r2]

我已经使用R平方和MAPE来作为准确性度量,尽管我不认为MAPE可以再显示了,因为我已经将时间序列问题转换为回归问题。您对此主题有何想法?

非常感谢您的时间和考虑。非常感谢您的帮助。

更新:我已经设法通过使用熊猫的合并来解决此问题。我首先创建了两个数据框,其中包含来自训练数据的每日平均值和小时平均值,然后将这些ataframe与测试数据合并:

data_test = merge(data_test, daily_avg,['dayofweek'],'daily_avg')
data_test = merge(data_test, hourly_av['dayofweek','hour'],'hourly_avg')
data_test.columns = ['y', 'dayofweek', 'hour', 'daily_avg', 'hourly_avg']

我们将合并功能定义为:

def merge(x,y,col,col_name):
    x =pd.merge(x, y, how='left', on=None, left_on=col, right_on=col,
            left_index=False, right_index=False, sort=True,
             copy=True, indicator=False,validate=None)
    x=x.rename(columns={'sales':col_name})
    return x

我现在可以在笔记本电脑上以每小时2000个位置的速度运行该模型,并获得不错的结果,但是我会尽力对其进行改进,同时保持快速。再次非常感谢您。

0 个答案:

没有答案