**Задание по мотивам реального тестового в 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. Для тренировки рекомендуем сначала решить самостоятельно, потом сверить с эталонным решением и подсказками.
На zasqlpython.ru есть 482 Python задачи с проверкой через Pyodide, конспекты Python и pandas, AI мок-собеседование с разбором ваших ответов.
← Все задания