Как рассчитать величину задержки рейса в минутах по историческим данным о перелетах?

Ответ

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

Базовый расчет с использованием pandas: Предположим, у нас есть колонки с временем прибытия в формате datetime.

import pandas as pd

# Загрузка данных
df = pd.read_csv('flights.csv', parse_dates=['scheduled_arrival', 'actual_arrival'])

# Расчет задержки в минутах.
# Положительное значение = опоздание, отрицательное = прибытие раньше расписания.
df['arrival_delay_minutes'] = (df['actual_arrival'] - df['scheduled_arrival']).dt.total_seconds() / 60

# Просмотр статистики
print(df['arrival_delay_minutes'].describe())

Обработка типичных проблем в данных:

  1. Переход через полночь: Если рейс запланирован на 23:50, а прибыл в 00:15, простая разница даст отрицательное значение. Нужна коррекция:

    def calculate_delay(scheduled, actual):
        delay = (actual - scheduled).total_seconds() / 60
        # Если задержка меньше -12 часов (720 мин), вероятно, переход через полночь в сторону опоздания
        if delay < -720:
            delay += 24 * 60  # Добавляем сутки в минутах
        # Если задержка больше +18 часов, вероятно, раннее прибытие с переходом через полночь назад
        elif delay > 1080:
            delay -= 24 * 60
        return delay
    
    df['arrival_delay_minutes'] = df.apply(
        lambda row: calculate_delay(row['scheduled_arrival'], row['actual_arrival']), axis=1
    )
  2. Отсутствующие и аномальные значения:

    # Удаление записей, где время прибытия отсутствует
    df_clean = df.dropna(subset=['actual_arrival', 'scheduled_arrival'])
    
    # Фильтрация физически невозможных задержек (например, > 2 суток или < -5 часов)
    df_clean = df_clean[(df_clean['arrival_delay_minutes'] > -300) & (df_clean['arrival_delay_minutes'] < 48*60)]

Агрегация и анализ:

# Средняя и медианная задержка по авиакомпании
agg_stats = df_clean.groupby('airline')['arrival_delay_minutes'].agg(['mean', 'median', 'std', 'count'])
print(agg_stats.sort_values('median'))

# Доля рейсов с опозданием более 15 минут
df_clean['is_delayed_15min'] = df_clean['arrival_delay_minutes'] > 15
delay_rate = df_clean.groupby('origin_airport')['is_delayed_15min'].mean() * 100

Для прогнозирования временных рядов этот признак arrival_delay_minutes часто является целевой переменной. Важно также создать лаговые признаки (задержки предыдущих рейсов того же самолета/маршрута) и признаки времени (час дня, день недели, месяц).

Ответ 18+ 🔞

А, ну это ж классика, ебать мои старые костыли! Каждый, кто хоть раз пытался понять, почему его самолёт торчит на полосе, а не у него в аэропорту, в итоге упрётся в эту самую задачу. Считаем, на сколько наш железный конь опоздал.

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

import pandas as pd

# Загрузка данных
df = pd.read_csv('flights.csv', parse_dates=['scheduled_arrival', 'actual_arrival'])

# Расчет задержки в минутах.
# Положительное значение = опоздание, отрицательное = прибытие раньше расписания.
df['arrival_delay_minutes'] = (df['actual_arrival'] - df['scheduled_arrival']).dt.total_seconds() / 60

# Просмотр статистики
print(df['arrival_delay_minutes'].describe())

Выполнил это — и сразу удивление пиздец. Оказывается, у тебя половина рейсов прилетела на 20 часов раньше, а вторая — улетела в будущее. Потому что главный подводный камень — переход через полночь. Самолёт по расписанию в 23:50, а припёрся в 00:15. Компьютер тупо вычтет и скажет, что он прилетел на 23 часа и 35 минут раньше. Хуй с горы, а не результат.

def calculate_delay(scheduled, actual):
    delay = (actual - scheduled).total_seconds() / 60
    # Если задержка меньше -12 часов (720 мин), вероятно, переход через полночь в сторону опоздания
    if delay < -720:
        delay += 24 * 60  # Добавляем сутки в минутах
    # Если задержка больше +18 часов, вероятно, раннее прибытие с переходом через полночь назад
    elif delay > 1080:
        delay -= 24 * 60
    return delay

df['arrival_delay_minutes'] = df.apply(
    lambda row: calculate_delay(row['scheduled_arrival'], row['actual_arrival']), axis=1
)

Дальше — святое дело: почистить данные. Потому что обязательно найдётся запись, где время прибытия — это NaT, или какой-нибудь распиздяй вручную вбил дату 1900 года. А ещё физические ограничения: ну не может Boeing 737 опоздать на две недели, это уже не задержка, а кругосветное путешествие.

# Удаление записей, где время прибытия отсутствует
df_clean = df.dropna(subset=['actual_arrival', 'scheduled_arrival'])

# Фильтрация физически невозможных задержек (например, > 2 суток или < -5 часов)
df_clean = df_clean[(df_clean['arrival_delay_minutes'] > -300) & (df_clean['arrival_delay_minutes'] < 48*60)]

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

# Средняя и медианная задержка по авиакомпании
agg_stats = df_clean.groupby('airline')['arrival_delay_minutes'].agg(['mean', 'median', 'std', 'count'])
print(agg_stats.sort_values('median'))

# Доля рейсов с опозданием более 15 минут
df_clean['is_delayed_15min'] = df_clean['arrival_delay_minutes'] > 15
delay_rate = df_clean.groupby('origin_airport')['is_delayed_15min'].mean() * 100

А если ты хочешь не просто посчитать, а спрогнозировать эту самую задержку, то эта колонка arrival_delay_minutes и будет твоей священной целью. Только не забудь накрутить фичей вокруг да около: задержки предыдущих рейсов этого самолёта, час дня, день недели. Потому что в понедельник утром в «Шереметьево» — это ёперный театр и овердохуища народу, а не просто время суток. Вот тогда модель и заработает как надо.