Анализ бронирований и сервисов

Middle Python Логистика

Условие задания

**Задание по мотивам реального тестового в Aviasales.**

**Данные:** CSV с заказами авиабилетов:
[см. код в задании]

**Задание:**
1. Pivot по service: min/max цены, средняя маржа (price - cost), кол-во заказов
2. Топ-5 стран по выручке (price × qty) и по прибыли (profit)
3. Динамика маржинальности по месяцам: растёт или падает?
4. Booking depth (depart_date - book_date): влияет ли на цену? Визуализируйте.

Пример данных

Структура для ориентира — реальные значения из эталонного решения.

import pandas as pd
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

# --- Генерация данных ---
np.random.seed(42)
N_orders = 3000
services = ['flight', 'insurance', 'hotel', 'transfer', 'excursion']
countries = ['Turkey', 'Thailand', 'Egypt', 'UAE', 'Maldives',
             'Georgia', 'Armenia', 'Serbia', 'Montenegro', 'Sri Lanka']

rows = []
for i in range(N_orders):
    order_id = 1000 + i
    country = np.random.choice(countries, p=[0.25, 0.15, 0.15, 0.1, 0.05,
                                              0.1, 0.07, 0.05, 0.04, 0.04])
    book_date = pd.Timestamp('2024-01-01') + pd.Timedelta(days=np.random.randint(0, 180))
    depth = np.random.randint(7, 120)
    depart_date = book_date + pd.Timedelta(days=depth)

    # Каждый заказ = flight + случайные доп. сервисы
    order_services = ['flight'] + list(np.random.choice(
        ['insurance', 'hotel', 'transfer', 'excursion'],
        size=np.random.randint(0, 4), replace=False))

    for svc in order_services:
        qty = np.random.randint(1, 4) if svc == 'flight' else np.random.randint(1, 3)
        if svc == 'flight':
            price = np.random.randint(15000, 80000)
            margin = np.random.uniform(0.05, 0.15)
        elif svc == 'hotel':
            price = np.random.randint(5000, 50000)
            margin = np.random.uniform(0.08, 0.20)
        elif svc == 'insurance':
            price = np.random.randint(500, 3000)
            margin = np.random.uniform(0.50, 0.80)
        elif svc == 'transfer':
            price = np.random.randint(1000, 8000)
            margin = np.random.uniform(0.15, 0.30)
        else:
            price = np.random.randint(2000, 15000)
            margin = np.random.uniform(0.20, 0.40)

        cost = int(price * (1 - margin))
        rows.append({
            'order_id': order_id, 'service': svc, 'country': country,
            'book_date': book_date, 'depart_date': depart_date,
            'price': price, 'cost': cost, 'qty': qty,
        })

df = pd.DataFrame(rows)
df['revenue'] = df['price'] * df['qty']
df['profit'] = (df['price'] - df['cost']) * df['qty']
df['margin_pct'] = (df['profit'] / df['revenue'] * 100).round(2)
df['booking_depth'] = (df['depart_date'] - df['book_date']).dt.days
df['book_month'] = df['book_date'].dt.to_period('M')

print(f"Записей: {len(df):,}, заказов: {df['order_id'].nunique():,}")

# --- 1. Pivot по service ---
pivot = df.groupby('service').agg(
    orders=('order_id', 'nunique'),
    min_price=('price', 'min'),
    max_price=('price', 'max'),
    avg_price=('price', 'mean'),
    total_revenue=('revenue', 'sum'),
    total_profit=('profit', 'sum'),
    avg_margin=('margin_pct', 'mean'),
).round(1)

print("\n=== Сводка по сервисам ===")
print(pivot.sort_values('total_revenue', ascending=False).to_string())

# --- 2. Топ-5 стран ---
by_country = df.groupby('country').agg(
    revenue=('revenue', 'sum'),
    profit=('profit', 'sum'),
    orders=('order_id', 'nunique'),
).assign(margin_pct=lambda x: (x['profit'] / x['revenue'] * 100).round(1))

print("\n=== Топ-5 стран по выручке ===")
print(by_country.nlargest(5, 'revenue').to_string())

print("\n=== Топ-5 стран по прибыли ===")
print(by_country.nlargest(5, 'profit').to_string())

# --- 3. Динамика маржинальности ---
monthly = df.groupby('book_month').agg(
    revenue=('revenue', 'sum'),
    profit=('profit', 'sum'),
).assign(margin_pct=lambda x: (x['profit'] / x['revenue'] * 100).round(2))

print("\n=== Маржинальность по месяцам ===")
print(monthly.to_string())
trend = 'растёт' if monthly['margin_pct'].iloc[-1] > monthly['margin_pct'].iloc[0] else 'падает'
print(f"Тренд: маржинальность {trend}")

# --- 4. Booking depth vs price ---
flights = df[df['service'] == 'flight'].copy()
flights['depth_bin'] = pd.cut(flights['booking_depth'],
                               bins=[0, 14, 30, 60, 90, 150],
                               labels=['<2 нед', '2-4 нед', '1-2 мес', '2-3 мес', '3+ мес'])

depth_analysis = flights.groupby('depth_bin').agg(
    avg_price=('price', 'mean'),
    median_price=('price', 'median'),
    count=('order_id', 'count'),
).round(0)

print("\n=== Booking depth → цена авиабилета ===")
print(depth_analysis.to_string())

fig, ax = plt.subplots(figsize=(10, 5))
ax.scatter(flights['booking_depth'], flights['price'], alpha=0.2, s=10, c='#FF5A1F')
# Тренд
z = np.polyfit(flights['booking_depth'], flights['price'], 1)
x_line = np.linspace(7, 120, 100)
ax.plot(x_line, np.polyval(z, x_line), 'k--', linewidth=2, label=f'Тренд: {z[0]:+.0f} руб./день')
ax.set_xlabel('Booking depth (дней до вылета)')
ax.set_ylabel('Цена билета, руб.')
ax.set_title('Глубина бронирования vs цена')
ax.legend()
plt.tight_layout()
plt.savefig('booking_depth.png', dpi=150)
print("\nГрафик: booking_depth.png")

Темы

python pandas pivot маржинальность Aviasales

Подсказки

Все тестовые задания →

Частые вопросы

Какой уровень знаний нужен для задачи "Анализ бронирований и сервисов"?

Это задание для уровня Middle. Для middle-аналитиков с опытом 1-3 года, требует уверенного владения темой и понимания edge cases.

На каких собеседованиях встречается такая задача?

Подобные задания в категории «Python» регулярно дают на собеседованиях аналитика данных в Яндекс, Сбер, Ozon, Авито, Тинькофф, Wildberries, T-Bank, X5, ВТБ и других крупных IT-компаниях. Тематика: python, pandas, pivot, маржинальность, Aviasales.

Сколько времени даётся на решение?

На реальном собеседовании на подобную задачу отводится 15-30 минут — оцениваются подход, корректность, обработка edge cases. Для тренировки рекомендуем сначала решить самостоятельно, потом сверить с эталонным решением и подсказками.

Где ещё потренироваться по теме «Python»?

На zasqlpython.ru есть 482 Python задачи с проверкой через Pyodide, конспекты Python и pandas, AI мок-собеседование с разбором ваших ответов.

← Все задания