Skip to main content

Post Analysis Toolkit

post_analysis_toolkit - локальная библиотека для пост-анализа промо, rollout-ов и других квази-экспериментальных задач, где нужен воспроизводимый causal / quasi-causal пайплайн.

Пакет закрывает типовой цикл:

  1. подготовили панель или временной ряд;
  2. проверили предпосылки метода;
  3. посчитали эффект;
  4. проверили устойчивость результата;
  5. получили табличные результаты и готовые графики.

Документация в этом файле ориентирована на пользователя пакета.

Что умеет пакет

  • классический Difference-in-Differences
  • регрессионный DiD
  • DiD с HC1 robust SE или cluster-robust SE
  • DiD с TWFE
  • DiD с лог-трансформацией outcome
  • DiD с относительной индексацией outcome
  • Synthetic Control
  • Causal Impact
  • Interrupted Time Series
  • Triple Difference
  • Regression Discontinuity Design
  • Propensity Score Matching
  • Doubly Robust / AIPW
  • Event Study с эффектами по временным бинам
  • проверки предпосылок по каждому методу
  • базовые проверки устойчивости
  • встроенные matplotlib-графики

MDE и чувствительность

Для всех MDE по умолчанию используются alpha=0.05, power=0.80 и двусторонний тест. Если метрика и дизайн допускают аналитическую оценку, базовый ориентир — t-test/формула через корректную стандартную ошибку.

MDE относится к конкретному estimand и спецификации. В рабочем результате нужно отдельно показывать абсолютный MDE в единицах метрики, относительный MDE относительно явно указанного baseline и cumulative MDE за весь post-period.

Режим mde_mode="auto" используется по умолчанию: если у метода есть корректная стандартная ошибка, выбирается аналитический расчет, иначе — параметрическая симуляция. mde_mode="parametric" принудительно включает быструю параметрическую симуляцию; прежнее значение mde_mode="simulation" сохранено как его алиас. mde_mode="resampling" включает полноценные симуляции с генерацией данных и повторным запуском оценивателя. Быстрый proxy доступен только явно как mde_mode="proxy". Для симуляций число повторов будет задаваться параметром n_simulations: 500 для отладки, 2000 для рабочего расчета и 5000+ для финального отчета. Вместе с ним нужно сохранять random_state и power curve.

Параметрическая симуляция: для каждого кандидатного эффекта пакет генерирует оценки по схеме delta + Normal(0, uncertainty_scale). Для регрессионных и observational-методов uncertainty_scale — SE. Для Event Study это SE конкретного коэффициента конкретного временного бина, поэтому MDE считается отдельно для каждого бина. Для Synthetic Control/Causal Impact/ITS — pre-fit или validation RMSE, нормированный на размер post-period.

В режиме resampling пакет создает псевдоданные: берет fitted/counterfactual значения без эффекта, добавляет ресэмплированные центрированные остатки и кандидатный эффект, а затем повторно запускает весь поддержанный оцениватель. Для Synthetic Control заново подбираются веса, для PSM заново строится matching, для Causal Impact заново обучается state-space модель, а для ITS заново оценивается регрессия. Такой режим заметно медленнее и требует явно задать разумное n_simulations и, при необходимости, mde_effect_grid.

Power curve можно посмотреть в result.details["mde_power_curve"].

После вызова любой fit_* MDE читается так:

result.summary[["mde_absolute", "mde_relative", "mde_cumulative", "mde_type", "mde_simulation_kind"]]
result.details["mde_power_curve"]

Пример полноценного resampling-MDE для поддержанного метода:

result = fit_synthetic_control(
    panel_df,
    outcome_col="outcome",
    unit_col="unit",
    time_col="sale_date",
    treated_unit="treated_city",
    intervention_time="2026-02-01",
    mde_mode="resampling",
    n_simulations=200,
    mde_effect_grid=[0, 2, 4, 6],
    random_state=42,
)
result.summary[["mde_mode_resolved", "mde_simulation_kind", "mde_absolute"]]

