upliftlab 0.26.2__tar.gz → 0.26.3__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.
- {upliftlab-0.26.2 → upliftlab-0.26.3}/PKG-INFO +1 -1
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/__init__.py +1 -1
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/__main__.py +36 -16
- {upliftlab-0.26.2 → upliftlab-0.26.3}/pyproject.toml +1 -1
- {upliftlab-0.26.2 → upliftlab-0.26.3}/tests/test_engine.py +22 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/upliftlab.egg-info/PKG-INFO +1 -1
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/app.js +5 -5
- upliftlab-0.26.3/web/catalog.js +3 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/charts.js +1 -1
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/engine-files.js +1 -1
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/index.html +2 -2
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/report.js +5 -5
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/runtime.js +1 -1
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/store.js +4 -4
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/summary.js +2 -2
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/views/algorithms.js +4 -4
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/views/data.js +2 -2
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/views/home.js +3 -3
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/views/launcher.js +2 -2
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/views/results.js +5 -5
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/views/workspace.js +5 -5
- upliftlab-0.26.2/web/catalog.js +0 -3
- {upliftlab-0.26.2 → upliftlab-0.26.3}/LICENSE +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/README.md +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/__init__.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/dml.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/duality_r_learner.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/lite/dml.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/lite/duality_r_learner.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/lite/r_learner.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/lite/t_learner.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/lite/x_learner.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/r_learner.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/random_baseline.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/t_learner.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/algorithms/x_learner.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/arena.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/bridge.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/contract.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/datasets.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/metrics.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/scenario.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/server.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/templates/start_lightgbm.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/templates/start_pytorch.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/engine/upliftlab/templates/start_sklearn.py +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/setup.cfg +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/upliftlab.egg-info/SOURCES.txt +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/upliftlab.egg-info/dependency_links.txt +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/upliftlab.egg-info/entry_points.txt +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/upliftlab.egg-info/requires.txt +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/upliftlab.egg-info/top_level.txt +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/data/LICENSE-criteo.md +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/editor-entry.js +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/LICENSE +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.0.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.1.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.10.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.11.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.12.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.13.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.14.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.15.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.16.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.17.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.18.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.19.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.2.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.20.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.21.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.22.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.23.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.24.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.25.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.26.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.27.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.28.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.29.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.3.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.30.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.31.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.32.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.33.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.34.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.35.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.36.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.37.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.38.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.39.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.4.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.40.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.41.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.42.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.43.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.44.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.45.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.46.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.47.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.48.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.49.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.5.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.50.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.51.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.52.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.53.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.54.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.55.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.56.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.57.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.58.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.59.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.6.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.60.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.61.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.62.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.63.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.64.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.65.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.66.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.67.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.68.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.69.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.7.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.70.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.71.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.72.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.73.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.74.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.75.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.76.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.77.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.78.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.79.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.8.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.80.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.81.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.82.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.83.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.84.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.85.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.86.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.87.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.88.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.89.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.9.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.90.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/PretendardVariable.subset.91.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/archivo-latin-ext-wdth-normal.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/archivo-latin-wdth-normal.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/geist-mono-latin-ext-wght-normal.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/files/geist-mono-latin-wght-normal.woff2 +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/fonts/font.css +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/lib.js +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/styles.css +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/vendor/LICENSES.txt +0 -0
- {upliftlab-0.26.2 → upliftlab-0.26.3}/web/vendor/editor.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: upliftlab
|
|
3
|
-
Version: 0.26.
|
|
3
|
+
Version: 0.26.3
|
|
4
4
|
Summary: Find the uplift model that gives the most incremental outcome for the same budget, on your own experiment data
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://uplift-lab.vercel.app
|
|
@@ -77,28 +77,48 @@ def main(argv=None):
|
|
|
77
77
|
print(f'{dataset} · {len(algorithms)} algorithms · {args.cycles} cycles · device {devices[0]}', file=sys.stderr)
|
|
78
78
|
bridge.set_reporter(lambda m: (lambda d: d.get('phase') == 'done' and print(f" c{d['cycle']} {d['arm']} done", file=sys.stderr))(json.loads(m)))
|
|
79
79
|
result = bridge.run_all(dataset, algorithms, args.cycles, args.explore, args.target, args.seed, args.mode, args.budget, args.base_learner, args.efficiency)
|
|
80
|
-
|
|
81
|
-
measure = ('incremental' if by.lower().endswith('incr') else 'gain') + (' (truth)' if by.startswith('truth') else ' (observed)')
|
|
82
|
-
volume = by.lower().endswith('incr') # ranked by incremental outcome: the band is the gap to first place, as on the screen
|
|
83
|
-
print(f"\n{'rank':<5}{'algorithm':<26}{measure:>22}{'vs 1st, 95% CI' if volume else 'observed 95% CI':>30}{'AUCC':>8}{'Qini':>8}{'fit ms':>9}")
|
|
84
|
-
for s in sorted(result['summary']['arms'], key=lambda s: s['rank']):
|
|
85
|
-
g, ci, auc, ms = s[by], s['ci'], s['aucc'], s['trainMs']
|
|
86
|
-
gain = '—' if g is None else f'{g:,.0f}' if volume else f'{g:+.1%}'
|
|
87
|
-
q = '—' if s['qini'] is None else f"{s['qini']:.3f}"
|
|
88
|
-
gap = s.get('vsLeader') or {}
|
|
89
|
-
if volume:
|
|
90
|
-
band = '1st' if s['rank'] == 1 else '—' if gap.get('diff') is None or gap.get('ci', [None])[0] is None else f"{gap['diff']:+,.0f} [{gap['ci'][0]:+,.0f}, {gap['ci'][1]:+,.0f}]"
|
|
91
|
-
else:
|
|
92
|
-
band = '—' if ci[0] is None else f'[{ci[0]:+.1%}, {ci[1]:+.1%}]'
|
|
93
|
-
auc = '—' if auc is None else f'{auc:.3f}'
|
|
94
|
-
ms = '—' if ms is None else f'{ms:.0f}'
|
|
95
|
-
print(f"{s['rank']:<5}{s['name'][:25]:<26}{gain:>22}{band:>30}{auc:>8}{q:>8}{ms:>9}")
|
|
80
|
+
print('\n' + '\n'.join(table(result)))
|
|
96
81
|
print('\n' + conclusion(result, args.mode))
|
|
97
82
|
if args.out:
|
|
98
83
|
Path(args.out).write_text(json.dumps({'format': 'upliftlab.run/1', **result}, ensure_ascii=False))
|
|
99
84
|
print(f'\nwrote {args.out}', file=sys.stderr)
|
|
100
85
|
|
|
101
86
|
|
|
87
|
+
def table(result):
|
|
88
|
+
"""The ranking as lines of text. The second header line names each column's basis. The ranked value, its gap to first
|
|
89
|
+
place, AUCC and Qini use the ranking's basis; the 95% CI is always observed, since only observed data has sampling
|
|
90
|
+
error. With synthetic data that adds the observed gap as its own column, which is what an experiment would have shown."""
|
|
91
|
+
arms, by = result['summary']['arms'], result['summary']['by']
|
|
92
|
+
truth, volume = by.startswith('truth'), by.lower().endswith('incr')
|
|
93
|
+
basis, lead = ('truth' if truth else 'observed'), min(arms, key=lambda s: s['rank'])
|
|
94
|
+
dash = '—'
|
|
95
|
+
fix = lambda v, f: dash if v is None else format(v, f) # noqa: E731
|
|
96
|
+
|
|
97
|
+
def observed_gap(s):
|
|
98
|
+
gap = s.get('vsLeader') or {}
|
|
99
|
+
if s['rank'] == 1:
|
|
100
|
+
return '1st'
|
|
101
|
+
if gap.get('diff') is None or (gap.get('ci') or [None])[0] is None:
|
|
102
|
+
return dash
|
|
103
|
+
return f"{gap['diff']:+,.0f} [{gap['ci'][0]:+,.0f}, {gap['ci'][1]:+,.0f}]"
|
|
104
|
+
|
|
105
|
+
columns = [('incremental' if volume else 'gain', basis, 13, lambda s: fix(s[by], ',.0f' if volume else '+.1%'))]
|
|
106
|
+
if volume and truth:
|
|
107
|
+
columns.append(('vs 1st', 'truth', 10, lambda s: '1st' if s['rank'] == 1 else dash if s[by] is None or lead[by] is None else f'{s[by] - lead[by]:+,.0f}'))
|
|
108
|
+
if volume:
|
|
109
|
+
columns.append(('vs 1st, 95% CI', 'observed', 26, observed_gap))
|
|
110
|
+
else:
|
|
111
|
+
columns.append(('95% CI', 'observed', 20, lambda s: dash if s['ci'][0] is None else f"[{s['ci'][0]:+.1%}, {s['ci'][1]:+.1%}]"))
|
|
112
|
+
columns += [('AUCC', basis, 10, lambda s: fix(s['auccTruth' if truth else 'aucc'], '.3f')),
|
|
113
|
+
('Qini', basis, 10, lambda s: fix(s['qiniTruth' if truth else 'qini'], '.3f')),
|
|
114
|
+
('fit ms', '', 8, lambda s: fix(s['trainMs'], '.0f'))]
|
|
115
|
+
lines = [f"{'rank':<5}{'algorithm':<26}" + ''.join(f'{name:>{w}}' for name, _, w, _ in columns),
|
|
116
|
+
' ' * 31 + ''.join(f'{b:>{w}}' for _, b, w, _ in columns)]
|
|
117
|
+
for s in sorted(arms, key=lambda s: s['rank']):
|
|
118
|
+
lines.append(f"{s['rank']:<5}{s['name'][:25]:<26}" + ''.join(f'{cell(s):>{w}}' for _, _, w, cell in columns))
|
|
119
|
+
return lines
|
|
120
|
+
|
|
121
|
+
|
|
102
122
|
def conclusion(result, mode):
|
|
103
123
|
"""One line under the table: who won, by how much over Random, on which basis."""
|
|
104
124
|
arms, by = result['summary']['arms'], result['summary']['by']
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "upliftlab"
|
|
7
|
-
version = "0.26.
|
|
7
|
+
version = "0.26.3"
|
|
8
8
|
description = "Find the uplift model that gives the most incremental outcome for the same budget, on your own experiment data"
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
10
|
readme = "README.md"
|
|
@@ -17,6 +17,7 @@ from upliftlab.arena import Run, knapsack, select, best_cutoff # noqa: E402
|
|
|
17
17
|
from upliftlab.arena import cost_estimator # noqa: E402
|
|
18
18
|
from upliftlab.metrics import aucc, cost_curve, pooled, qini, qini_draws, qini_of_draws, uplift_at # noqa: E402
|
|
19
19
|
from upliftlab.scenario import ReplayScenario, SyntheticScenario # noqa: E402
|
|
20
|
+
from upliftlab.__main__ import table # noqa: E402
|
|
20
21
|
|
|
21
22
|
BUILTINS = {a['id']: {'id': a['id'], 'name': a['name'], 'code': a['code']} for a in bridge.builtins()}
|
|
22
23
|
TEMPLATES = {t['id']: t for t in bridge.templates()}
|
|
@@ -216,6 +217,27 @@ def test_ranking_does_not_depend_on_the_units_of_cost_or_outcome(algorithm):
|
|
|
216
217
|
assert other_gain == pytest.approx(gain, abs=1e-9), units
|
|
217
218
|
|
|
218
219
|
|
|
220
|
+
def test_the_command_line_table_ranks_and_measures_gaps_on_one_basis():
|
|
221
|
+
def arm(rank, name, truth, observed, gap=None):
|
|
222
|
+
return {'rank': rank, 'name': name, 'truthIncR': truth, 'incR': observed, 'ci': [None, None], 'trainMs': 5., 'aucc': .51, 'auccTruth': .61, 'qini': .02, 'qiniTruth': .04,
|
|
223
|
+
'vsLeader': None if gap is None else {'diff': gap, 'ci': [gap - 100, gap + 100], 'tied': False}}
|
|
224
|
+
synthetic = {'summary': {'by': 'truthIncR', 'arms': [arm(1, 'R-Learner', 2342., 2500.), arm(2, 'Random', 2172., 2139., gap=-361.)]}}
|
|
225
|
+
head, basis, first, second = table(synthetic)
|
|
226
|
+
assert head.split()[2:] == ['incremental', 'vs', '1st', 'vs', '1st,', '95%', 'CI', 'AUCC', 'Qini', 'fit', 'ms']
|
|
227
|
+
assert basis.split() == ['truth', 'truth', 'observed', 'truth', 'truth'], 'every column names its basis'
|
|
228
|
+
assert second.split()[2:6] == ['2,172', '-170', '-361', '[-461,'], 'the gap to first place is on the ranking basis, the observed one is its own column'
|
|
229
|
+
assert '0.610' in second and '0.040' in second, 'AUCC and Qini follow the ranking basis'
|
|
230
|
+
observed = {'summary': {'by': 'incR', 'arms': [arm(1, 'R-Learner', None, 2500.), arm(2, 'Random', None, 2139., gap=-361.)]}}
|
|
231
|
+
head, basis, first, second = table(observed)
|
|
232
|
+
assert basis.split() == ['observed', 'observed', 'observed', 'observed'] and second.split()[2:4] == ['2,139', '-361']
|
|
233
|
+
assert '0.510' in second and '0.020' in second
|
|
234
|
+
run = call('run_all', dataset='synthetic/base', algorithms=pick('r_learner', 'random_baseline'), cycles=3, mode='budget')
|
|
235
|
+
lines, s = table(run), sorted(run['summary']['arms'], key=lambda a: a['rank'])
|
|
236
|
+
assert lines[3].split()[3] == f"{s[1]['truthIncR'] - s[0]['truthIncR']:+,.0f}"
|
|
237
|
+
by_gain = table(call('run_all', dataset='synthetic/base', algorithms=pick('r_learner', 'random_baseline'), cycles=3))
|
|
238
|
+
assert by_gain[0].split()[2:4] == ['gain', '95%'] and by_gain[1].split() == ['truth', 'observed', 'truth', 'truth']
|
|
239
|
+
|
|
240
|
+
|
|
219
241
|
def test_qini_and_uplift_at_k_follow_their_definitions():
|
|
220
242
|
line = [{'f': f, 'cost': f * 10, 'value': f * 4} for f in (0, .25, .5, .75, 1)]
|
|
221
243
|
assert qini(line) == pytest.approx(0), 'a straight line is random targeting'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: upliftlab
|
|
3
|
-
Version: 0.26.
|
|
3
|
+
Version: 0.26.3
|
|
4
4
|
Summary: Find the uplift model that gives the most incremental outcome for the same budget, on your own experiment data
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://uplift-lab.vercel.app
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Shell: header, router and the engine chip. Views live in ./views and read from ./store.
|
|
2
|
-
import {$, esc, go} from './lib.js?v=0.26.
|
|
3
|
-
import {state, subscribe, boot, running} from './store.js?v=0.26.
|
|
4
|
-
import {workspaceView} from './views/workspace.js?v=0.26.
|
|
5
|
-
import {algorithmsView} from './views/algorithms.js?v=0.26.
|
|
6
|
-
import {dataView} from './views/data.js?v=0.26.
|
|
2
|
+
import {$, esc, go} from './lib.js?v=0.26.3';
|
|
3
|
+
import {state, subscribe, boot, running} from './store.js?v=0.26.3';
|
|
4
|
+
import {workspaceView} from './views/workspace.js?v=0.26.3';
|
|
5
|
+
import {algorithmsView} from './views/algorithms.js?v=0.26.3';
|
|
6
|
+
import {dataView} from './views/data.js?v=0.26.3';
|
|
7
7
|
|
|
8
8
|
const routes = [
|
|
9
9
|
[/^\/?$/, workspaceView, () => ({})],
|