honestab 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
honestab-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sofya Babkina
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,258 @@
1
+ Metadata-Version: 2.4
2
+ Name: honestab
3
+ Version: 0.1.0
4
+ Summary: A/B test analysis with methods verified by simulation: CUPED, delta method, sequential testing, SRM
5
+ Author: Sofya Babkina
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/sofetizm/honestab
8
+ Keywords: ab-testing,experimentation,statistics,cuped,delta-method,sequential-testing
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
12
+ Classifier: Intended Audience :: Science/Research
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: numpy>=1.24
17
+ Requires-Dist: scipy>=1.10
18
+ Requires-Dist: pandas>=2.0
19
+ Requires-Dist: pyyaml>=6
20
+ Provides-Extra: plots
21
+ Requires-Dist: matplotlib>=3.7; extra == "plots"
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8; extra == "dev"
24
+ Requires-Dist: ruff>=0.6; extra == "dev"
25
+ Requires-Dist: statsmodels>=0.14; extra == "dev"
26
+ Requires-Dist: matplotlib>=3.7; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # honestab — A/B-тесты без самообмана
30
+
31
+ [![CI](https://github.com/sofetizm/honestab/actions/workflows/ci.yml/badge.svg)](https://github.com/sofetizm/honestab/actions/workflows/ci.yml)
32
+ [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/sofetizm/honestab/blob/main/notebooks/demo.ipynb)
33
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
34
+ ![License](https://img.shields.io/badge/license-MIT-green)
35
+
36
+ Python-библиотека для анализа A/B-тестов, в которой **каждый метод проверен симуляциями**.
37
+ Она проверяет SRM, применяет CUPED, считает ratio-метрики дельта-методом, делает поправку
38
+ на множественность и поддерживает последовательное тестирование. На выходе — готовое
39
+ решение: катить, не катить или не доверять результатам. В комплекте навык для ИИ-агентов
40
+ (Claude Code, Codex CLI и др.), который анализирует эксперименты с помощью этой библиотеки.
41
+
42
+ ## Почему «честный»
43
+
44
+ Корректный статистический метод в A/A-тесте (эффекта нет) должен находить «эффект» в 5%
45
+ случаев — это и есть уровень значимости α = 0.05. Типичные ошибки аналитики ломают это
46
+ обещание. Числа ниже — из тысяч симулированных экспериментов ([`validation/`](validation/VALIDATION.md)):
47
+
48
+ | Ошибка | Что получается | Как надо (honestab) |
49
+ |---|---|---|
50
+ | Смотреть на p-value каждый день и остановиться, как только p < 0.05 | **24,5%** ложных срабатываний при 20 проверках | mSPRT: 1,2% |
51
+ | Считать CTR по таблице просмотров | **9,8%** ложных срабатываний | дельта-метод: 4,8% |
52
+ | Проверить 10 метрик и порадоваться значимой | **39,6%** шанс хотя бы одной ложной находки | поправка Holm: 4,8% |
53
+ | Не заметить, что в одной группе потерялась часть пользователей | «Эффект» в **99,8%** A/A-тестов | проверка SRM ловит 83,8% и блокирует решение |
54
+ | Не использовать данные до эксперимента | мощность 57,5% | CUPED: 75,3% при тех же 40 000 пользователях |
55
+
56
+ ![Подглядывание](validation/peeking.png)
57
+
58
+ ## Быстрый старт
59
+
60
+ ```bash
61
+ pip install honestab
62
+ ```
63
+
64
+ ```python
65
+ import honestab as hb
66
+
67
+ report = hb.analyze(df, [ # df: одна строка на пользователя, колонка group = A/B
68
+ hb.Metric("выручка", column="revenue", covariate="pre_revenue", role="primary"),
69
+ hb.Metric("конверсия", column="converted", kind="proportion"),
70
+ hb.Metric("CTR", numerator="clicks", denominator="views"),
71
+ hb.Metric("сессии", column="sessions", role="guardrail"),
72
+ ])
73
+ print(report.to_markdown())
74
+ ```
75
+
76
+ Результат:
77
+
78
+ > **Решение: КАТИТЬ**
79
+
80
+ - Основная метрика «выручка» значимо улучшилась: +11.73% (95% ДИ +5.31% … +18.56%).
81
+
82
+ **Проверка SRM:** пройдена — размеры групп [29957, 30043], ожидалось [30000, 30000], p = 0.726
83
+
84
+ | Метрика | Роль | Метод | Контроль | Тест | Эффект | 95% ДИ | p | p (с поправкой) |
85
+ |---|---|---|---|---|---|---|---|---|
86
+ | выручка ✱ | primary | cuped | 279.1 | 311.9 | +11.73% | [+5.31%; +18.56%] | 0.000249 | — |
87
+ | конверсия | secondary | proportions | 0.1064 | 0.1084 | +1.90% | [-2.69%; +6.72%] | 0.423 | 0.423 |
88
+ | CTR ✱ | secondary | ratio (delta method) | 0.06723 | 0.06935 | +3.16% | [+0.73%; +5.65%] | 0.0106 | 0.0211 |
89
+ | сессии | guardrail | welch | 3.508 | 3.491 | -0.49% | [-1.56%; +0.59%] | 0.368 | — |
90
+
91
+ ✱ — статистически значимо при α = 0.05 (вторичные метрики — с поправкой holm).
92
+
93
+ Примечания:
94
+ - выручка: theta = 0.1842; corr(y, x) = 0.566; variance reduced by ~32%
95
+
96
+ Что происходит внутри одного вызова:
97
+ 1. **Проверка SRM**: если размеры групп не соответствуют плану, решение — «не доверять», и
98
+ метрики не интерпретируются.
99
+ 2. **Метод выбирается по типу метрики**: Welch, CUPED (если есть ковариата до эксперимента),
100
+ z-тест для долей, дельта-метод для отношений, бутстреп для медиан.
101
+ 3. **Поправка Holm** для вторичных метрик.
102
+ 4. **Guardrail-метрики** блокируют выкатку, если значимо ухудшились.
103
+ 5. **Решение с объяснением.** Если эффект не найден, отчёт сообщает MDE: какой эффект
104
+ эксперимент вообще мог заметить.
105
+
106
+ ## Командная строка
107
+
108
+ ```bash
109
+ honestab analyze data.csv --config experiment.yaml # отчёт в Markdown (или --json)
110
+ honestab design --kind proportion --baseline 0.05 --mde-rel 0.05 --daily 20000
111
+ honestab design --kind mean --baseline 280 --std 1100 --mde-rel 0.05 --variance-reduction 0.3
112
+ honestab srm 50120 49710
113
+ honestab example demo.csv --revenue-lift 0.05 # синтетический эксперимент + конфиг
114
+ ```
115
+
116
+ <details>
117
+ <summary>Пример experiment.yaml</summary>
118
+
119
+ ```yaml
120
+ group_col: group
121
+ control: A
122
+ treatment: B
123
+ expected_split: 0.5
124
+ alpha: 0.05
125
+ correction: holm # holm | bh | none
126
+ metrics:
127
+ - {name: revenue per user, column: revenue, covariate: pre_revenue, role: primary}
128
+ - {name: conversion, column: converted, kind: proportion}
129
+ - {name: ctr, numerator: clicks, denominator: views}
130
+ - {name: median time, column: time_spent, kind: median}
131
+ - {name: sessions per user, column: sessions, role: guardrail}
132
+ ```
133
+ </details>
134
+
135
+ ## Методы
136
+
137
+ | Задача | Функция | Суть |
138
+ |---|---|---|
139
+ | Среднее на пользователя | `hb.welch` | t-тест Уэлча; относительный эффект с интервалом Филлера |
140
+ | Снижение дисперсии | `hb.cuped` | CUPED: y − θ(x − x̄), x — метрика до эксперимента; дисперсия × (1 − ρ²) |
141
+ | Конверсия | `hb.proportions` | z-тест для двух долей |
142
+ | CTR, средний чек, выручка на сессию | `hb.ratio` | дельта-метод: учитывает, что события одного пользователя связаны |
143
+ | Медиана, перцентили | `hb.bootstrap` | перцентильный бутстреп |
144
+ | Подглядывание | `hb.msprt` | mSPRT: always-valid p-value и интервалы |
145
+ | Много метрик | `hb.holm`, `hb.benjamini_hochberg` | FWER / FDR |
146
+ | Потеря пользователей | `hb.srm` | χ²-тест размеров групп, порог p < 0.001 |
147
+ | Дизайн | `hb.sample_size`, `hb.sample_size_proportion`, `hb.mde`, `hb.duration_days` | размер выборки, MDE, длительность (с учётом CUPED) |
148
+ | Байесовский взгляд | `hb.bayes.beta_binomial` | P(B лучше A), ожидаемые потери |
149
+
150
+ Несколько решений, которых нет в «учебных» реализациях:
151
+ - **Интервал относительного эффекта по Филлеру.** Обычный дельта-метод для «+x%» иногда
152
+ выдаёт интервал, включающий 0, при p < 0.05, и отчёт противоречит сам себе. Интервал
153
+ Филлера исключает ноль ровно тогда, когда значим тест разности; это проверяется тестами
154
+ на 40 случайных выборках.
155
+ - **CUPED с θ по объединённым группам**, без утечки эффекта в оценку.
156
+ - **Бутстреп кусками**: память ограничена даже на сотнях тысяч пользователей.
157
+ - **Результаты сверены с эталонами**: p-value и интервалы совпадают со scipy и statsmodels,
158
+ дисперсия дельта-метода — с линеаризацией.
159
+
160
+ ## Валидация
161
+
162
+ [`validation/run.py`](validation/run.py) прогоняет тысячи симулированных экспериментов и
163
+ проверяет оба обещания каждого метода:
164
+ 1. без эффекта он ошибается в 5% случаев;
165
+ 2. с эффектом находит его так часто, как обещает формула мощности.
166
+
167
+ Полный отчёт с доверительными интервалами — [`validation/VALIDATION.md`](validation/VALIDATION.md).
168
+
169
+ | Проверка | Результат |
170
+ |---|---|
171
+ | A/A-тесты: Welch, CUPED, z-тест, дельта-метод | 4,2% … 5,0% ложных срабатываний (норма — 5%) |
172
+ | Покрытие 95% доверительных интервалов | 95,0% и 95,1% |
173
+ | Ложные тревоги проверки SRM | 0,0% |
174
+ | Мощность при эффекте +10% к выручке | Welch 57,5%, CUPED 75,3% |
175
+ | Цена безопасного подглядывания | mSPRT находит эффект в 51,6% случаев против 82,3% у одного анализа в конце, но останавливается в среднем на 63% срока |
176
+
177
+ Один неочевидный вывод: если эффект мультипликативный (выручка × 1.1), разброс в тестовой
178
+ группе тоже растёт, и учебная формула размера выборки завышает мощность. В
179
+ `hb.sample_size` для этого есть параметр `std_treatment`.
180
+
181
+ ![Ложные срабатывания](validation/aa.png)
182
+
183
+ ## Навык для ИИ-агентов
184
+
185
+ [`skills/ab-test-analysis/`](skills/ab-test-analysis/SKILL.md) учит агента анализировать
186
+ эксперимент по шагам:
187
+ 1. уточнить гипотезу и основную метрику;
188
+ 2. привести данные к строке на пользователя;
189
+ 3. проверить SRM;
190
+ 4. выбрать метод;
191
+ 5. запустить `honestab`;
192
+ 6. написать отчёт с интервалами и MDE.
193
+
194
+ Навык не позволяет агенту объявить победителя по сегменту или «тренду к значимости».
195
+
196
+ ```bash
197
+ npx skills add sofetizm/honestab
198
+ ```
199
+
200
+ Или скопируй папку `skills/ab-test-analysis/` в `~/.claude/skills/` (Claude Code),
201
+ `~/.agents/skills/` (Codex CLI), `.github/skills/` (Copilot), `.cursor/skills/` (Cursor).
202
+
203
+ ## Демо
204
+
205
+ [`notebooks/demo.ipynb`](notebooks/demo.ipynb) — полный разбор эксперимента с известным
206
+ истинным эффектом: дизайн и экономия трафика с CUPED → SRM → отчёт → почему CTR нельзя
207
+ считать по просмотрам → подглядывание → байесовский взгляд.
208
+
209
+ ## Структура
210
+
211
+ ```
212
+ src/honestab/
213
+ methods.py welch, proportions, ratio (дельта-метод), cuped, bootstrap
214
+ design.py размер выборки, MDE, длительность
215
+ checks.py SRM
216
+ multiple.py Holm, Benjamini–Hochberg
217
+ sequential.py mSPRT
218
+ bayes.py Beta-Binomial
219
+ experiment.py Metric, analyze, Report — от таблицы до решения
220
+ datasets.py синтетические эксперименты с известным эффектом
221
+ cli.py командная строка
222
+ validation/ симуляции, отчёт и графики
223
+ skills/ навык для ИИ-агентов
224
+ notebooks/ демо
225
+ tests/ сверка с scipy/statsmodels, свойства методов, CLI, сокращённые симуляции
226
+ ```
227
+
228
+ ## Разработка
229
+
230
+ ```bash
231
+ pip install -e ".[dev]"
232
+ pytest # быстрые тесты, ~15 секунд
233
+ pytest -m slow # сокращённые симуляции, ~2 минуты
234
+ python validation/run.py # полная валидация, ~10 минут
235
+ ```
236
+
237
+ ## Ограничения
238
+
239
+ - Два варианта: контроль и тест. Для A/B/n сравни каждый вариант с контролем и сделай
240
+ поправку на множественность.
241
+ - Методы асимптотические: для групп меньше нескольких сотен пользователей интервалы менее точны.
242
+ - Не учитываются сетевые эффекты (взаимодействие пользователей разных групп) и
243
+ кластерная рандомизация (по городам, магазинам).
244
+ - mSPRT оценивает дисперсию по данным — это приближение, точное начиная с нескольких
245
+ сотен наблюдений в группе (что и показывает валидация).
246
+
247
+ ## Литература
248
+
249
+ - Kohavi, Tang, Xu. *Trustworthy Online Controlled Experiments*. Cambridge University Press, 2020.
250
+ - Deng, Xu, Kohavi, Walker. *Improving the Sensitivity of Online Controlled Experiments by
251
+ Utilizing Pre-Experiment Data* (CUPED). WSDM 2013.
252
+ - Deng, Knoblich, Lu. *Applying the Delta Method in Metric Analytics*. KDD 2018.
253
+ - Johari, Koomen, Pekelis, Walsh. *Peeking at A/B Tests* (mSPRT). KDD 2017.
254
+ - Fieller. *Some Problems in Interval Estimation*. JRSS B, 1954.
255
+
256
+ ## Лицензия
257
+
258
+ MIT
@@ -0,0 +1,230 @@
1
+ # honestab — A/B-тесты без самообмана
2
+
3
+ [![CI](https://github.com/sofetizm/honestab/actions/workflows/ci.yml/badge.svg)](https://github.com/sofetizm/honestab/actions/workflows/ci.yml)
4
+ [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/sofetizm/honestab/blob/main/notebooks/demo.ipynb)
5
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
6
+ ![License](https://img.shields.io/badge/license-MIT-green)
7
+
8
+ Python-библиотека для анализа A/B-тестов, в которой **каждый метод проверен симуляциями**.
9
+ Она проверяет SRM, применяет CUPED, считает ratio-метрики дельта-методом, делает поправку
10
+ на множественность и поддерживает последовательное тестирование. На выходе — готовое
11
+ решение: катить, не катить или не доверять результатам. В комплекте навык для ИИ-агентов
12
+ (Claude Code, Codex CLI и др.), который анализирует эксперименты с помощью этой библиотеки.
13
+
14
+ ## Почему «честный»
15
+
16
+ Корректный статистический метод в A/A-тесте (эффекта нет) должен находить «эффект» в 5%
17
+ случаев — это и есть уровень значимости α = 0.05. Типичные ошибки аналитики ломают это
18
+ обещание. Числа ниже — из тысяч симулированных экспериментов ([`validation/`](validation/VALIDATION.md)):
19
+
20
+ | Ошибка | Что получается | Как надо (honestab) |
21
+ |---|---|---|
22
+ | Смотреть на p-value каждый день и остановиться, как только p < 0.05 | **24,5%** ложных срабатываний при 20 проверках | mSPRT: 1,2% |
23
+ | Считать CTR по таблице просмотров | **9,8%** ложных срабатываний | дельта-метод: 4,8% |
24
+ | Проверить 10 метрик и порадоваться значимой | **39,6%** шанс хотя бы одной ложной находки | поправка Holm: 4,8% |
25
+ | Не заметить, что в одной группе потерялась часть пользователей | «Эффект» в **99,8%** A/A-тестов | проверка SRM ловит 83,8% и блокирует решение |
26
+ | Не использовать данные до эксперимента | мощность 57,5% | CUPED: 75,3% при тех же 40 000 пользователях |
27
+
28
+ ![Подглядывание](validation/peeking.png)
29
+
30
+ ## Быстрый старт
31
+
32
+ ```bash
33
+ pip install honestab
34
+ ```
35
+
36
+ ```python
37
+ import honestab as hb
38
+
39
+ report = hb.analyze(df, [ # df: одна строка на пользователя, колонка group = A/B
40
+ hb.Metric("выручка", column="revenue", covariate="pre_revenue", role="primary"),
41
+ hb.Metric("конверсия", column="converted", kind="proportion"),
42
+ hb.Metric("CTR", numerator="clicks", denominator="views"),
43
+ hb.Metric("сессии", column="sessions", role="guardrail"),
44
+ ])
45
+ print(report.to_markdown())
46
+ ```
47
+
48
+ Результат:
49
+
50
+ > **Решение: КАТИТЬ**
51
+
52
+ - Основная метрика «выручка» значимо улучшилась: +11.73% (95% ДИ +5.31% … +18.56%).
53
+
54
+ **Проверка SRM:** пройдена — размеры групп [29957, 30043], ожидалось [30000, 30000], p = 0.726
55
+
56
+ | Метрика | Роль | Метод | Контроль | Тест | Эффект | 95% ДИ | p | p (с поправкой) |
57
+ |---|---|---|---|---|---|---|---|---|
58
+ | выручка ✱ | primary | cuped | 279.1 | 311.9 | +11.73% | [+5.31%; +18.56%] | 0.000249 | — |
59
+ | конверсия | secondary | proportions | 0.1064 | 0.1084 | +1.90% | [-2.69%; +6.72%] | 0.423 | 0.423 |
60
+ | CTR ✱ | secondary | ratio (delta method) | 0.06723 | 0.06935 | +3.16% | [+0.73%; +5.65%] | 0.0106 | 0.0211 |
61
+ | сессии | guardrail | welch | 3.508 | 3.491 | -0.49% | [-1.56%; +0.59%] | 0.368 | — |
62
+
63
+ ✱ — статистически значимо при α = 0.05 (вторичные метрики — с поправкой holm).
64
+
65
+ Примечания:
66
+ - выручка: theta = 0.1842; corr(y, x) = 0.566; variance reduced by ~32%
67
+
68
+ Что происходит внутри одного вызова:
69
+ 1. **Проверка SRM**: если размеры групп не соответствуют плану, решение — «не доверять», и
70
+ метрики не интерпретируются.
71
+ 2. **Метод выбирается по типу метрики**: Welch, CUPED (если есть ковариата до эксперимента),
72
+ z-тест для долей, дельта-метод для отношений, бутстреп для медиан.
73
+ 3. **Поправка Holm** для вторичных метрик.
74
+ 4. **Guardrail-метрики** блокируют выкатку, если значимо ухудшились.
75
+ 5. **Решение с объяснением.** Если эффект не найден, отчёт сообщает MDE: какой эффект
76
+ эксперимент вообще мог заметить.
77
+
78
+ ## Командная строка
79
+
80
+ ```bash
81
+ honestab analyze data.csv --config experiment.yaml # отчёт в Markdown (или --json)
82
+ honestab design --kind proportion --baseline 0.05 --mde-rel 0.05 --daily 20000
83
+ honestab design --kind mean --baseline 280 --std 1100 --mde-rel 0.05 --variance-reduction 0.3
84
+ honestab srm 50120 49710
85
+ honestab example demo.csv --revenue-lift 0.05 # синтетический эксперимент + конфиг
86
+ ```
87
+
88
+ <details>
89
+ <summary>Пример experiment.yaml</summary>
90
+
91
+ ```yaml
92
+ group_col: group
93
+ control: A
94
+ treatment: B
95
+ expected_split: 0.5
96
+ alpha: 0.05
97
+ correction: holm # holm | bh | none
98
+ metrics:
99
+ - {name: revenue per user, column: revenue, covariate: pre_revenue, role: primary}
100
+ - {name: conversion, column: converted, kind: proportion}
101
+ - {name: ctr, numerator: clicks, denominator: views}
102
+ - {name: median time, column: time_spent, kind: median}
103
+ - {name: sessions per user, column: sessions, role: guardrail}
104
+ ```
105
+ </details>
106
+
107
+ ## Методы
108
+
109
+ | Задача | Функция | Суть |
110
+ |---|---|---|
111
+ | Среднее на пользователя | `hb.welch` | t-тест Уэлча; относительный эффект с интервалом Филлера |
112
+ | Снижение дисперсии | `hb.cuped` | CUPED: y − θ(x − x̄), x — метрика до эксперимента; дисперсия × (1 − ρ²) |
113
+ | Конверсия | `hb.proportions` | z-тест для двух долей |
114
+ | CTR, средний чек, выручка на сессию | `hb.ratio` | дельта-метод: учитывает, что события одного пользователя связаны |
115
+ | Медиана, перцентили | `hb.bootstrap` | перцентильный бутстреп |
116
+ | Подглядывание | `hb.msprt` | mSPRT: always-valid p-value и интервалы |
117
+ | Много метрик | `hb.holm`, `hb.benjamini_hochberg` | FWER / FDR |
118
+ | Потеря пользователей | `hb.srm` | χ²-тест размеров групп, порог p < 0.001 |
119
+ | Дизайн | `hb.sample_size`, `hb.sample_size_proportion`, `hb.mde`, `hb.duration_days` | размер выборки, MDE, длительность (с учётом CUPED) |
120
+ | Байесовский взгляд | `hb.bayes.beta_binomial` | P(B лучше A), ожидаемые потери |
121
+
122
+ Несколько решений, которых нет в «учебных» реализациях:
123
+ - **Интервал относительного эффекта по Филлеру.** Обычный дельта-метод для «+x%» иногда
124
+ выдаёт интервал, включающий 0, при p < 0.05, и отчёт противоречит сам себе. Интервал
125
+ Филлера исключает ноль ровно тогда, когда значим тест разности; это проверяется тестами
126
+ на 40 случайных выборках.
127
+ - **CUPED с θ по объединённым группам**, без утечки эффекта в оценку.
128
+ - **Бутстреп кусками**: память ограничена даже на сотнях тысяч пользователей.
129
+ - **Результаты сверены с эталонами**: p-value и интервалы совпадают со scipy и statsmodels,
130
+ дисперсия дельта-метода — с линеаризацией.
131
+
132
+ ## Валидация
133
+
134
+ [`validation/run.py`](validation/run.py) прогоняет тысячи симулированных экспериментов и
135
+ проверяет оба обещания каждого метода:
136
+ 1. без эффекта он ошибается в 5% случаев;
137
+ 2. с эффектом находит его так часто, как обещает формула мощности.
138
+
139
+ Полный отчёт с доверительными интервалами — [`validation/VALIDATION.md`](validation/VALIDATION.md).
140
+
141
+ | Проверка | Результат |
142
+ |---|---|
143
+ | A/A-тесты: Welch, CUPED, z-тест, дельта-метод | 4,2% … 5,0% ложных срабатываний (норма — 5%) |
144
+ | Покрытие 95% доверительных интервалов | 95,0% и 95,1% |
145
+ | Ложные тревоги проверки SRM | 0,0% |
146
+ | Мощность при эффекте +10% к выручке | Welch 57,5%, CUPED 75,3% |
147
+ | Цена безопасного подглядывания | mSPRT находит эффект в 51,6% случаев против 82,3% у одного анализа в конце, но останавливается в среднем на 63% срока |
148
+
149
+ Один неочевидный вывод: если эффект мультипликативный (выручка × 1.1), разброс в тестовой
150
+ группе тоже растёт, и учебная формула размера выборки завышает мощность. В
151
+ `hb.sample_size` для этого есть параметр `std_treatment`.
152
+
153
+ ![Ложные срабатывания](validation/aa.png)
154
+
155
+ ## Навык для ИИ-агентов
156
+
157
+ [`skills/ab-test-analysis/`](skills/ab-test-analysis/SKILL.md) учит агента анализировать
158
+ эксперимент по шагам:
159
+ 1. уточнить гипотезу и основную метрику;
160
+ 2. привести данные к строке на пользователя;
161
+ 3. проверить SRM;
162
+ 4. выбрать метод;
163
+ 5. запустить `honestab`;
164
+ 6. написать отчёт с интервалами и MDE.
165
+
166
+ Навык не позволяет агенту объявить победителя по сегменту или «тренду к значимости».
167
+
168
+ ```bash
169
+ npx skills add sofetizm/honestab
170
+ ```
171
+
172
+ Или скопируй папку `skills/ab-test-analysis/` в `~/.claude/skills/` (Claude Code),
173
+ `~/.agents/skills/` (Codex CLI), `.github/skills/` (Copilot), `.cursor/skills/` (Cursor).
174
+
175
+ ## Демо
176
+
177
+ [`notebooks/demo.ipynb`](notebooks/demo.ipynb) — полный разбор эксперимента с известным
178
+ истинным эффектом: дизайн и экономия трафика с CUPED → SRM → отчёт → почему CTR нельзя
179
+ считать по просмотрам → подглядывание → байесовский взгляд.
180
+
181
+ ## Структура
182
+
183
+ ```
184
+ src/honestab/
185
+ methods.py welch, proportions, ratio (дельта-метод), cuped, bootstrap
186
+ design.py размер выборки, MDE, длительность
187
+ checks.py SRM
188
+ multiple.py Holm, Benjamini–Hochberg
189
+ sequential.py mSPRT
190
+ bayes.py Beta-Binomial
191
+ experiment.py Metric, analyze, Report — от таблицы до решения
192
+ datasets.py синтетические эксперименты с известным эффектом
193
+ cli.py командная строка
194
+ validation/ симуляции, отчёт и графики
195
+ skills/ навык для ИИ-агентов
196
+ notebooks/ демо
197
+ tests/ сверка с scipy/statsmodels, свойства методов, CLI, сокращённые симуляции
198
+ ```
199
+
200
+ ## Разработка
201
+
202
+ ```bash
203
+ pip install -e ".[dev]"
204
+ pytest # быстрые тесты, ~15 секунд
205
+ pytest -m slow # сокращённые симуляции, ~2 минуты
206
+ python validation/run.py # полная валидация, ~10 минут
207
+ ```
208
+
209
+ ## Ограничения
210
+
211
+ - Два варианта: контроль и тест. Для A/B/n сравни каждый вариант с контролем и сделай
212
+ поправку на множественность.
213
+ - Методы асимптотические: для групп меньше нескольких сотен пользователей интервалы менее точны.
214
+ - Не учитываются сетевые эффекты (взаимодействие пользователей разных групп) и
215
+ кластерная рандомизация (по городам, магазинам).
216
+ - mSPRT оценивает дисперсию по данным — это приближение, точное начиная с нескольких
217
+ сотен наблюдений в группе (что и показывает валидация).
218
+
219
+ ## Литература
220
+
221
+ - Kohavi, Tang, Xu. *Trustworthy Online Controlled Experiments*. Cambridge University Press, 2020.
222
+ - Deng, Xu, Kohavi, Walker. *Improving the Sensitivity of Online Controlled Experiments by
223
+ Utilizing Pre-Experiment Data* (CUPED). WSDM 2013.
224
+ - Deng, Knoblich, Lu. *Applying the Delta Method in Metric Analytics*. KDD 2018.
225
+ - Johari, Koomen, Pekelis, Walsh. *Peeking at A/B Tests* (mSPRT). KDD 2017.
226
+ - Fieller. *Some Problems in Interval Estimation*. JRSS B, 1954.
227
+
228
+ ## Лицензия
229
+
230
+ MIT
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "honestab"
7
+ version = "0.1.0"
8
+ description = "A/B test analysis with methods verified by simulation: CUPED, delta method, sequential testing, SRM"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Sofya Babkina" }]
13
+ keywords = ["ab-testing", "experimentation", "statistics", "cuped", "delta-method", "sequential-testing"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Topic :: Scientific/Engineering :: Mathematics",
18
+ "Intended Audience :: Science/Research",
19
+ ]
20
+ dependencies = ["numpy>=1.24", "scipy>=1.10", "pandas>=2.0", "pyyaml>=6"]
21
+
22
+ [project.optional-dependencies]
23
+ plots = ["matplotlib>=3.7"]
24
+ dev = ["pytest>=8", "ruff>=0.6", "statsmodels>=0.14", "matplotlib>=3.7"]
25
+
26
+ [project.scripts]
27
+ honestab = "honestab.cli:main"
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/sofetizm/honestab"
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
34
+
35
+ [tool.pytest.ini_options]
36
+ testpaths = ["tests"]
37
+ markers = ["slow: large simulations (~minutes)"]
38
+ addopts = "-m 'not slow'"
39
+
40
+ [tool.ruff]
41
+ line-length = 110
42
+ target-version = "py310"
43
+
44
+ [tool.ruff.lint]
45
+ select = ["E", "F", "I", "B", "UP"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,18 @@
1
+ """honestab — A/B test analysis with methods verified by simulation."""
2
+ from . import bayes, checks, datasets, design, methods, multiple, sequential
3
+ from .checks import srm
4
+ from .design import duration_days, mde, sample_size, sample_size_proportion
5
+ from .experiment import Metric, Report, analyze
6
+ from .methods import bootstrap, cuped, proportions, ratio, welch
7
+ from .multiple import benjamini_hochberg, holm
8
+ from .result import EffectResult
9
+ from .sequential import msprt
10
+
11
+ __version__ = "0.1.0"
12
+
13
+ __all__ = [
14
+ "EffectResult", "Metric", "Report", "analyze", "bayes", "benjamini_hochberg", "bootstrap",
15
+ "checks", "cuped", "datasets", "design", "duration_days", "holm", "mde", "methods", "msprt",
16
+ "multiple", "proportions", "ratio", "sample_size", "sample_size_proportion", "sequential",
17
+ "srm", "welch",
18
+ ]
@@ -0,0 +1,36 @@
1
+ """Bayesian view of a conversion experiment (Beta-Binomial).
2
+
3
+ Gives "probability that B is better" and "expected loss if we choose B". Useful for
4
+ communicating results; it does **not** make peeking safe and does not replace the
5
+ pre-registered frequentist decision.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+
11
+ import numpy as np
12
+
13
+
14
+ @dataclass
15
+ class BayesResult:
16
+ prob_treatment_better: float
17
+ expected_loss_treatment: float
18
+ expected_loss_control: float
19
+ rel_effect_ci: tuple[float, float]
20
+
21
+
22
+ def beta_binomial(successes_control: int, n_control: int, successes_treatment: int,
23
+ n_treatment: int, prior: tuple[float, float] = (1.0, 1.0),
24
+ draws: int = 200_000, seed: int | None = 0, ci: float = 0.95) -> BayesResult:
25
+ a, b = prior
26
+ rng = np.random.default_rng(seed)
27
+ pc = rng.beta(a + successes_control, b + n_control - successes_control, draws)
28
+ pt = rng.beta(a + successes_treatment, b + n_treatment - successes_treatment, draws)
29
+ rel = pt / pc - 1
30
+ q = (1 - ci) / 2
31
+ return BayesResult(
32
+ prob_treatment_better=float(np.mean(pt > pc)),
33
+ expected_loss_treatment=float(np.mean(np.maximum(pc - pt, 0))),
34
+ expected_loss_control=float(np.mean(np.maximum(pt - pc, 0))),
35
+ rel_effect_ci=(float(np.quantile(rel, q)), float(np.quantile(rel, 1 - q))),
36
+ )