В этой версии для каждого кандидата эффекта создаются псевдоданные и заново вызывается Synthetic Control. Поэтому resampling значительно медленнее параметрического режима. Для нестандартного оценивателя можно передать mde_simulator, который возвращает массив повторных оценок эффекта.

Параметры mde_mode, alpha, power, alternative, n_simulations, random_state, mde_effect_grid и mde_simulator добавлены в функции оценки эффекта. Вызов с mde_mode="proxy" дополнительно заполняет proxy_mde_*; при analytic-, parametric- или resampling-режиме эти legacy-поля остаются пустыми.

8. PSM

Используй, когда:

  • treatment бинарный
  • есть наблюдаемые ковариаты, через которые можно объяснять селекцию в treatment
  • хочется построить matched control на основе propensity score

В текущей версии пакет реализует ATT через nearest-neighbor matching по propensity score.

9. Doubly Robust

Используй, когда:

  • treatment бинарный
  • есть набор ковариат
  • хочется оценку, устойчивую к ошибке в одной из двух моделей: propensity model или outcome model

В текущей версии это AIPW / doubly robust ATE.

Что пакет не делает

  • не строит данные из сырых таблиц
  • не проверяет бизнес-смысл метрик за тебя
  • не знает, был ли contamination доноров
  • не умеет автоматически находить “правильный” метод
  • не снимает необходимость ручной интерпретации

То есть пакет отвечает за оценивание и diagnostics, а не за бизнес-контекст и data engineering.

Быстрый старт

Установка локально

Если работаешь из этого репозитория:

PYTHONPATH=. .venv/bin/python -m pytest tests/post_analysis_toolkit/test_smoke.py -q

Базовый импорт

from post_analysis_toolkit import (
    check_did_assumptions,
    check_doubly_robust_assumptions,
    fit_did_regression,
    fit_event_study,
    fit_doubly_robust,
    fit_synthetic_control,
    fit_causal_impact,
    fit_its,
    fit_psm,
    fit_rdd,
    fit_triple_difference,
)

Как начать, если видишь пакет впервые

Практически всегда работа с пакетом выглядит так:

  1. Подготовь данные в одном из поддерживаемых форматов: панель unit x time, один временной ряд или observational dataset.
  2. Выбери метод под свой дизайн.
  3. Сначала запусти check_*_assumptions(...).
  4. Потом запусти fit_* (...).
  5. Если метод выглядит применимым, запусти run_*_robustness(...).
  6. Посмотри summary, details и figures.

Минимальный шаблон:

diag = check_did_assumptions(...)
display(diag.checks)

result = fit_did_regression(...)
display(result.summary)

robust = run_did_robustness(...)
display(robust.summary)

Особенно важно:

  • подробное описание параметров теперь находится прямо внутри описания каждой функции, а не в отдельном общем словаре;
  • если видишь аргумент вида *_options, это почти всегда значит “перебор нескольких спецификаций”, а не “одна настройка модели”.

Как читать result-объекты

Почти все функции возвращают один из двух dataclass-объектов:

  • DiagnosticResult: результат проверки предпосылок
  • MethodResult: результат расчета эффекта или robustness-check

Обычно внутри есть:

  • summary: компактная итоговая таблица
  • details: более подробные таблицы, например коэффициенты, веса, placebo-результаты
  • figures: готовые matplotlib-графики
  • metadata: технические объекты, конфиги, fit-результаты библиотек

Базовые структуры данных

Panel data

Для DiD, Synthetic Control, Causal Impact ожидается длинная панель, обычно такого вида:

import pandas as pd

panel_df = pd.DataFrame(
    {
        "sale_date": [...],
        "unit": [...],          # город / пиццерия / страна / магазин
        "metric": [...],        # outcome
        "treated_flag": [...],  # 1 для treated, 0 для control
        "post_flag": [...],     # 1 после старта промо, 0 до
    }
)

