Повышение эффективности контролируемых временных рядов - PullRequest
1 голос
/ 03 мая 2019

У меня есть почасовые данные за последние 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])]

Мое текущее время выполнения составляет 30 секунд для каждой итерации в цикле for, который слишком медленный из-за неправильного способа добавления средних значений в тестовые данные. Я был бы очень признателен, если бы кто-нибудь мог указать, как я могу реализовать это немного быстрее.

Я также добавлю остальную часть своего кода и сделаю некоторые другие наблюдения:

    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 больше указывается, поскольку я превратил проблему временных рядов в проблему регрессии. Есть какие-нибудь мысли с вашей стороны по этому вопросу?

Большое спасибо за ваше время и внимание. Любая помощь очень ценится.

Обновление: мне удалось решить проблему с помощью слияния панд. Сначала я создал два фрейма данных, содержащих средние суточные и среднечасовые значения из обучающих данных, а затем объединил эти фреймы с тестовыми данными:

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 мест в час на ноутбуке с приличными результатами, но я постараюсь улучшить ее, сохраняя при этом скорость. Большое спасибо еще раз.

...