Условие
Дан CSV booking_analytics_test_df.csv с заказами и сопутствующими услугами Aviasales (страховки, изменения, уведомления и т. д.). Поля:
order_id— уникальный идентификатор заказа,order_price— цена заказа,service— название услуги,service_price— цена услуги,service_profit— профит от услуги,origin_country,destination_country— страны вылета и прилёта,order_platform— платформа (desktop,ios,android),booking_depth— глубина бронирования (дней между покупкой и поездкой),is_return—0в одну сторону,1туда-обратно,adults,children— пассажиры в заказе,order_date— дата покупки.
Задание:
- С помощью
pandasсоберите таблицу: в строках — наименование услуги, в столбцах — минимальная и максимальная цена услуги, минимальный и максимальный профит от услуги, количество заказов с услугой. - Соберите таблицу: в строках — услуга, в столбцах — цена и профит услуг по топ-5 странам вылета по продажам билетов.
Структура данных
CSV ~10–20k строк. Пример:
order_id,order_price,service,service_price,service_profit,origin_country,...,order_date
KPD24O68HE2O,6090.0,changes,499.00,399.20,RU,RU,desktop,15.43,0,1.0,0,2024-01-05
KRR4DN4PG7OG,18280.0,notification,299.00,271.77,AM,TR,desktop,32.42,0,2.0,0,2024-01-17
Один order_id обычно с одной услугой; но возможно повторение — стоит проверить.
Решение
Подход
Чистый Pandas groupby + agg + pivot_table. Главная тонкость — «количество заказов» vs «строк», и фильтрация на топ-5 стран.
Реализация
import pandas as pd
df = pd.read_csv("booking_analytics_test_df.csv", parse_dates=["order_date"])
print(df.shape)
print(df.head())
# Sanity: есть ли повторяющиеся order_id?
print("order_id unique:", df["order_id"].nunique(), "rows:", len(df))Вопрос 1. Сводная по услуге
q1 = df.groupby("service").agg(
min_service_price=("service_price", "min"),
max_service_price=("service_price", "max"),
min_service_profit=("service_profit", "min"),
max_service_profit=("service_profit", "max"),
orders_count=("order_id", "nunique"),
).round(2)
print(q1.sort_values("orders_count", ascending=False))nunique — на случай дубликатов order_id (один заказ + одна услуга может прилететь дважды из ETL).
Вопрос 2. Цена и профит по топ-5 origin_country
# Шаг 1. Топ-5 стран по числу заказов
top5_origin = (
df.groupby("origin_country")["order_id"]
.nunique()
.sort_values(ascending=False)
.head(5)
.index
.tolist()
)
print("Top-5 origin:", top5_origin)
# Шаг 2. Фильтр + pivot
df_top = df[df["origin_country"].isin(top5_origin)]
q2 = df_top.pivot_table(
index="service",
columns="origin_country",
values=["service_price", "service_profit"],
aggfunc="mean",
).round(2)
print(q2)Для compact-вывода стоит упорядочить столбцы:
q2 = q2.reindex(columns=top5_origin, level="origin_country")Проверка
# Контрольная сумма: сумма orders_count в q1 ≈ числу строк (если по 1 услуге на заказ)
assert q1["orders_count"].sum() <= df["order_id"].nunique() * 5 # услуг немного
# В q2 не должно быть стран вне top5
assert set(q2.columns.get_level_values("origin_country")) == set(top5_origin)Подводные камни
countvsnunique. Еслиorder_idдублируется (несколько строк на один заказ),countпосчитает строки, а нам нужны уникальные заказы. Использоватьnunique.- «Топ-5 стран по продажам билетов» — что это? Чаще всего «по числу заказов». Но может быть «по выручке» (
order_price.sum()) — уточнить. У нас взяли по числу заказов; если интервью требует выручку — заменить. - Услуга с одной строкой — даст
min == max == price. Если в задании важна вариативность — добавить колонкуn_observations. pivot_table(aggfunc="mean")или"sum"? В вопросе 2 говорится «цена и профит услуг» — двусмысленно. Я взялmean(логично, цена услуги — характеристика); если нужно «общая выручка» —sum.origin_country = NA. Если в данных есть пропуски — они попадут вgroupbyкак отдельная категория;dropna=Falseили явный фильтр.serviceс пропусками. Заказы без услуг могут идти какservice = NaN. Удалите или вынесите в отдельную строку.- Округление. Цены и профит — обычно 2 знака. Не выводите
123.456789012— это «грязный» отчёт.
Альтернативы
Можно сделать через crosstab или pivot_table с margins=True для добавления «Итого» по столбцам/строкам — для слайда руководству удобно.
df.pivot_table(
index="service", columns="origin_country",
values="service_price", aggfunc="mean", margins=True, margins_name="Total"
)Эталонный ответ
# Q1
q1 = df.groupby("service").agg(
min_service_price=("service_price", "min"),
max_service_price=("service_price", "max"),
min_service_profit=("service_profit", "min"),
max_service_profit=("service_profit", "max"),
orders_count=("order_id", "nunique"),
)
# Q2
top5 = df.groupby("origin_country")["order_id"].nunique().nlargest(5).index
q2 = (df[df["origin_country"].isin(top5)]
.pivot_table(index="service", columns="origin_country",
values=["service_price", "service_profit"], aggfunc="mean"))Главные акценты — nunique для заказов и фильтрация на топ-5 до pivot.