Минимальные требования:

  • time_col должен парситься в дату
  • outcome_col должен быть числовым
  • treated_col и post_col должны кодироваться как 0/1
  • для Synthetic Control и Causal Impact должен существовать unit_col

Time series data

Для ITS нужен один временной ряд:

ts_df = pd.DataFrame(
    {
        "sale_date": [...],
        "metric": [...],
    }
)

Дополнительно можно передавать внешние фичи:

ts_df["weather_temp"] = ...
ts_df["holiday_flag"] = ...

Observational data

Для PSM и Doubly Robust нужен наблюдательный датасет с бинарным treatment:

obs_df = pd.DataFrame(
    {
        "user_id": [...],
        "treatment_flag": [...],   # 0/1
        "outcome": [...],
        "x1": [...],
        "x2": [...],
        "x3": [...],
    }
)

Минимальные требования:

  • treatment_col бинарный 0/1
  • outcome_col числовой
  • covariates должны быть числовыми или заранее закодированными

RDD data

Для RDD нужен срез наблюдений с running variable:

rdd_df = pd.DataFrame(
    {
        "running": [...],   # расстояние до порога или исходная шкала
        "outcome": [...],
        "x1": [...],        # optional covariates
    }
)

Минимальные требования:

  • running_col числовой
  • известен cutoff
  • по обе стороны cutoff есть наблюдения

Triple Difference data

Для Triple Difference нужна панель с третьим бинарным измерением:

ddd_df = pd.DataFrame(
    {
        "sale_date": [...],
        "unit": [...],
        "treated_flag": [...],
        "post_flag": [...],
        "subgroup_flag": [...],  # 1 = affected subgroup, 0 = internal control subgroup
        "outcome": [...],
    }
)

Полностью готовые минимальные примеры

Ниже четыре примера, которые можно буквально скопировать в ноутбук и запустить как первый smoke-test пакета.

1. End-to-end пример: DiD

import pandas as pd

from post_analysis_toolkit import (
    check_did_assumptions,
    fit_did_regression,
    run_did_robustness,
)

did_df = pd.DataFrame(
    {
        "sale_date": pd.date_range("2026-02-01", periods=12, freq="D").tolist() * 2,
        "unit": ["Batumi"] * 12 + ["Tbilisi"] * 12,
        "metric": [
            10, 11, 10, 12, 11, 10, 15, 16, 15, 17, 16, 15,
            9, 10, 9, 11, 10, 9, 10, 11, 10, 12, 11, 10,
        ],
        "treated_flag": [1] * 12 + [0] * 12,
    }
)
did_df["post_flag"] = (did_df["sale_date"] >= pd.Timestamp("2026-02-07")).astype(int)

did_diag = check_did_assumptions(
    did_df,
    outcome_col="metric",
    time_col="sale_date",
    treated_col="treated_flag",
    post_col="post_flag",
    intervention_time="2026-02-07",
    unit_col="unit",
    cluster_col="unit",
)
print(did_diag.checks)

did_result = fit_did_regression(
    did_df,
    outcome_col="metric",
    time_col="sale_date",
    treated_col="treated_flag",
    post_col="post_flag",
    unit_col="unit",
    cluster_col="unit",
    twfe=True,
)
print(did_result.summary)

did_robust = run_did_robustness(
    did_df,
    outcome_col="metric",
    time_col="sale_date",
    treated_col="treated_flag",
    post_col="post_flag",
    intervention_time="2026-02-07",
    unit_col="unit",
    cluster_col="unit",
)
print(did_robust.summary)

2. End-to-end пример: Synthetic Control

import pandas as pd

from post_analysis_toolkit import (
    check_synthetic_control_assumptions,
    fit_synthetic_control,
    run_synthetic_control_robustness,
)

