← Назад к вопросам
Как рассчитать величину задержки рейса в минутах по историческим данным о перелетах?
2.0 Middle🔥 151 комментариев
#Временные ряды#Машинное обучение#Опыт и проекты
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Расчет задержки рейса по историческим данным
Предсказание задержек рейсов — это важная задача в авиационной индустрии. Это регрессионная задача, где нужно предсказать величину задержки (в минутах) на основе исторических данных о рейсах.
Шаг 1: Подготовка данных
Вначале нужно загрузить и исследовать данные:
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
# Загрузка данных
df = pd.read_csv('flights.csv')
# Исследование
print(df.head())
print(df.info())
print(df.describe())
# Проверка целевой переменной
print(f"\nЗадержка - Статистика:")
print(df['ArrDelay'].describe())
print(f"Процент пропущенных: {df['ArrDelay'].isna().sum() / len(df) * 100:.2f}%")
# Визуализация распределения задержек
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.hist(df['ArrDelay'], bins=50, edgecolor='black')
plt.xlabel('Задержка (минуты)')
plt.ylabel('Количество рейсов')
plt.title('Распределение задержек рейсов')
plt.subplot(1, 2, 2)
plt.hist(df[df['ArrDelay'] <= 60]['ArrDelay'], bins=50, edgecolor='black')
plt.xlabel('Задержка (минуты)')
plt.ylabel('Количество рейсов')
plt.title('Задержки <= 60 минут')
plt.tight_layout()
plt.show()
Шаг 2: Инженерия признаков
Из исторических данных нужно создать информативные признаки:
# Временные признаки
df['DepTime_hour'] = df['DepTime'] // 100
df['DepTime_minute'] = df['DepTime'] % 100
df['DayOfWeek_sin'] = np.sin(2 * np.pi * df['DayOfWeek'] / 7)
df['DayOfWeek_cos'] = np.cos(2 * np.pi * df['DayOfWeek'] / 7)
df['Month_sin'] = np.sin(2 * np.pi * df['Month'] / 12)
df['Month_cos'] = np.cos(2 * np.pi * df['Month'] / 12)
# Признаки по маршруту
df['route_id'] = df['Origin'] + '_' + df['Dest']
# Исторические признаки — средняя задержка по маршруту
route_delays = df.groupby('route_id')['ArrDelay'].mean()
df['route_avg_delay'] = df['route_id'].map(route_delays)
# Исторические признаки — средняя задержка по авиакомпании
airline_delays = df.groupby('Carrier')['ArrDelay'].mean()
df['airline_avg_delay'] = df['Carrier'].map(airline_delays)
# Исторические признаки — средняя задержка по часу вылета
hour_delays = df.groupby('DepTime_hour')['ArrDelay'].mean()
df['hour_avg_delay'] = df['DepTime_hour'].map(hour_delays)
# Признаки расстояния
df['distance_category'] = pd.cut(
df['Distance'],
bins=[0, 500, 1000, 2000, 10000],
labels=['short', 'medium', 'long', 'ultra_long']
)
# Кодирование категорических признаков
df_encoded = pd.get_dummies(
df,
columns=['Origin', 'Dest', 'Carrier', 'distance_category'],
drop_first=True
)
print(f"Количество признаков: {df_encoded.shape[1]}")
Шаг 3: Обработка пропущенных значений
# Удаление рейсов с пропущенной задержкой
df_clean = df_encoded.dropna(subset=['ArrDelay'])
print(f"До удаления пропусков: {len(df_encoded)} строк")
print(f"После удаления пропусков: {len(df_clean)} строк")
# Обработка пропусков в признаках
df_clean = df_clean.fillna(df_clean.median(numeric_only=True))
# Удаление выбросов (опционально)
Q1 = df_clean['ArrDelay'].quantile(0.25)
Q3 = df_clean['ArrDelay'].quantile(0.75)
IQR = Q3 - Q1
df_clean = df_clean[
(df_clean['ArrDelay'] >= Q1 - 1.5 * IQR) &
(df_clean['ArrDelay'] <= Q3 + 1.5 * IQR)
]
print(f"После удаления выбросов: {len(df_clean)} строк")
Шаг 4: Разделение на тренировку и тест
# Выбор признаков и целевой переменной
feature_columns = [col for col in df_clean.columns if col != 'ArrDelay']
X = df_clean[feature_columns]
y = df_clean['ArrDelay']
# Разделение временных рядов (важно для авиа-данных!)
# Последние 20% данных — тест (более новые данные)
test_size = int(len(X) * 0.2)
X_train, X_test = X[:-test_size], X[-test_size:]
y_train, y_test = y[:-test_size], y[-test_size:]
print(f"Тренировка: {len(X_train)} примеров")
print(f"Тест: {len(X_test)} примеров")
# Нормализация
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
Шаг 5: Обучение моделей
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import numpy as np
# Модель 1: Random Forest
rf_model = RandomForestRegressor(
n_estimators=100,
max_depth=20,
min_samples_split=5,
min_samples_leaf=2,
random_state=42,
n_jobs=-1
)
rf_model.fit(X_train_scaled, y_train)
# Модель 2: Gradient Boosting
gb_model = GradientBoostingRegressor(
n_estimators=100,
learning_rate=0.1,
max_depth=5,
min_samples_split=5,
subsample=0.8,
random_state=42
)
gb_model.fit(X_train_scaled, y_train)
# Модель 3: XGBoost (более современный вариант)
import xgboost as xgb
xgb_model = xgb.XGBRegressor(
n_estimators=100,
learning_rate=0.1,
max_depth=5,
subsample=0.8,
random_state=42
)
xgb_model.fit(X_train_scaled, y_train)
print("Все модели обучены")
Шаг 6: Оценка качества
def evaluate_model(model, X_train, X_test, y_train, y_test, name):
"""Оценка качества модели"""
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)
train_mse = mean_squared_error(y_train, y_train_pred)
test_mse = mean_squared_error(y_test, y_test_pred)
train_mae = mean_absolute_error(y_train, y_train_pred)
test_mae = mean_absolute_error(y_test, y_test_pred)
train_r2 = r2_score(y_train, y_train_pred)
test_r2 = r2_score(y_test, y_test_pred)
print(f"\n{name}:")
print(f" Train RMSE: {np.sqrt(train_mse):.2f} мин, MAE: {train_mae:.2f} мин, R²: {train_r2:.3f}")
print(f" Test RMSE: {np.sqrt(test_mse):.2f} мин, MAE: {test_mae:.2f} мин, R²: {test_r2:.3f}")
print(f" Переобучение: {np.sqrt(test_mse) - np.sqrt(train_mse):.2f} мин")
return {
'name': name,
'test_rmse': np.sqrt(test_mse),
'test_mae': test_mae,
'test_r2': test_r2,
'predictions': y_test_pred
}
results = []
results.append(evaluate_model(rf_model, X_train_scaled, X_test_scaled, y_train, y_test, "Random Forest"))
results.append(evaluate_model(gb_model, X_train_scaled, X_test_scaled, y_train, y_test, "Gradient Boosting"))
results.append(evaluate_model(xgb_model, X_train_scaled, X_test_scaled, y_train, y_test, "XGBoost"))
Шаг 7: Ансамбль моделей
# Комбинирование предсказаний
y_pred_ensemble = (
rf_model.predict(X_test_scaled) * 0.3 +
gb_model.predict(X_test_scaled) * 0.4 +
xgb_model.predict(X_test_scaled) * 0.3
)
ensemble_mae = mean_absolute_error(y_test, y_pred_ensemble)
ensemble_rmse = np.sqrt(mean_squared_error(y_test, y_pred_ensemble))
ensemble_r2 = r2_score(y_test, y_pred_ensemble)
print(f"\nЭнсамбль (RF 30% + GB 40% + XGB 30%):")
print(f" Test RMSE: {ensemble_rmse:.2f} мин")
print(f" Test MAE: {ensemble_mae:.2f} мин")
print(f" Test R²: {ensemble_r2:.3f}")
Шаг 8: Интерпретация результатов
# Важность признаков
feature_importance = pd.DataFrame({
'feature': feature_columns,
'importance': xgb_model.feature_importances_
}).sort_values('importance', ascending=False).head(15)
print("\nТоп-15 важных признаков:")
print(feature_importance)
# Анализ ошибок
errors = y_test - y_pred_ensemble
print(f"\nОшибки предсказания:")
print(f" Средняя ошибка: {errors.mean():.2f} мин")
print(f" Стандартное отклонение: {errors.std():.2f} мин")
print(f" Доля предсказаний в пределах 10 мин: {(abs(errors) <= 10).sum() / len(errors) * 100:.1f}%")
print(f" Доля предсказаний в пределах 30 мин: {(abs(errors) <= 30).sum() / len(errors) * 100:.1f}%")
Практическое применение
# Функция для предсказания задержки нового рейса
def predict_flight_delay(departure_hour, airline, origin, dest, distance):
"""Предсказание задержки для конкретного рейса"""
# Создание входного примера
sample = np.zeros((1, len(feature_columns)))
# Заполнить признаки на основе входных данных
# ...
# Нормализация
sample_scaled = scaler.transform(sample)
# Предсказание
delay_pred = xgb_model.predict(sample_scaled)[0]
return max(0, delay_pred) # Задержка не может быть отрицательной
# Пример
predicted_delay = predict_flight_delay(
departure_hour=14,
airline='UA',
origin='JFK',
dest='LAX',
distance=2500
)
print(f"Предсказанная задержка: {predicted_delay:.0f} минут")
Ключевые метрики
- RMSE (Root Mean Squared Error) — среднее квадратическое отклонение
- MAE (Mean Absolute Error) — средняя абсолютная ошибка в минутах (более интерпретируемо)
- R² (коэффициент детерминации) — доля объяснённой дисперсии
- Доля точных предсказаний — процент рейсов, предсказанных в пределах N минут
Итоговые рекомендации
- Используй временное разделение (не random split) для авиа-данных
- Создавай исторические признаки по маршруту и авиакомпании
- Комбинируй несколько моделей для лучших результатов
- Анализируй ошибки по типам рейсов (дневные, ночные, выходные)
- Регулярно переобучай модель с новыми данными
- MAE — лучшая метрика для практического применения