Использование ARIMA для прогнозирования средней дневной ставки

Средние дневные ставки (далее именуемые ADR) представляют собой средние дневные ставки, оплачиваемые постоянным клиентом в отеле.

Это важный показатель для отеля, поскольку он отражает общую прибыльность каждого клиента.

В этом примере средние дневные ставки для каждого клиента усредняются за неделю, а затем прогнозируются с использованием модели ARIMA.

Приведенный ниже анализ основан на данных Antonio, Almeida and Nunes (2019): Наборы данных о спросе на бронирование отелей.

Манипуляция данными

В этом конкретном наборе данных год и номер недели для каждого клиента (вместе с зарегистрированным значением ADR каждого клиента) указываются отдельно.

Вот столбцы даты:

Вот столбец ADR:

Во-первых, номер года и недели объединяется в одну строку:

df1 = df['ArrivalDateYear'].map(str) + df['ArrivalDateWeekNumber'].map(str)
print (df1)
df1=pd.DataFrame(df1)

Затем новый столбец объединяется со столбцом ADR с помощью панд:

df2 = DataFrame(c, columns= ['ADR']) 
df2
df3=pd.concat([df1, df2], axis = 1)
df3
df3.columns = ['FullDate', 'ADR']

Затем эти значения сортируются по дате:

df3
df3.sort_values(['FullDate','ADR'], ascending=True)

Следующим шагом будет получение среднего значения ADR за неделю, например для записи 201527 (27-я неделя 2015 г.) все значения ADR усредняются, и так далее для каждой последующей недели.

df4 = df3.groupby('FullDate').agg("mean")
df4
df4.sort_values(['FullDate'], ascending=True)

ARIMA

Используя этот недавно сформированный временной ряд, модель ARIMA теперь может использоваться для прогнозирования ADR.

В этой модели первые 100 недель данных используются в качестве обучающих данных, а последние 15 недель используются как тестовые данные для сравнения с прогнозами, сделанными моделью. .

Вот график вновь сформированного временного ряда:

С первого взгляда на график кажется, что сезонность присутствует. Однако для проверки этого на обучающих данных генерируется функция автокорреляции.

plot_acf(train_df, lags=60, zero=False);

Мы можем видеть, что корреляции (после падения примерно между 10 и 45 неделями) автокорреляция снова достигает пика на лаге 52, что подразумевает годовую сезонность. По сути, это означает, что существует сильная корреляция между значениями ADR, регистрируемыми каждые 52 недели.

Используя эту информацию, m = 52 устанавливается как сезонный компонент в модели ARIMA, а pmdarima используется для автоматического выбора параметров p, d, q для модель.

