experimentation-toolkit 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.
- experimentation_toolkit-0.1.0/LICENSE +21 -0
- experimentation_toolkit-0.1.0/PKG-INFO +213 -0
- experimentation_toolkit-0.1.0/README.md +180 -0
- experimentation_toolkit-0.1.0/pyproject.toml +79 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/__init__.py +51 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/analysis/__init__.py +6 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/analysis/continuous.py +255 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/analysis/proportion.py +204 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/diagnostics/__init__.py +5 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/diagnostics/srm.py +89 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/models/__init__.py +40 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/models/core.py +51 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/models/enums.py +41 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/models/results.py +211 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/multiple_testing/__init__.py +5 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/multiple_testing/corrections.py +65 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/power/__init__.py +8 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/power/proportion.py +238 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/py.typed +1 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/validation/__init__.py +1 -0
- experimentation_toolkit-0.1.0/src/experimentation_toolkit/validation/inputs.py +89 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 datenwissenschaften
|
|
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,213 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: experimentation-toolkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Typed, statistically explicit frequentist analysis for controlled A/B experiments
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: datenwissenschaften
|
|
8
|
+
Author-email: martin.franke@datenwissenschaften.com
|
|
9
|
+
Requires-Python: >=3.12,<4.0
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Provides-Extra: examples
|
|
17
|
+
Requires-Dist: ipykernel (>=6.29,<8.0) ; extra == "examples"
|
|
18
|
+
Requires-Dist: kaleido (>=1.1,<2.0) ; extra == "examples"
|
|
19
|
+
Requires-Dist: mypy (>=1.11,<2.0) ; extra == "dev"
|
|
20
|
+
Requires-Dist: nbclient (>=0.10,<1.0) ; extra == "examples"
|
|
21
|
+
Requires-Dist: nbformat (>=5.10,<6.0) ; extra == "examples"
|
|
22
|
+
Requires-Dist: numpy (>=2.0,<3.0)
|
|
23
|
+
Requires-Dist: plotly (>=6.0,<7.0) ; extra == "examples"
|
|
24
|
+
Requires-Dist: pydantic (>=2.8,<3.0)
|
|
25
|
+
Requires-Dist: pytest (>=8.3,<9.0) ; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-cov (>=5.0,<7.0) ; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff (>=0.6,<1.0) ; extra == "dev"
|
|
28
|
+
Requires-Dist: scipy (>=1.13,<2.0)
|
|
29
|
+
Requires-Dist: scipy-stubs (>=1.15,<2.0) ; extra == "dev"
|
|
30
|
+
Project-URL: Repository, https://github.com/datenwissenschaften/experimentation-toolkit
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# experimentation-toolkit
|
|
34
|
+
|
|
35
|
+
[](https://github.com/datenwissenschaften/experimentation-toolkit/actions/workflows/ci.yml) [](https://pypi.org/project/experimentation-toolkit/)  [](https://github.com/datenwissenschaften/experimentation-toolkit/commits/main) [](LICENSE)   [](https://codecov.io/gh/datenwissenschaften/experimentation-toolkit)
|
|
36
|
+
|
|
37
|
+
`experimentation-toolkit` is a typed Python library for classical frequentist analysis of
|
|
38
|
+
independent, two-arm controlled experiments. It connects assignment diagnostics, metric-level
|
|
39
|
+
inference, multiplicity correction, power planning, and a serializable report without making a
|
|
40
|
+
DataFrame the core abstraction.
|
|
41
|
+
|
|
42
|
+
The package reports estimates, uncertainty, and statistical evidence. It deliberately does not
|
|
43
|
+
turn a small p-value into a claim that an effect is important, beneficial, or worth shipping.
|
|
44
|
+
|
|
45
|
+
## Quick example
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from experimentation_toolkit import (
|
|
49
|
+
Experiment,
|
|
50
|
+
ExperimentReport,
|
|
51
|
+
analyze_proportion,
|
|
52
|
+
check_sample_ratio_mismatch,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
experiment = Experiment(control="control", treatment="treatment")
|
|
56
|
+
srm = check_sample_ratio_mismatch(
|
|
57
|
+
observed_counts={"control": 10_000, "treatment": 10_050},
|
|
58
|
+
expected_proportions={"control": 0.5, "treatment": 0.5},
|
|
59
|
+
)
|
|
60
|
+
conversion = analyze_proportion(
|
|
61
|
+
control_successes=1_200,
|
|
62
|
+
control_total=10_000,
|
|
63
|
+
treatment_successes=1_280,
|
|
64
|
+
treatment_total=10_050,
|
|
65
|
+
confidence_level=0.95,
|
|
66
|
+
alternative="two-sided",
|
|
67
|
+
metric_name="conversion",
|
|
68
|
+
)
|
|
69
|
+
report = ExperimentReport(
|
|
70
|
+
experiment=experiment,
|
|
71
|
+
analyses=(conversion,),
|
|
72
|
+
diagnostics=(srm,),
|
|
73
|
+
)
|
|
74
|
+
print(report.model_dump_json(indent=2))
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The absolute effect is a risk difference. A move from 10% to 11% is `0.01`, or **one
|
|
78
|
+
percentage point**. Its relative lift is `0.10`, or **10% relative**. Results expose these as
|
|
79
|
+
separate fields with an explicit relative-effect definition.
|
|
80
|
+
|
|
81
|
+
## Supported analyses
|
|
82
|
+
|
|
83
|
+
- Independent binary outcomes from success counts and totals
|
|
84
|
+
- Independent continuous outcomes from numeric array-like samples
|
|
85
|
+
- Two-sided, treatment-greater, and treatment-less alternatives
|
|
86
|
+
- Sample-ratio-mismatch checks for two or more assignment groups
|
|
87
|
+
- Proportion sample-size planning and achieved-power approximation
|
|
88
|
+
- Bonferroni, Holm, and Benjamini-Hochberg p-value adjustment
|
|
89
|
+
- Immutable Pydantic models and standards-compliant JSON reports
|
|
90
|
+
|
|
91
|
+
## Statistical methods
|
|
92
|
+
|
|
93
|
+
Proportion inference uses a pooled two-sample score z-test for the point null
|
|
94
|
+
`p_treatment - p_control = 0`. The risk-difference confidence interval is Newcombe's hybrid
|
|
95
|
+
score interval formed from Wilson limits without continuity correction. It remains useful at
|
|
96
|
+
zero and one, while an explicit warning flags sparse expected cells where the test's normal
|
|
97
|
+
approximation may be inaccurate.
|
|
98
|
+
|
|
99
|
+
Continuous inference uses Welch's unequal-variance two-sample t-test and the
|
|
100
|
+
Welch-Satterthwaite degrees of freedom. It does not assume equal group variances. Both analyses
|
|
101
|
+
define effects as treatment minus control and align one-sided confidence bounds with the chosen
|
|
102
|
+
alternative. An unbounded endpoint is serialized as `null`, never infinity.
|
|
103
|
+
|
|
104
|
+
See [docs/statistical-methods.md](docs/statistical-methods.md) for formulas, assumptions, and
|
|
105
|
+
references.
|
|
106
|
+
|
|
107
|
+
## Assumptions
|
|
108
|
+
|
|
109
|
+
Inference assumes randomized or otherwise exchangeable groups, independent experimental units,
|
|
110
|
+
a metric definition fixed independently of observed treatment results, and no interference
|
|
111
|
+
between units. Binary observations must be Bernoulli outcomes at the analysis unit. Welch
|
|
112
|
+
inference assumes finite variance and is exact under normal sampling; with larger samples it
|
|
113
|
+
uses the sampling distribution of the mean. The library cannot establish that these assumptions
|
|
114
|
+
hold from arrays or counts alone.
|
|
115
|
+
|
|
116
|
+
## Sample ratio mismatch
|
|
117
|
+
|
|
118
|
+
`check_sample_ratio_mismatch` applies Pearson's chi-square goodness-of-fit test to observed arm
|
|
119
|
+
counts and configured allocation probabilities. `FAIL` means the allocation is statistically
|
|
120
|
+
inconsistent with the plan at the selected threshold. It indicates a potential randomization,
|
|
121
|
+
eligibility, logging, or instrumentation problem; it does not identify the cause. Inspect SRM
|
|
122
|
+
before interpreting treatment effects.
|
|
123
|
+
|
|
124
|
+
## Power and sample size
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from experimentation_toolkit import proportion_sample_size
|
|
128
|
+
|
|
129
|
+
plan = proportion_sample_size(
|
|
130
|
+
baseline_rate=0.10,
|
|
131
|
+
minimum_detectable_effect=0.01,
|
|
132
|
+
mde_type="absolute", # +1 percentage point
|
|
133
|
+
alpha=0.05,
|
|
134
|
+
power=0.80,
|
|
135
|
+
allocation_ratio=1.0, # treatment / control
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Set `mde_type="relative"` to express the MDE as a fraction of baseline: `0.10` at a 10%
|
|
140
|
+
baseline targets an 11% treatment rate. The calculation is a fixed-horizon normal approximation,
|
|
141
|
+
and the returned achieved power reflects the integer group sizes. Planning inputs are assumptions,
|
|
142
|
+
not estimates guaranteed to hold after launch.
|
|
143
|
+
|
|
144
|
+
## Multiple testing
|
|
145
|
+
|
|
146
|
+
`adjust_p_values` preserves original hypothesis order and optionally carries labels. Bonferroni
|
|
147
|
+
and Holm control the family-wise error rate. Benjamini-Hochberg controls the false discovery rate
|
|
148
|
+
under independence or positive dependence and must not be interpreted as FWER control. The caller
|
|
149
|
+
defines the hypothesis family before looking at results.
|
|
150
|
+
|
|
151
|
+
## Interpretation
|
|
152
|
+
|
|
153
|
+
Statistical significance quantifies incompatibility with a null model under its assumptions.
|
|
154
|
+
Practical significance requires domain context, costs, guardrails, and a pre-specified decision
|
|
155
|
+
rule. Read the absolute effect and its confidence interval first. Relative effects are omitted
|
|
156
|
+
when the control denominator is zero or when continuous means do not support a conservative,
|
|
157
|
+
non-negative and numerically resolved ratio-scale interpretation.
|
|
158
|
+
|
|
159
|
+
## Installation
|
|
160
|
+
|
|
161
|
+
Python 3.12 is required.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
python -m pip install experimentation-toolkit
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
For a source checkout:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
python -m pip install -e '.[dev]'
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
To execute the visual notebook, install the separate example dependencies:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
python -m pip install -e '.[dev,examples]'
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Testing and development
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
ruff check .
|
|
183
|
+
ruff format --check .
|
|
184
|
+
mypy
|
|
185
|
+
pytest
|
|
186
|
+
pytest --cov=experimentation_toolkit --cov-report=term-missing
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The deterministic [game onboarding notebook](examples/game_onboarding.ipynb) demonstrates
|
|
190
|
+
assignment counts, SRM, a primary completion metric, retention and session-duration metrics, Holm
|
|
191
|
+
correction, Plotly-generated uncertainty plots rendered as GitHub-compatible PNGs, and report
|
|
192
|
+
serialization. It is self-contained, uses a fixed seed, and labels all generated observations as
|
|
193
|
+
synthetic.
|
|
194
|
+
|
|
195
|
+
## Limitations
|
|
196
|
+
|
|
197
|
+
Version 0.1 supports independent, two-arm, fixed-horizon analyses only. It does not implement
|
|
198
|
+
paired or clustered designs, exact unconditional proportion tests, covariate adjustment, CUPED,
|
|
199
|
+
sequential monitoring, alpha spending, Bayesian analysis, causal identification, heterogeneous
|
|
200
|
+
effects, feature flags, or platform infrastructure. The proportion test and power methods use
|
|
201
|
+
large-sample approximations; warnings do not convert them into exact procedures.
|
|
202
|
+
|
|
203
|
+
## Roadmap
|
|
204
|
+
|
|
205
|
+
Statistically useful next steps include stratified analyses with explicit estimands, cluster-aware
|
|
206
|
+
standard errors and design effects, robust or transformed continuous outcomes, exact methods for
|
|
207
|
+
sparse binary data, and simulation-based validation of planning assumptions. Each belongs behind
|
|
208
|
+
a distinct API that states its sampling unit and assumptions.
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
|
213
|
+
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# experimentation-toolkit
|
|
2
|
+
|
|
3
|
+
[](https://github.com/datenwissenschaften/experimentation-toolkit/actions/workflows/ci.yml) [](https://pypi.org/project/experimentation-toolkit/)  [](https://github.com/datenwissenschaften/experimentation-toolkit/commits/main) [](LICENSE)   [](https://codecov.io/gh/datenwissenschaften/experimentation-toolkit)
|
|
4
|
+
|
|
5
|
+
`experimentation-toolkit` is a typed Python library for classical frequentist analysis of
|
|
6
|
+
independent, two-arm controlled experiments. It connects assignment diagnostics, metric-level
|
|
7
|
+
inference, multiplicity correction, power planning, and a serializable report without making a
|
|
8
|
+
DataFrame the core abstraction.
|
|
9
|
+
|
|
10
|
+
The package reports estimates, uncertainty, and statistical evidence. It deliberately does not
|
|
11
|
+
turn a small p-value into a claim that an effect is important, beneficial, or worth shipping.
|
|
12
|
+
|
|
13
|
+
## Quick example
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from experimentation_toolkit import (
|
|
17
|
+
Experiment,
|
|
18
|
+
ExperimentReport,
|
|
19
|
+
analyze_proportion,
|
|
20
|
+
check_sample_ratio_mismatch,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
experiment = Experiment(control="control", treatment="treatment")
|
|
24
|
+
srm = check_sample_ratio_mismatch(
|
|
25
|
+
observed_counts={"control": 10_000, "treatment": 10_050},
|
|
26
|
+
expected_proportions={"control": 0.5, "treatment": 0.5},
|
|
27
|
+
)
|
|
28
|
+
conversion = analyze_proportion(
|
|
29
|
+
control_successes=1_200,
|
|
30
|
+
control_total=10_000,
|
|
31
|
+
treatment_successes=1_280,
|
|
32
|
+
treatment_total=10_050,
|
|
33
|
+
confidence_level=0.95,
|
|
34
|
+
alternative="two-sided",
|
|
35
|
+
metric_name="conversion",
|
|
36
|
+
)
|
|
37
|
+
report = ExperimentReport(
|
|
38
|
+
experiment=experiment,
|
|
39
|
+
analyses=(conversion,),
|
|
40
|
+
diagnostics=(srm,),
|
|
41
|
+
)
|
|
42
|
+
print(report.model_dump_json(indent=2))
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The absolute effect is a risk difference. A move from 10% to 11% is `0.01`, or **one
|
|
46
|
+
percentage point**. Its relative lift is `0.10`, or **10% relative**. Results expose these as
|
|
47
|
+
separate fields with an explicit relative-effect definition.
|
|
48
|
+
|
|
49
|
+
## Supported analyses
|
|
50
|
+
|
|
51
|
+
- Independent binary outcomes from success counts and totals
|
|
52
|
+
- Independent continuous outcomes from numeric array-like samples
|
|
53
|
+
- Two-sided, treatment-greater, and treatment-less alternatives
|
|
54
|
+
- Sample-ratio-mismatch checks for two or more assignment groups
|
|
55
|
+
- Proportion sample-size planning and achieved-power approximation
|
|
56
|
+
- Bonferroni, Holm, and Benjamini-Hochberg p-value adjustment
|
|
57
|
+
- Immutable Pydantic models and standards-compliant JSON reports
|
|
58
|
+
|
|
59
|
+
## Statistical methods
|
|
60
|
+
|
|
61
|
+
Proportion inference uses a pooled two-sample score z-test for the point null
|
|
62
|
+
`p_treatment - p_control = 0`. The risk-difference confidence interval is Newcombe's hybrid
|
|
63
|
+
score interval formed from Wilson limits without continuity correction. It remains useful at
|
|
64
|
+
zero and one, while an explicit warning flags sparse expected cells where the test's normal
|
|
65
|
+
approximation may be inaccurate.
|
|
66
|
+
|
|
67
|
+
Continuous inference uses Welch's unequal-variance two-sample t-test and the
|
|
68
|
+
Welch-Satterthwaite degrees of freedom. It does not assume equal group variances. Both analyses
|
|
69
|
+
define effects as treatment minus control and align one-sided confidence bounds with the chosen
|
|
70
|
+
alternative. An unbounded endpoint is serialized as `null`, never infinity.
|
|
71
|
+
|
|
72
|
+
See [docs/statistical-methods.md](docs/statistical-methods.md) for formulas, assumptions, and
|
|
73
|
+
references.
|
|
74
|
+
|
|
75
|
+
## Assumptions
|
|
76
|
+
|
|
77
|
+
Inference assumes randomized or otherwise exchangeable groups, independent experimental units,
|
|
78
|
+
a metric definition fixed independently of observed treatment results, and no interference
|
|
79
|
+
between units. Binary observations must be Bernoulli outcomes at the analysis unit. Welch
|
|
80
|
+
inference assumes finite variance and is exact under normal sampling; with larger samples it
|
|
81
|
+
uses the sampling distribution of the mean. The library cannot establish that these assumptions
|
|
82
|
+
hold from arrays or counts alone.
|
|
83
|
+
|
|
84
|
+
## Sample ratio mismatch
|
|
85
|
+
|
|
86
|
+
`check_sample_ratio_mismatch` applies Pearson's chi-square goodness-of-fit test to observed arm
|
|
87
|
+
counts and configured allocation probabilities. `FAIL` means the allocation is statistically
|
|
88
|
+
inconsistent with the plan at the selected threshold. It indicates a potential randomization,
|
|
89
|
+
eligibility, logging, or instrumentation problem; it does not identify the cause. Inspect SRM
|
|
90
|
+
before interpreting treatment effects.
|
|
91
|
+
|
|
92
|
+
## Power and sample size
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from experimentation_toolkit import proportion_sample_size
|
|
96
|
+
|
|
97
|
+
plan = proportion_sample_size(
|
|
98
|
+
baseline_rate=0.10,
|
|
99
|
+
minimum_detectable_effect=0.01,
|
|
100
|
+
mde_type="absolute", # +1 percentage point
|
|
101
|
+
alpha=0.05,
|
|
102
|
+
power=0.80,
|
|
103
|
+
allocation_ratio=1.0, # treatment / control
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Set `mde_type="relative"` to express the MDE as a fraction of baseline: `0.10` at a 10%
|
|
108
|
+
baseline targets an 11% treatment rate. The calculation is a fixed-horizon normal approximation,
|
|
109
|
+
and the returned achieved power reflects the integer group sizes. Planning inputs are assumptions,
|
|
110
|
+
not estimates guaranteed to hold after launch.
|
|
111
|
+
|
|
112
|
+
## Multiple testing
|
|
113
|
+
|
|
114
|
+
`adjust_p_values` preserves original hypothesis order and optionally carries labels. Bonferroni
|
|
115
|
+
and Holm control the family-wise error rate. Benjamini-Hochberg controls the false discovery rate
|
|
116
|
+
under independence or positive dependence and must not be interpreted as FWER control. The caller
|
|
117
|
+
defines the hypothesis family before looking at results.
|
|
118
|
+
|
|
119
|
+
## Interpretation
|
|
120
|
+
|
|
121
|
+
Statistical significance quantifies incompatibility with a null model under its assumptions.
|
|
122
|
+
Practical significance requires domain context, costs, guardrails, and a pre-specified decision
|
|
123
|
+
rule. Read the absolute effect and its confidence interval first. Relative effects are omitted
|
|
124
|
+
when the control denominator is zero or when continuous means do not support a conservative,
|
|
125
|
+
non-negative and numerically resolved ratio-scale interpretation.
|
|
126
|
+
|
|
127
|
+
## Installation
|
|
128
|
+
|
|
129
|
+
Python 3.12 is required.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
python -m pip install experimentation-toolkit
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
For a source checkout:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
python -m pip install -e '.[dev]'
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
To execute the visual notebook, install the separate example dependencies:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
python -m pip install -e '.[dev,examples]'
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Testing and development
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
ruff check .
|
|
151
|
+
ruff format --check .
|
|
152
|
+
mypy
|
|
153
|
+
pytest
|
|
154
|
+
pytest --cov=experimentation_toolkit --cov-report=term-missing
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The deterministic [game onboarding notebook](examples/game_onboarding.ipynb) demonstrates
|
|
158
|
+
assignment counts, SRM, a primary completion metric, retention and session-duration metrics, Holm
|
|
159
|
+
correction, Plotly-generated uncertainty plots rendered as GitHub-compatible PNGs, and report
|
|
160
|
+
serialization. It is self-contained, uses a fixed seed, and labels all generated observations as
|
|
161
|
+
synthetic.
|
|
162
|
+
|
|
163
|
+
## Limitations
|
|
164
|
+
|
|
165
|
+
Version 0.1 supports independent, two-arm, fixed-horizon analyses only. It does not implement
|
|
166
|
+
paired or clustered designs, exact unconditional proportion tests, covariate adjustment, CUPED,
|
|
167
|
+
sequential monitoring, alpha spending, Bayesian analysis, causal identification, heterogeneous
|
|
168
|
+
effects, feature flags, or platform infrastructure. The proportion test and power methods use
|
|
169
|
+
large-sample approximations; warnings do not convert them into exact procedures.
|
|
170
|
+
|
|
171
|
+
## Roadmap
|
|
172
|
+
|
|
173
|
+
Statistically useful next steps include stratified analyses with explicit estimands, cluster-aware
|
|
174
|
+
standard errors and design effects, robust or transformed continuous outcomes, exact methods for
|
|
175
|
+
sparse binary data, and simulation-based validation of planning assumptions. Each belongs behind
|
|
176
|
+
a distinct API that states its sampling unit and assumptions.
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "experimentation-toolkit"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Typed, statistically explicit frequentist analysis for controlled A/B experiments"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "datenwissenschaften", email = "martin.franke@datenwissenschaften.com" },
|
|
10
|
+
]
|
|
11
|
+
requires-python = ">=3.12,<4.0"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"numpy>=2.0,<3.0",
|
|
14
|
+
"scipy>=1.13,<2.0",
|
|
15
|
+
"pydantic>=2.8,<3.0"
|
|
16
|
+
]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
22
|
+
"Typing :: Typed",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
dev = [
|
|
27
|
+
"mypy>=1.11,<2.0",
|
|
28
|
+
"pytest>=8.3,<9.0",
|
|
29
|
+
"pytest-cov>=5.0,<7.0",
|
|
30
|
+
"ruff>=0.6,<1.0",
|
|
31
|
+
"scipy-stubs>=1.15,<2.0",
|
|
32
|
+
]
|
|
33
|
+
examples = [
|
|
34
|
+
"ipykernel>=6.29,<8.0",
|
|
35
|
+
"nbclient>=0.10,<1.0",
|
|
36
|
+
"kaleido>=1.1,<2.0",
|
|
37
|
+
"nbformat>=5.10,<6.0",
|
|
38
|
+
"plotly>=6.0,<7.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Repository = "https://github.com/datenwissenschaften/experimentation-toolkit"
|
|
43
|
+
|
|
44
|
+
[build-system]
|
|
45
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
46
|
+
build-backend = "poetry.core.masonry.api"
|
|
47
|
+
|
|
48
|
+
[tool.poetry]
|
|
49
|
+
packages = [{ include = "experimentation_toolkit", from = "src" }]
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
target-version = "py312"
|
|
53
|
+
line-length = 100
|
|
54
|
+
src = ["src", "tests"]
|
|
55
|
+
|
|
56
|
+
[tool.ruff.lint]
|
|
57
|
+
select = ["E", "F", "I", "B", "UP", "SIM", "RUF"]
|
|
58
|
+
|
|
59
|
+
[tool.ruff.format]
|
|
60
|
+
quote-style = "double"
|
|
61
|
+
|
|
62
|
+
[tool.mypy]
|
|
63
|
+
python_version = "3.12"
|
|
64
|
+
strict = true
|
|
65
|
+
files = ["src", "examples"]
|
|
66
|
+
plugins = ["pydantic.mypy"]
|
|
67
|
+
|
|
68
|
+
[tool.pytest.ini_options]
|
|
69
|
+
addopts = "--strict-markers --strict-config -ra"
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
pythonpath = ["."]
|
|
72
|
+
|
|
73
|
+
[tool.coverage.run]
|
|
74
|
+
branch = true
|
|
75
|
+
source = ["experimentation_toolkit"]
|
|
76
|
+
|
|
77
|
+
[tool.coverage.report]
|
|
78
|
+
show_missing = true
|
|
79
|
+
skip_covered = true
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Statistically explicit tools for classical controlled A/B experiments."""
|
|
2
|
+
|
|
3
|
+
from experimentation_toolkit.analysis import analyze_continuous, analyze_proportion
|
|
4
|
+
from experimentation_toolkit.diagnostics import check_sample_ratio_mismatch
|
|
5
|
+
from experimentation_toolkit.models import (
|
|
6
|
+
AchievedPowerResult,
|
|
7
|
+
AlternativeHypothesis,
|
|
8
|
+
AnalysisResult,
|
|
9
|
+
ConfidenceInterval,
|
|
10
|
+
CorrectionMethod,
|
|
11
|
+
DiagnosticResult,
|
|
12
|
+
DiagnosticStatus,
|
|
13
|
+
Experiment,
|
|
14
|
+
ExperimentReport,
|
|
15
|
+
MDEType,
|
|
16
|
+
Metric,
|
|
17
|
+
MetricType,
|
|
18
|
+
MultipleTestingResult,
|
|
19
|
+
ProportionSampleSizeResult,
|
|
20
|
+
SRMResult,
|
|
21
|
+
Variant,
|
|
22
|
+
)
|
|
23
|
+
from experimentation_toolkit.multiple_testing import adjust_p_values
|
|
24
|
+
from experimentation_toolkit.power import proportion_achieved_power, proportion_sample_size
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"AchievedPowerResult",
|
|
28
|
+
"AlternativeHypothesis",
|
|
29
|
+
"AnalysisResult",
|
|
30
|
+
"ConfidenceInterval",
|
|
31
|
+
"CorrectionMethod",
|
|
32
|
+
"DiagnosticResult",
|
|
33
|
+
"DiagnosticStatus",
|
|
34
|
+
"Experiment",
|
|
35
|
+
"ExperimentReport",
|
|
36
|
+
"MDEType",
|
|
37
|
+
"Metric",
|
|
38
|
+
"MetricType",
|
|
39
|
+
"MultipleTestingResult",
|
|
40
|
+
"ProportionSampleSizeResult",
|
|
41
|
+
"SRMResult",
|
|
42
|
+
"Variant",
|
|
43
|
+
"adjust_p_values",
|
|
44
|
+
"analyze_continuous",
|
|
45
|
+
"analyze_proportion",
|
|
46
|
+
"check_sample_ratio_mismatch",
|
|
47
|
+
"proportion_achieved_power",
|
|
48
|
+
"proportion_sample_size",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
__version__ = "0.1.0"
|