Как найти последнюю покупку по человеку в данных?
Что проверяют
Умеете ли вы решать задачу «последняя запись в группе» сразу для всех пользователей, а не для одного. Это классика на оконные функции и groupby. Следом спрашивают, что будет при двух покупках с одинаковым временем и как решение поведёт себя на сотнях миллионов строк.
Как отвечать
Уточните условия: нужна одна строка на человека или все покупки последнего дня, учитывать ли возвраты и отменённые заказы, в каком часовом поясе хранится время. Потом дайте решение.
В SQL универсальный вариант через окно: ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY purchased_at DESC, purchase_id DESC) в подзапросе и фильтр rn = 1 снаружи. Второй ключ сортировки делает результат детерминированным при совпадении времени, а если нужны все покупки в последний момент, берут RANK(). В PostgreSQL короче: SELECT DISTINCT ON (user_id) * FROM purchases ORDER BY user_id, purchased_at DESC. Если запрос частый, помогает индекс по (user_id, purchased_at DESC).
В pandas три рабочих способа:
idxmax:df.loc[df.groupby('user_id')['purchased_at'].idxmax()], одна строка на пользователя;- сортировка и дубли:
df.sort_values('purchased_at').drop_duplicates('user_id', keep='last'); tail:df.sort_values('purchased_at').groupby('user_id').tail(1).
Перед этим колонку приводят к datetime через pd.to_datetime, иначе даты вида 01.03.2024 отсортируются как текст. В PySpark то же окно: Window.partitionBy('user_id').orderBy(F.desc('purchased_at')) и row_number().
Где спотыкаются
Фильтруют одного пользователя и сортируют, а потом обходят всех в цикле. На миллионе пользователей это миллион проходов по таблице вместо одного.
Считают MAX(purchased_at) по группе и соединяют результат с исходной таблицей. При совпадающем времени строк на человека становится две, и это не всегда замечают.
Пишут GROUP BY user_id и выбирают неагрегированные колонки. MySQL без режима ONLY_FULL_GROUP_BY вернёт значения из произвольной строки группы, а не из последней покупки, и ошибки не будет.