dates = pd.date_range("2026-02-01", periods=14, freq="D")

sc_df = pd.DataFrame(
    {
        "sale_date": list(dates) * 3,
        "unit": ["Batumi"] * 14 + ["Tbilisi"] * 14 + ["Kutaisi"] * 14,
        "metric": (
            [100, 101, 99, 100, 102, 101, 100, 110, 111, 109, 112, 111, 110, 109]
            + [98, 99, 97, 98, 100, 99, 98, 99, 100, 98, 101, 100, 99, 98]
            + [102, 103, 101, 102, 104, 103, 102, 103, 104, 102, 105, 104, 103, 102]
        ),
    }
)

sc_diag = check_synthetic_control_assumptions(
    sc_df,
    outcome_col="metric",
    unit_col="unit",
    time_col="sale_date",
    treated_unit="Batumi",
    intervention_time="2026-02-08",
)
print(sc_diag.checks)

sc_result = fit_synthetic_control(
    sc_df,
    outcome_col="metric",
    unit_col="unit",
    time_col="sale_date",
    treated_unit="Batumi",
    intervention_time="2026-02-08",
)
print(sc_result.summary)

sc_robust = run_synthetic_control_robustness(
    sc_df,
    outcome_col="metric",
    unit_col="unit",
    time_col="sale_date",
    treated_unit="Batumi",
    intervention_time="2026-02-08",
)
print(sc_robust.summary)

3. End-to-end пример: ITS

import pandas as pd

from post_analysis_toolkit import (
    check_its_assumptions,
    fit_its,
    run_its_robustness,
)

its_df = pd.DataFrame(
    {
        "sale_date": pd.date_range("2026-01-01", periods=20, freq="D"),
        "metric": [
            100, 102, 101, 103, 102, 101, 100, 102, 101, 103,
            108, 110, 109, 111, 110, 109, 108, 110, 109, 111,
        ],
    }
)

its_diag = check_its_assumptions(
    its_df,
    outcome_col="metric",
    time_col="sale_date",
    intervention_time="2026-01-11",
)
print(its_diag.checks)

its_result = fit_its(
    its_df,
    outcome_col="metric",
    time_col="sale_date",
    intervention_time="2026-01-11",
)
print(its_result.summary)

its_robust = run_its_robustness(
    its_df,
    outcome_col="metric",
    time_col="sale_date",
    intervention_time="2026-01-11",
)
print(its_robust.summary)

4. End-to-end пример: PSM

import pandas as pd

from post_analysis_toolkit import (
    check_psm_assumptions,
    fit_psm,
    run_psm_robustness,
)

