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