Jupyter-ноутбук для детектирования аномалий в отказах платежей по разрезам страна × метод × день.
Фреймворк сочетает два подхода к детекции аномалий:
-
LOO-бенчмарки
- Leave-One-Out (исключение текущего дня) по стране и по методу.
- Односторонний биномиальный тест.
- Коррекция множественных сравнений с помощью FDR (Benjamini–Hochberg).
-
GLM-базлайн
- Биномиальная логит-модель с предикторами
countryиpayment_method. - Поддерживается регуляризация (L2) или сглаживание Джеффриса.
- Вычисление ожидаемой доли отказов (
p_hat_glm), Z-статистики и q-values.
- Биномиальная логит-модель с предикторами
Результат — таблицы с флагами аномалий и сводные метрики.
- Python ≥ 3.8
- pandas
- numpy
- scipy
- statsmodels
matplotlib (для статических графиков)- plotly (для интерактивных графиков)
Установка зависимостей:
pip install pandas numpy scipy statsmodels matplotlib plotlyОжидаемый формат входных данных — таблица событий с колонками:
| колонка | тип | описание |
|---|---|---|
date |
datetime64[ns] | Дата транзакции |
country |
string | Код страны (например, DE, FR, ES) |
payment_method |
string | Способ оплаты (Visa, PayPal, ApplePay и др.) |
is_problem |
int (0/1) | Индикатор отказа (1 — отказ, 0 — успех) |
Пример данных:
date,country,payment_method,is_problem
2025-07-01,DE,Visa,0
2025-07-01,DE,Visa,1
2025-07-01,FR,PayPal,0
2025-07-02,ES,ApplePay,0
- Агрегация событий в дневные метрики (
n,x,rate). - Расчёт LOO-бенчмарков (
p0_country,p0_method). - Биномиальные тесты + q-values по BH.
- Флаги аномалий по LOO (страна, метод).
- GLM-базлайн (регуляризованный или Jeffreys smoothing).
- Флаги аномалий по GLM.
- Формирование итоговых таблиц:
result— полная таблица со всеми флагами;anomalies_glm— аномалии по GLM;anomalies_looboth— пересечение LOO-анализов;anomalies_any— все аномалии (GLM или LOO).
Для анализа динамики отказов используются интерактивные графики на Plotly.
Функция plot_series строит временной ряд с дополнительными возможностями:
- линия фактической доли отказов (
rate); - базлайны (
p_hat_glm,p0_country,p0_method); - автоматическая подсветка дней, где сработали флаги аномалий (
alert_*); - резюме по последней дате: значения, лифты, ближайший базлайн, количество аномальных дней, q-values.
plot_series(result, country="DE", method="PayPal")# подсветить только дни, где сработал GLM
plot_series(result, "BR", "card_processing", highlight_cols=("alert_glm",))
# подсветить дни по строгому правилу (GLM + оба LOO)
plot_series(result, "BD", "card_processing", highlight_cols=("alert_strict",))
⚠️ Обратите внимание: если указываете один флаг, используйте кортеж или список:
("alert_glm",)или["alert_glm"]— строка"alert_glm"без скобок будет воспринята как набор символов.
Под графиком автоматически выводится текстовое пояснение:
- значения
rate,p_hat_glm,p0_country,p0_methodна последнюю дату; - лифты (отклонения факта от каждого базлайна);
- ближайший к факту базлайн (что «объясняет» данные лучше);
- количество аномальных дней по выбранным флагам;
- q-values и статусы флагов.
Это упрощает интерпретацию: сразу видно, является ли рост отказов проблемой страны, метода или их комбинации.
В ноутбуке сохраняются 4 CSV:
result_full.csv— полная таблица с метриками и флагами.anomalies_glm.csv— аномалии по GLM.anomalies_looboth.csv— аномалии, подтверждённые обоими LOO.anomalies_any.csv— все аномалии.
В ноутбуке предусмотрена генерация синтетических данных:
- базовые вероятности отказа зависят от страны, метода и дня недели;
- искусственно вносятся аномалии (например, PayPal в DE, 10–12 июля).
Это позволяет протестировать пайплайн без реальных данных.
Основные параметры задаются через AnomalyConfig:
alpha_country,alpha_method,alpha_glm— уровни значимости.min_n_pair— минимальное число транзакций за день для проверки.min_delta_*— минимальный лифт (отклонение) для алерта.glm_l2_alpha— сила L2-регуляризации для GLM.use_glm_regularized— использовать ли регуляризацию (True) или Jeffreys smoothing (False).add_interaction— добавить взаимодействиеC(country):C(payment_method).
df_events = (pd.read_csv("events.csv")
.assign(date=lambda d: pd.to_datetime(d['date'])))
cfg = AnomalyConfig(alpha_country=0.05, min_n_pair=200)
result, anomalies_glm, anomalies_looboth, anomalies_any = run_anomaly_pipeline(df_events, cfg)
print(result.head())