Прогнозирование авиапассажиропотока между городами РФ

Всем привет!

Это моя первая статья на Хабре, поэтому буду рад комментариям, советам, предложениям и любой реакции.

Я работаю в авиакомпании, занимаюсь анализом продаж, что сильно связано в том числе с планированием и прогнозированием. В условиях, когда российский рынок авиаперевозок сужается, авиакомпании стремятся оптимизировать свою маршрутную сеть, а если и развиваться — то только на направлениях с высоким пассажиропотоком. Дефицит самолетов в условиях санкций делает ошибки непростительными, поэтому своей целью я ставил разработку модели прогнозирования трафика между городами РФ.

Архитектура

В целом, направления делятся на несколько крупных подгрупп, для которых логично использовать разные паттерны в прогнозировании. Так, например, есть курортные направления из Москвы (Сочи, Крым и прочие), по которым спрос хоть и сезонен, но довольно стабилен. Есть курортные направления из регионов, где спрос сильно сезон — банально по той причине, что рейсы выполняются только летом. В целом, поток на курортных направлений сильно зависит от количества гостиниц — нельзя уместить миллион туристов в Сочи за раз, если койко-мест — всего 500 тысяч. Существует также отдельный пул вахтовых направлений — по ним спрос всесезонный, который зависит от экономики вахтового региона. Я счел целесообразным решить на первом этапе задачу кластеризации, где я по пулу факторов попробую разделить направления на кластеры, и только потом — задачу регрессии.

Соответственно, в качестве факторов, влияющих на пассажиропоток, я выбрал следующие:

  • Тип направления: курортное, вахтовое, деловое (между крупными деловыми центрами)

  • Средняя температура в городах и обеспеченность коллективными средствами размещения (для выделения курортных направлений)

  • Расстояние между городами

  • Сезонность

  • Средняя зарплата в городах, население городов, коэффициент мобильности населения и ВРП региона (для выделения вахтовых направлений)

  • Годовой пассажиропоток между городами

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

Концептуальная схема прогностической моделиКонцептуальная схема прогностической модели

Кластеризация

Определим вводные данные для кластеризации:

Признак

Описание

RSC

Круговой сегмент

Var

Месячный к-т вариации

Dist

Расстояние

Temp*

Среднегодовая температура

Population*

Население

Total*

Годовой пассажиропоток чз город

Mobility*

Коэффициент мобильности населения

GRP*

ВРП региона

GRP_per_capita*

ВРП на душу населения в регионе

ZP*

Средняя заработная плата в регионе

* — парные факторы для города вылета и города прилета

Месячный коэффициент вариации рассчитал как отношение месячного среднего квадратического отклонения к среднемесячному пассажиропотоку — это позволит оценить, насколько в среднем поток нестабилен.

Все факторы, что со звездочкой, были взяты из Росстата, кроме коэффициента мобильности — его на нашел как отношение общего количества вылетевших из города пассажиров к населению города.

Отдельной задачей был расчет расстояния между городами, его я реализовал с помощью библиотеки geopy:

from geopy.geocoders import Nominatim
from geopy.distance import geodesic as GD
from math import radians, cos, sin, asin, sqrt 

app = Nominatim(user_agent='myencoder', timeout=5)

cities_for_dist = tdf['Город'].unique()
dist = pd.DataFrame(columns=['Dist'], index=cities_for_dist)

def get_dist(city1='Москва', city2='Санкт-Петербург'):
    
    loc1 = app.geocode(city1).raw
    loc2 = app.geocode(city2).raw
    lat1 = float(loc1['lat'])
    lon1 = float(loc1['lon'])
    lat2 = float(loc2['lat'])
    lon2 = float(loc2['lon'])
    dest = distance_1(lat1, lat2, lon1, lon2)
    return dest

def distance_1(La1, La2, Lo1, Lo2): 
      
    Lo1 = radians(Lo1) 
    Lo2 = radians(Lo2) 
    La1 = radians(La1) 
    La2 = radians(La2) 
       
    # Формула Гаверсинуса
    D_Lo = Lo2 - Lo1 
    D_La = La2 - La1 
    P = sin(D_La / 2)**2 + cos(La1) * cos(La2) * sin(D_Lo / 2)**2 
  
    Q = 2 * asin(sqrt(P)) 
     
    # Радиус земли для расчета расстояния
    R_km = 6371 
       
    # Итоговый результат
    return(Q * R_km)

Алгоритм выполнялся довольно долго — для 400 направлений ушло около 20 минут. Поэтому я сразу сохранил его в csv и в следующих итерациях читал csv-файл с расстояниями. Удивительно, что не возникло проблем с распознаванием таких специфических городов, как Игарка, Талакан и Купол.

Я планировал сформировать около 5–10 кластеров, чтобы выборки в каждом кластере были адекватного (не слишком маленького) размера.

Вводные данные выглядят следующим образом:

Вводные данные для кластеризацииВводные данные для кластеризации

Далее я решил отобрать оптимальное число кластеров с помощью коэффициент Силуэта, а заодно подобрать гиперпараметр n_init. Ситуация сложилась следующим образом:

c7c7b6398bd84ae966fa5a90a5a67e23.png

Видим, что лучшее число кластеров — 7 штук, при гиперпараметре в n_init равном 2. Хотя коэффициент Силуэта невысокий, этого достаточно, чтобы получить приемлемые результаты. А результаты кластеризации оказались очень интересными.

Кластерная разбивкаКластерная разбивка

Видим, что кластеры разделились очень логично, а при детальном рассмотрении я практически не нашел выбросов в кластерах.

Регрессия

Теперь попробуем построить три модели регрессии: RandomForest, GradientBoosting, CatBoost.

В качестве вводных берем следующие факторы:

Признак

Описание

Total

Пассажиропоток на направлении за год

Var

Месячный к-т вариации

Dist

Расстояние

Temp*

Среднегодовая температура

Population*

Население

Total1*

Годовой пассажиропоток чз город

Mobility*

Коэффициент мобильности населения

GRP*

ВРП региона

GRP_per_capita*

ВРП на душу населения в регионе

ZP*

Средняя заработная плата в регионе

Вводные для регрессии выглядят так:

80e768c0809168f0a8d4c681799c6375.png

После отбора гиперпараметров, вышли следующие результаты:

Модель/

Метрика

RandomForest Regressor

GradientBoosting Regressor

CatBoost Regressor

R2

0,56

0,45

0,75

MAPE

29,3%

30,0%

53,6%

Видим, что для всех трех моделей и R2, и MAPE оказались не очень качественными, но для дальнейших исследований я оставлю RandomForest и GradientBoosting.

Вывод

Удалось с умеренной точностью прогнозировать пассажиропоток на московских рейсах с ошибкой менее 30%;

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

Рекомендации для себя на будущее я оставил следующие:

  1. Добавить фактор емкостей конкурентов для повышения точности модели

  2. Включить фактор сезонных мероприятий или перевахтовок для крупных городов

  3. Добавить в анализ перемещения между городами в том числе трансферными рейсами

Спасибо за уделенное внимание. Буду рад обратной связи.

© Habrahabr.ru