TGViewer
Мир аналитика данных Мир аналитика данных @analysts_world · 4.56K subscribers
Post #327 1.65K
Я про вас не забыла и вот принесла кое-что интересное. Телегу замедляют, но я остаюсь тут и не хочу предлагать вам куда-то переходить. Поддержите огонечками, что вы тут и вам это по прежнему нужно!

Сегодня хочу показать удобный SQL-прием для A/B тестов: как смотреть метрику сразу по нескольким двухнедельным периодам и не размечать каждый интервал руками. Так легче заметить, метрика стабильна или скачет от периода к периоду.

В этом примере считаем такую метрику: доля пользователей, которые открыли карточку товара из целевого блока, среди всех пользователей, открывших карточку товара за период.
Логика очень простая:
➡️ берем всех уникальных пользователей, открывших карточку товара
➡️ отдельно считаем тех, кто пришел в карточку из блока Подборка для вас
➡️ делим второе на первое и получаем долю

Сгенерировала df_product_events(event_ts, user_id, event_name, event_payload).
import duckdb
import pandas as pd

query = """
with events as (
select
date_add(
date '2026-02-02',
INTERVAL (cast(14 * floor(date_diff('day', date '2026-02-02', cast(event_ts as date)) / 14.0) as integer)) DAY) as period_start,
user_id,
json_extract_string(event_payload, '$.source_block') as source_block
from df_product_events
where event_ts >= timestamp '2026-02-02 00:00:00'
and event_ts < timestamp '2026-03-16 00:00:00'
and event_name = 'product_card_open'
and nullif(trim(user_id), '') is not null
)
select
period_start,
period_start + INTERVAL 13 DAY as period_end,
count(distinct user_id) as users_total,
count(distinct case when source_block = 'Подборка для вас' then user_id end) as users_from_target_block,
round(
100.0 * count(distinct case when source_block = 'Подборка для вас' then user_id end)
/ count(distinct user_id),
2
) as share_from_target_block
from events
group by 1, 2
order by 1
"""
result = duckdb.query(query).to_df()


Самая важная часть здесь - это расчет period_start (каждому событию назначается начало его двухнедельного периода)

Что здесь происходит:
1️⃣Берем опорную дату, с которой хотим начать нарезку периодов
2️⃣Через date_diff считаем, сколько дней прошло от нее до события
3️⃣ Делим это число на 14.0, чтобы понять, в какой двухнедельный интервал попало событие.
4️⃣Через floor округляем вниз, чтобы получить номер двухнедельного интервала
5️⃣ потом умножаем на 14 и прибавляем к опорной дате через date_add
В итоге каждое событие автоматически попадает в нужное окно.

Например, если опорная дата - 2026-02-02, то:
событие от 2026-02-03 попадет в период с началом 2026-02-02
событие от 2026-02-14 тоже попадет туда же
а событие от 2026-02-16 уже уйдет в следующий период

Дальше все обычно: по каждому периоду считаем всех уникальных пользователей карточки товара, отдельно - пользователей из целевого блока, и получаем долю.

Мне нравится этот способ тем, что он очень легко масштабируется. Не нужно руками прописывать периоды через case when, не нужно собирать отдельные запросы под каждое окно. Один раз задали опорную дату и длину периода - и дальше запрос сам аккуратно раскладывает события как надо.👍

Если нужно смотреть метрику не по 14 дням, а, например, по трем неделям, меняется одно число и все. Удобно же!

В ноутбуке я еще отдельно оформила формулировку метрики в виде плашки зеленого цвета - так что можно заглянуть не только за SQL, но и за кодом такой вставки. Отделять метрику от метрики в огромных скриптах очень удобно!
  • 🔥 40
  • ❤‍🔥 26
  • ❤ 4
More from @analysts_world
  1. Sep 21, 2026📊 Задачка с собеседования Ну что, по итогам голосования большинство хотят задачки и sql.…
  2. Sep 14, 2026Post #345
  3. Sep 14, 2026Что-то я тут прям зачастила с A/B тестами 😅 Смотрю на последние посты и такое чувство, чт…
  4. Sep 1, 2026🎒 С 1 сентября, друзья! Сегодня как раз отправила своих детей в школу – и вот это чувство…
  5. Aug 24, 2026Вне выборки Обычно здесь про SQL, Python и AB-тесты. Но не всё, что важно, попадает в выбо…
  6. Aug 20, 2026Fuckup Night от создателей Trisigma, Ares и karpov.courses Согласитесь, ивенты, где все де…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →