Условие
Дан датафрейм orders с колонками user_id, order_id, amount, city, ts. Нужно за один проход получить по каждому пользователю: число заказов, средний чек, медианный чек, сумму, дату последнего заказа и число уникальных городов. Колонки должны называться n_orders, mean_check, median_check, total, last_ts, n_cities.
Решение
Подход
groupby(...).agg(...) через named aggregation (синтаксис new_col=('source_col', 'func')). Это рекомендованный с pandas 0.25+ способ — он сразу даёт плоский индекс колонок без MultiIndex.
Реализация
import pandas as pd
result = (
orders
.groupby('user_id', as_index=False)
.agg(
n_orders=('order_id', 'nunique'),
mean_check=('amount', 'mean'),
median_check=('amount', 'median'),
total=('amount', 'sum'),
last_ts=('ts', 'max'),
n_cities=('city', 'nunique'),
)
)Если нужна кастомная функция, передаём lambda:
.agg(p95=('amount', lambda s: s.quantile(0.95)))Подводные камни
- Если использовать словарь
agg({'amount': ['mean','sum']})— получитсяMultiIndexколонок, и его придётся выравниватьresult.columns = ['_'.join(c) for c in result.columns]. nuniqueпоorder_idважно, если строки задвоены (например, после join-а с товарами);countпосчитает позиции, а не уникальные заказы.as_index=Falseчтобыuser_idостался колонкой, а не индексом.- NaN в
amountмолча игнорируютсяmean/median/sum— если важно, заранееdropnaилиfillna(0). - Для строковых дат
maxдаст лексикографический максимум, а не хронологический —pd.to_datetimeобязателен.
Эталонный ответ
groupby('user_id').agg(n_orders=('order_id','nunique'), mean_check=('amount','mean'), ...) — named aggregation даёт плоские колонки за один проход.