Synthetic control простыми словами

Проверь себя · 1/3разбор после ответа
Нужно выбрать заказы, где delivery_city не равно 'Moscow', но при этом включить строки, где delivery_city = NULL (город неизвестен). Какой фильтр корректен?

Зачем это знать

Запустили маркетинговую кампанию в одном городе — как измерить эффект? DiD требует «похожий» контрольный город, но такого часто нет. Synthetic control строит идеальный синтетический контроль из взвешенной комбинации других городов.

В marketplace, delivery, финтех-аналитике synthetic control — state-of-the-art метод для гео-экспериментов.

Короткое объяснение

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

Синтетический контроль(t) = w1 × City1(t) + w2 × City2(t) + ...

Веса w1, w2, ... минимизируют расхождение в предпериоде.

Пример

Воздействие: Москва получила фичу в январе.

Кандидаты в контроль: Екатеринбург, Казань, Новосибирск.

В предпериоде Екб/Казань/Нск по отдельности не совпадают с Москвой, но комбинация 0.5×Екб + 0.3×Казань + 0.2×Нск идеально повторяет московский тренд.

Пост-период: смотрим разницу Москва vs синтетический контроль = эффект.

Алгоритм

  1. Собрать данные по treatment-юниту и пулу потенциальных контролей
  2. Оптимизировать веса: минимум RMSE в предпериоде между treatment и синтетикой
  3. Применить те же веса к пост-периоду
  4. Разница = treatment effect

Преимущества vs DiD

DiD

  • Требует параллельных трендов
  • Один контроль

Synthetic control

  • Строит идеальный контроль
  • Веса подбираются по предпериоду
  • Лучше работает для одной treatment-единицы

В Python

from syntheticcontrol import SyntheticControl

sc = SyntheticControl(
    df=data,
    unit='city',
    time='date',
    treatment_unit='Moscow',
    treatment_date='2026-01-01',
    outcome='orders'
)
sc.fit()
sc.plot()  # показывает treatment vs synthetic
print(sc.effect)  # numeric estimate

Классический пример из литературы

Налог на сигареты в Калифорнии (1988):

В 1988 году в Калифорнии ввели налог на сигареты. Какой эффект на потребление?

Synthetic control: веса других штатов → построили «синтетическую Калифорнию без налога». Разница = эффект налога.

Работа Abadie et al. (2010) — классика.

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Placebo-тест

Проверка валидности:

Применить synthetic control к не-treated единице. Если видите «эффект» — метод не работает.

Или: применить к treatment в предпериоде, где эффекта быть не должно.

Ограничения

Одна treatment-единица

Классический synthetic control — одна единица. Есть расширения для нескольких.

Нужен длинный предпериод

Иначе веса переобучатся.

Должны быть похожие единицы

Если Москва уникальна и нет похожих городов — синтетика плохо построится.

Расширения

Generalized Synthetic Control

Работает с несколькими treatment-единицами.

Augmented Synthetic Control

Комбинирует с регрессией для лучшей подгонки.

Matrix Completion

ML-подход, веса подбираются автоматически.

Когда использовать

  • Гео-эксперименты с одной treatment-единицей
  • Оценка эффекта policy-изменений
  • Маркетинговые эксперименты на уровне регионов
  • Нельзя провести рандомизацию

На собесе

«Что такое synthetic control?» Построение «идеального контроля» через взвешенное среднее других единиц.

«Отличие от DiD?» DiD — один контроль, SC — взвешенная комбинация.

«Когда использовать?» Одна treatment-единица, нельзя A/B, есть пул похожих контрольных.

«Проверка валидности?» Placebo-тесты на не-treated единицах.

Частые ошибки

Переобучение на предпериоде

Слишком много единиц в пуле → веса подстраиваются под шум.

Короткий предпериод

Веса могут не отражать реальные тренды.

Игнорировать spillover

Если treatment влияет на контроли (утечка эффекта) — SC не работает.

Связанные темы

FAQ

Какие библиотеки?

pysyncon, syntheticcontrol в Python. Synth в R.

Сколько pre-periods?

Минимум 20, желательно 50+.

Веса negative?

В классическом SC — только non-negative, сумма = 1. Augmented — negative можно.