>>> Arima_model=pm.auto_arima(train_df, start_p=0, start_q=0, max_p=10, max_q=10, start_P=0, start_Q=0, max_P=10, max_Q=10, m=52, stepwise=True, seasonal=True, information_criterion='aic', trace=True, d=1, D=1, error_action='warn', suppress_warnings=True, random_state = 20, n_fits=30)
Performing stepwise search to minimize aic
 ARIMA(0,1,0)(0,1,0)[52]             : AIC=422.399, Time=0.32 sec
 ARIMA(1,1,0)(1,1,0)[52]             : AIC=inf, Time=21.87 sec
 ARIMA(0,1,1)(0,1,1)[52]             : AIC=inf, Time=48.20 sec
 ARIMA(0,1,0)(1,1,0)[52]             : AIC=inf, Time=40.99 sec
 ARIMA(0,1,0)(0,1,1)[52]             : AIC=inf, Time=38.19 sec
 ARIMA(0,1,0)(1,1,1)[52]             : AIC=inf, Time=39.33 sec
 ARIMA(1,1,0)(0,1,0)[52]             : AIC=414.708, Time=0.95 sec
 ARIMA(1,1,0)(0,1,1)[52]             : AIC=inf, Time=47.51 sec
 ARIMA(1,1,0)(1,1,1)[52]             : AIC=inf, Time=59.30 sec
 ARIMA(2,1,0)(0,1,0)[52]             : AIC=413.878, Time=1.86 sec
 ARIMA(2,1,0)(1,1,0)[52]             : AIC=inf, Time=59.96 sec
 ARIMA(2,1,0)(0,1,1)[52]             : AIC=inf, Time=60.34 sec
 ARIMA(2,1,0)(1,1,1)[52]             : AIC=inf, Time=77.60 sec
 ARIMA(3,1,0)(0,1,0)[52]             : AIC=414.514, Time=2.05 sec
 ARIMA(2,1,1)(0,1,0)[52]             : AIC=415.165, Time=3.74 sec
 ARIMA(1,1,1)(0,1,0)[52]             : AIC=413.365, Time=1.91 sec
 ARIMA(1,1,1)(1,1,0)[52]             : AIC=415.351, Time=66.38 sec
 ARIMA(1,1,1)(0,1,1)[52]             : AIC=inf, Time=57.54 sec
 ARIMA(1,1,1)(1,1,1)[52]             : AIC=inf, Time=76.00 sec
 ARIMA(0,1,1)(0,1,0)[52]             : AIC=411.433, Time=1.08 sec
 ARIMA(0,1,1)(1,1,0)[52]             : AIC=413.422, Time=29.17 sec
 ARIMA(0,1,1)(1,1,1)[52]             : AIC=inf, Time=67.32 sec
 ARIMA(0,1,2)(0,1,0)[52]             : AIC=413.343, Time=1.76 sec
 ARIMA(1,1,2)(0,1,0)[52]             : AIC=415.196, Time=3.69 sec
 ARIMA(0,1,1)(0,1,0)[52] intercept   : AIC=413.377, Time=2.05 sec

Best model:  ARIMA(0,1,1)(0,1,0)[52]          
Total fit time: 809.281 seconds

Конфигурация модели ARIMA ARIMA (0, 1, 1) (0, 1, 0) [52] указана как модель наилучшего соответствия согласно наименьшему значению AIC.

Теперь с помощью модели можно делать прогнозы на следующие 15 недель, сравнивая эти прогнозы с набором тестов:

predictions=pd.DataFrame(Arima_model.predict(n_periods=15), index=test_df)
predictions=np.array(predictions)

Перед сравнением прогнозов со значениями из тестового набора прогнозы изменяют форму, чтобы они были в том же формате, что и тестовый набор:

>>> predictions=predictions.reshape(15,-1)
>>> predictions
array([[ 88.0971519 ],
       [103.18056307],
       [117.93678827],
       [121.38546969],
       [112.9812769 ],
       [120.69309927],
       [144.4014371 ],
       [166.36546077],
       [181.69684755],
       [190.12507961],
       [204.36831063],
       [218.85150166],
       [216.59090879],
       [197.74194692],
       [156.98273524]])

Теперь тестовые значения можно сравнить с прогнозами на основе среднеквадратичной ошибки (RMSE) - меньшее значение указывает на меньшую ошибку.

>>> mse = mean_squared_error(test_df, predictions)
>>> rmse = math.sqrt(mse)
>>> print('RMSE: %f' % rmse)
RMSE: 10.093574
>>> np.mean(test_df)
160.492142162915

Получено среднеквадратичное значение 10 по сравнению со средним значением 160. Это представляет ошибку чуть более 6% от среднего, что указывает на то, что модель предсказывает тенденции ADR с довольно высокой точностью.

Вот график прогнозируемых и фактических значений.

Вывод

Большое спасибо за ваше время, и мы будем благодарны за любые вопросы или отзывы.

Вы можете найти Jupyter Notebook для этого примера здесь.

Заявление об ограничении ответственности: эта статья написана на условиях «как есть» без каких-либо гарантий. Он был написан с целью предоставить обзор концепций науки о данных и не должен интерпретироваться как какой-либо профессиональный совет. Автор не имеет отношения к третьим лицам, упомянутым в этой статье.