Часто в аналитике возникает задача: есть история изменения цен (снимки/snapshots), и нужно найти объекты, где цена росла слишком быстро. Вот эта именно такая задачка может попасться на собеседовании.
Дано три таблицы:
listings_df - объявления и их параметры;agents_df - информация об агентах, разместивших объявления;districts - информация о районах🎯 Что нужно сделать?
Для каждого объявления:
- Сравнить цену на каждом снимке с ценой на предыдущем снимке.
- Посчитать процентное изменение цены.
- Найти среднее процентное изменение между последовательными снимками.
- Оставить объявления, у которых среднее изменение превышает 0,5%.
- Округлить результат до двух знаков и отсортировать по убыванию.
Причем важно: учитываем все объявления, даже снятые с публикации. И не нужно искать объявления, у которых цена выросла больше 0,5% хотя бы один раз. Нас интересует именно среднее изменение цены.
Разберем как можно решить эту задачу. 🧩
1️⃣ Самое интересное — функция LAG()
Чтобы сравнить цену объявления с предыдущим снимком, нам нужно как-то получить предыдущую цену в текущей строке.
Для этого в SQL есть оконная функция
lag().Она позволяет посмотреть на значение из предыдущей строки, не объединяя таблицу саму с собой.
Представим, что мы хотим для каждой даты узнать цену на предыдущем снимке.
WITH prev_amount AS (
SELECT
listing_id,
snapshot_date,
price,
LAG(price) OVER (PARTITION BY listing_id ORDER BY snapshot_date ) AS prev
FROM listings_df
)🔹
lag(price) - возьми значение price из предыдущей строки.🔹
partition by listing_id - раздели данные на группы по объявлениям. Для каждого объявления история цен будет рассчитываться отдельно.🔹
order by snapshot_date - расположи снимки в хронологическом порядке. Именно так SQL понимает, какая строка является предыдущей.2️⃣ Считаем средний процент изменения.
Когда предыдущая цена у нас есть, можно посчитать изменение.
SELECT
listing_id,
-- Считаем процент и сразу округляем
ROUND(AVG(100.0 * (price - prev) / NULLIF(prev, 0)), 2) AS avg_change
FROM prev_amount
GROUP BY 1
HAVING avg_change > 0.5
ORDER BY 2 descИспользую
100.0, чтобы вычисление выполнялось с дробной частью.3️⃣Фильтруем и сортируем.
Фильтруем уже агрегированные результаты. Здесь нас интересует среднее по объявлению, а не отдельные строки с изменением цены.
HAVING avg_change > 0.5
ORDER BY 2 desc Сортируем по второму столбцу - среднему изменению - от большего к меньшему.
На мой взгляд, это одни из самых полезных оконных функций для аналитика. С их помощью можно решать задачи на изменение метрик, анализ последовательности событий, поиск пропусков и многое другое.
🗂 В условии были перечислены сразу несколько таблиц:
listings, agents, districts и таблица со снимками цен.Но для решения задачи нам нужна только одна!
💡 Маленький лайфхак: не стоит автоматически делать
join всех таблиц, которые перечислены в условии. Сначала определите, какие поля нужны для решения и откуда их можно получить.🎁 Чтобы вам не пришлось искать данные для практики, я сгенерировала синтетический датасет (на Python) и подготовила Jupyter Notebook. Welcome на тренировку !