psm_df = pd.DataFrame(
    {
        "user_id": list(range(1, 13)),
        "treatment_flag": [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
        "outcome": [12, 11, 13, 12, 14, 13, 9, 8, 10, 9, 11, 10],
        "x1": [30, 32, 31, 29, 33, 34, 30, 31, 29, 28, 32, 33],
        "x2": [5, 6, 5, 4, 6, 7, 5, 5, 4, 4, 6, 6],
        "x3": [100, 110, 105, 98, 115, 120, 99, 108, 101, 97, 112, 118],
    }
)

psm_diag = check_psm_assumptions(
    psm_df,
    outcome_col="outcome",
    treatment_col="treatment_flag",
    covariate_cols=["x1", "x2", "x3"],
    caliper=0.2,
)
print(psm_diag.checks)

psm_result = fit_psm(
    psm_df,
    outcome_col="outcome",
    treatment_col="treatment_flag",
    covariate_cols=["x1", "x2", "x3"],
    caliper=0.2,
)
print(psm_result.summary)

psm_robust = run_psm_robustness(
    psm_df,
    outcome_col="outcome",
    treatment_col="treatment_flag",
    covariate_cols=["x1", "x2", "x3"],
    caliper_options=(None, 0.1, 0.2),
    neighbor_options=(1, 2),
)
print(psm_robust.summary)

Что возвращают функции

У пакета два основных result-объекта:

  • MethodResult
  • DiagnosticResult

Оба описаны в contracts.py

MethodResult

Поля:

  • method - имя метода
  • summary - главный итоговый DataFrame
  • details - словарь с дополнительными таблицами
  • figures - словарь matplotlib-фигур
  • metadata - служебные объекты, fit-модели, выбранные доноры и т.д.

DiagnosticResult

Поля:

  • method - имя диагностики
  • checks - таблица проверок предпосылок
  • details - вспомогательные таблицы
  • figures - диагностические графики
  • metadata - служебная информация

Как обычно с этим работать

diag = check_did_assumptions(...)
display(diag.checks)

result = fit_did_regression(...)
display(result.summary)
display(result.details["coefficients"])

fig = result.figures["actual_vs_counterfactual"]
display(fig)

Рекомендуемый workflow

Для любого метода лучше идти одинаково:

  1. Проверить, что метрика и grain корректны.
  2. Проверить предпосылки через check_*_assumptions.
  3. Посчитать основной эффект через fit_*.
  4. Прогнать run_*_robustness.
  5. Уже после этого писать вывод.

Минимальный шаблон:

diag = check_did_assumptions(...)
display(diag.checks)

result = fit_did_regression(...)
display(result.summary)

robust = run_did_robustness(...)
display(robust.summary)

DiD: пример

Ожидаемый формат данных

Пример для unit x day:

sale_date unit metric treated_flag post_flag
2026-02-10 Batumi 0.084 1 0
2026-02-10 Tbilisi 0.121 0 0
2026-02-11 Batumi 0.079 1 0
2026-02-25 Batumi 0.116 1 1
2026-02-25 Tbilisi 0.118 0 1

Обязательные колонки:

  • sale_date
  • unit
  • metric
  • treated_flag
  • post_flag

Опционально:

  • ковариаты
  • веса
  • колонка для кластеризации ошибок

1. Проверка предпосылок

from post_analysis_toolkit import check_did_assumptions

did_diag = check_did_assumptions(
    df=panel_df,
    outcome_col="metric",
    time_col="sale_date",
    treated_col="treated_flag",
    post_col="post_flag",
    intervention_time="2026-02-25",
    unit_col="unit",
    cluster_col="unit",
    event_bin_size=7,
)

Что смотреть:

  • строку Корреляция treated/control на предпериоде
  • строку Разница наклонов тренда на предпериоде
  • строку p-value разницы наклонов на предпериоде
  • строку p-value joint-теста лидов в event study
  • графики pretrend_triptych и event_study

2. Оценка эффекта

from post_analysis_toolkit import fit_did_regression

did_result = fit_did_regression(
    df=panel_df,
    outcome_col="metric",
    time_col="sale_date",
    treated_col="treated_flag",
    post_col="post_flag",
    unit_col="unit",
    cluster_col="unit",
    twfe=True,
    log_transform=False,
    relative=False,
)

Главные поля в summary:

  • estimate
  • p_value
  • ci_low, ci_high
  • relative_effect_vs_treated_pre
  • mde_absolute, mde_relative, mde_cumulative, mde_type
  • details["mde_power_curve"]

Для DiD режим auto по умолчанию считает MDE аналитически через SE interaction- оценки. Параметрический режим можно включить через mde_mode="parametric", а полноценный повторный запуск регрессии — через mde_mode="resampling". При кластеризации ориентируйся также на stderr_type.

3. Robustness

from post_analysis_toolkit import run_did_robustness

did_robust = run_did_robustness(
    df=panel_df,
    outcome_col="metric",
    time_col="sale_date",
    treated_col="treated_flag",
    post_col="post_flag",
    unit_col="unit",
    cluster_col="unit",
)

Это прогоняет набор спецификаций:

  • с TWFE и без
  • с логами и без
  • с относительной индексацией и без

Synthetic Control: пример

Ожидаемый формат данных

Пример для unit x day с одним treated unit и несколькими донорами:

sale_date unit metric
2026-02-10 Batumi 0.084
2026-02-10 Tbilisi 0.121
2026-02-10 Kutaisi 0.097
2026-02-11 Batumi 0.079
2026-02-11 Tbilisi 0.120

Обязательные колонки:

  • sale_date
  • unit
  • metric

Что важно:

  • одна строка на один unit в одну дату
  • donor units не должны быть затронуты treatment
  • metric должна быть сопоставима между unit-ами
from post_analysis_toolkit import (
    check_synthetic_control_assumptions,
    fit_synthetic_control,
    run_synthetic_control_robustness,
)

synth_diag = check_synthetic_control_assumptions(
    df=panel_df,
    outcome_col="metric",
    unit_col="unit",
    time_col="sale_date",
    treated_unit="Batumi",
    intervention_time="2026-02-25",
    donor_units=["Tbilisi", "Kutaisi"],
)

synth_result = fit_synthetic_control(
    df=panel_df,
    outcome_col="metric",
    unit_col="unit",
    time_col="sale_date",
    treated_unit="Batumi",
    intervention_time="2026-02-25",
    donor_units=["Tbilisi", "Kutaisi"],
)

synth_robust = run_synthetic_control_robustness(
    df=panel_df,
    outcome_col="metric",
    unit_col="unit",
    time_col="sale_date",
    treated_unit="Batumi",
    intervention_time="2026-02-25",
    donor_units=["Tbilisi", "Kutaisi"],
)

Что смотреть:

  • donor_count
  • pre_rmse
  • pre_r2
  • pre_corr
  • avg_post_effect
  • avg_post_relative_effect
  • таблицу весов details["weights"]

Проверки устойчивости:

  • placebo по каждому донору
  • leave-one-out по донорам

Causal Impact: пример

Ожидаемый формат данных

Формат на вход такой же, как у Synthetic Control.

Пример:

sale_date unit metric
2026-02-10 Batumi 0.084
2026-02-10 Tbilisi 0.121
2026-02-10 Kutaisi 0.097
2026-02-11 Batumi 0.079
2026-02-11 Tbilisi 0.120

Обязательные колонки:

  • sale_date
  • unit
  • metric

Что важно:

  • treated unit один
  • pre-period должен быть достаточно длинным
  • после выравнивания treated и donor рядов не должно быть пропусков
from post_analysis_toolkit import (
    check_causal_impact_assumptions,
    fit_causal_impact,
    run_causal_impact_robustness,
)

ci_diag = check_causal_impact_assumptions(
    df=panel_df,
    outcome_col="metric",
    unit_col="unit",
    time_col="sale_date",
    treated_unit="Batumi",
    intervention_time="2026-02-25",
    donor_units=["Tbilisi", "Kutaisi"],
)

ci_result = fit_causal_impact(
    df=panel_df,
    outcome_col="metric",
    unit_col="unit",
    time_col="sale_date",
    treated_unit="Batumi",
    intervention_time="2026-02-25",
    donor_units=["Tbilisi", "Kutaisi"],
)

ci_robust = run_causal_impact_robustness(
    df=panel_df,
    outcome_col="metric",
    unit_col="unit",
    time_col="sale_date",
    treated_unit="Batumi",
    intervention_time="2026-02-25",
    donor_units=["Tbilisi", "Kutaisi"],
)

Что смотреть:

  • строку R2 на валидации
  • строку RMSE на валидации
  • average_abs_effect
  • average_rel_effect
  • average_abs_effect_lower, average_abs_effect_upper
  • posterior_tail_area
  • mde_absolute, mde_relative, mde_cumulative, mde_type
  • details["mde_power_curve"]

Для Causal Impact simulation-режим много раз получает случайную оценку с масштабом, равным RMSE на валидации. Это еще не повторный запуск state-space pipeline для каждого pseudo-dataset; такой callback остается отдельным расширением.

Проверки устойчивости:

  • placebo по более ранним датам
  • leave-one-out по донорам

ITS: пример

Ожидаемый формат данных

Базовый пример для одного временного ряда:

sale_date metric
2026-01-01 125.0
2026-01-02 118.0
2026-01-03 133.0
2026-02-25 149.0
2026-02-26 152.0

Если есть дополнительные регрессоры:

sale_date metric weather_temp holiday_flag
2026-01-01 125.0 8.3 1
2026-01-02 118.0 7.9 0
2026-02-25 149.0 10.4 0

Обязательные колонки:

  • sale_date
  • metric

Что важно:

  • одна строка на одну дату
  • доп. фичи должны быть на том же дневном grain
  • ряд должен быть числовым и упорядочиваемым по времени
from post_analysis_toolkit import (
    build_standard_its_features,
    check_its_assumptions,
    fit_its,
    run_its_robustness,
)

its_diag = check_its_assumptions(
    df=ts_df,
    outcome_col="metric",
    time_col="sale_date",
    intervention_time="2026-02-25",
)

its_result = fit_its(
    df=ts_df,
    outcome_col="metric",
    time_col="sale_date",
    intervention_time="2026-02-25",
)

its_robust = run_its_robustness(
    df=ts_df,
    outcome_col="metric",
    time_col="sale_date",
    intervention_time="2026-02-25",
)

По умолчанию ITS использует:

  • time_index
  • post_flag
  • time_after_intervention
  • day-of-week dummies
  • month_start_flag
  • month_end_flag

Можно добавить дополнительные фичи:

its_result = fit_its(
    df=ts_df,
    outcome_col="metric",
    time_col="sale_date",
    intervention_time="2026-02-25",
    extra_feature_cols=["weather_temp", "holiday_flag"],
)

Что смотреть:

  • avg_abs_effect
  • avg_rel_effect
  • level_change_coef
  • slope_change_coef_per_day
  • строку R2 на валидации
  • строку RMSE на валидации
  • строку Средний rolling R2 на валидации
  • mde_absolute, mde_relative, mde_cumulative, mde_type
  • details["mde_power_curve"]

Для ITS simulation-режим много раз получает случайную оценку с масштабом, равным RMSE на валидации. Он относится к выбранному агрегированному постпериодному estimand и не является отдельным MDE для level и slope.

Проверки устойчивости:

  • разные длины pre-window
  • placebo intervention dates
  • разные feature sets

Стандартные проверки предпосылок

Во всех checks-таблицах пакет теперь возвращает русские колонки:

  • проверка
  • значение
  • ориентир

Для DiD

  • визуальная параллельность трендов
  • pre-period correlation
  • slope gap на pre-period
  • joint pre-trend test через event study
  • отсутствие параллельных изменений

Для Synthetic Control

  • достаточно доноров
  • хороший pre-fit
  • отсутствие contamination доноров
  • reasonable donor weights

Для Causal Impact

  • достаточно длинный pre-period
  • нормальное predictive quality на pre-period
  • отсутствие пропусков после выравнивания
  • отсутствие contamination контрольных рядов

Для ITS

  • достаточно длинный pre-period
  • нет сильных дырок в датах
  • модель более-менее объясняет pre-period
  • residual diagnostics не выглядят катастрофически
  • нет явных concurrent changes

Интерпретация результатов

Пакет возвращает числа, но не ставит автоматический ярлык “causal effect proven”.

Практически удобно мыслить так:

  • если assumptions слабые, а эффект красивый - не переоценивай вывод
  • если assumptions хорошие, но MDE огромный - отсутствие значимости мало что значит
  • если robustness разваливается - основной результат надо считать хрупким
  • если control-конструкция сомнительна - лучше честный descriptive / benchmark framing

Типичные ошибки

1. Неправильный grain

Например:

  • treated на уровне города
  • а outcome собран на уровне товара x день без аккуратной агрегации

Сначала выровняй unit анализа.

2. Плохой pre-period

Если pre-period слишком короткий, то:

  • ITS будет слабым
  • Causal Impact будет плохо предсказывать
  • Synthetic Control будет переобучаться

3. Перепутан post_flag

Проверь, что:

  • до даты intervention post_flag = 0
  • начиная с даты intervention post_flag = 1

4. Causal-язык без проверок

Нельзя говорить “акция вызвала рост”, если:

  • assumptions не прошли
  • donor pool слабый
  • robustness разваливается

Рекомендации по использованию в пост-анализах

  • Для продаж с нормальным donor pool начинай с Synthetic Control или Causal Impact
  • Для benchmark-сравнения treated vs control держи DiD
  • Если контроль надежно собрать не удалось, переходи к ITS
  • Если даже ITS слабый, оставляй descriptive analysis

Где смотреть готовые примеры

Публичный API

Пакет экспортирует:

  • compute_classic_did
  • fit_did_regression
  • fit_event_study
  • check_did_assumptions
  • run_did_robustness
  • fit_triple_difference
  • check_triple_difference_assumptions
  • run_triple_difference_robustness
  • fit_rdd
  • check_rdd_assumptions
  • run_rdd_robustness
  • fit_psm
  • check_psm_assumptions
  • run_psm_robustness
  • fit_doubly_robust
  • check_doubly_robust_assumptions
  • run_doubly_robust_robustness
  • fit_synthetic_control
  • check_synthetic_control_assumptions
  • run_synthetic_control_robustness
  • fit_causal_impact
  • check_causal_impact_assumptions
  • run_causal_impact_robustness
  • build_standard_its_features
  • fit_its
  • check_its_assumptions
  • run_its_robustness

Ограничения текущей версии

  • Synthetic Control здесь реализован в облегченной форме, без сложного набора pre-treatment predictors
  • Causal Impact зависит от сторонней библиотеки и требует аккуратного окружения
  • MDE по умолчанию выбирает аналитический расчет при наличии SE, а для методов без аналитической формы использует параметрическую симуляцию; режим resampling переобучает поддержанный расчет на каждом повторе
  • contamination и concurrent changes проверяются вручную, а не автоматически

Версия

Текущая версия пакета: 1.0.1

Metadata

Release files for post-analysis-toolkit 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for post-analysis-toolkit 1.0.1
File Size Uploaded
post_analysis_toolkit-1.0.1.tar.gz 96.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for post-analysis-toolkit 1.0.1
File Interpreter ABI Platform
post_analysis_toolkit-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 198.1 kB

Release files / post_analysis_toolkit-1.0.1.tar.gz

Download URL post_analysis_toolkit-1.0.1.tar.gz
Size 96.7 kB
Tags Source
SHA-256 checksum
How to use checksums
441f62213b17abe778e085adf3e10cf43e9e84d18745cd866966d835fb03be80
BLAKE2b-256 checksum
How to use checksums
cdb3e3430127dbdd132f83f11969fa4b0e57c36403df08f46afea40c8f6fc69c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.

Transparency log

Release files / post_analysis_toolkit-1.0.1-py3-none-any.whl

Download URL post_analysis_toolkit-1.0.1-py3-none-any.whl
Size 101.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6bce3083ac60084318c1eba61c5ca088dde8eed991aadcedacfdd9967fa5ff86
BLAKE2b-256 checksum
How to use checksums
79b9602ca5316e785bdc8602e55074724519f125c12cf82c907be43fb228606e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 18, 2026.

Transparency log

Release history Release notifications | RSS feed

1.0.9

2 release files

1.0.8

2 release files

1.0.7

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

This release

1.0.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page