upliftlab 0.26.2__tar.gz → 0.26.4__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.
Files changed (157) hide show
  1. {upliftlab-0.26.2 → upliftlab-0.26.4}/PKG-INFO +8 -5
  2. {upliftlab-0.26.2 → upliftlab-0.26.4}/README.md +6 -3
  3. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/__init__.py +1 -1
  4. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/__main__.py +36 -16
  5. {upliftlab-0.26.2 → upliftlab-0.26.4}/pyproject.toml +5 -3
  6. {upliftlab-0.26.2 → upliftlab-0.26.4}/tests/test_engine.py +22 -0
  7. {upliftlab-0.26.2 → upliftlab-0.26.4}/upliftlab.egg-info/PKG-INFO +8 -5
  8. upliftlab-0.26.4/upliftlab.egg-info/requires.txt +16 -0
  9. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/app.js +5 -5
  10. upliftlab-0.26.4/web/catalog.js +3 -0
  11. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/charts.js +1 -1
  12. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/engine-files.js +1 -1
  13. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/index.html +2 -2
  14. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/report.js +68 -5
  15. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/runtime.js +1 -1
  16. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/store.js +4 -4
  17. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/styles.css +1 -0
  18. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/summary.js +12 -5
  19. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/views/algorithms.js +4 -4
  20. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/views/data.js +2 -2
  21. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/views/home.js +3 -3
  22. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/views/launcher.js +2 -2
  23. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/views/results.js +20 -10
  24. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/views/workspace.js +5 -5
  25. upliftlab-0.26.2/upliftlab.egg-info/requires.txt +0 -16
  26. upliftlab-0.26.2/web/catalog.js +0 -3
  27. {upliftlab-0.26.2 → upliftlab-0.26.4}/LICENSE +0 -0
  28. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/__init__.py +0 -0
  29. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/dml.py +0 -0
  30. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/duality_r_learner.py +0 -0
  31. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/lite/dml.py +0 -0
  32. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/lite/duality_r_learner.py +0 -0
  33. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/lite/r_learner.py +0 -0
  34. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/lite/t_learner.py +0 -0
  35. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/lite/x_learner.py +0 -0
  36. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/r_learner.py +0 -0
  37. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/random_baseline.py +0 -0
  38. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/t_learner.py +0 -0
  39. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/algorithms/x_learner.py +0 -0
  40. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/arena.py +0 -0
  41. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/bridge.py +0 -0
  42. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/contract.py +0 -0
  43. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/datasets.py +0 -0
  44. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/metrics.py +0 -0
  45. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/scenario.py +0 -0
  46. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/server.py +0 -0
  47. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/templates/start_lightgbm.py +0 -0
  48. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/templates/start_pytorch.py +0 -0
  49. {upliftlab-0.26.2 → upliftlab-0.26.4}/engine/upliftlab/templates/start_sklearn.py +0 -0
  50. {upliftlab-0.26.2 → upliftlab-0.26.4}/setup.cfg +0 -0
  51. {upliftlab-0.26.2 → upliftlab-0.26.4}/upliftlab.egg-info/SOURCES.txt +0 -0
  52. {upliftlab-0.26.2 → upliftlab-0.26.4}/upliftlab.egg-info/dependency_links.txt +0 -0
  53. {upliftlab-0.26.2 → upliftlab-0.26.4}/upliftlab.egg-info/entry_points.txt +0 -0
  54. {upliftlab-0.26.2 → upliftlab-0.26.4}/upliftlab.egg-info/top_level.txt +0 -0
  55. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/data/LICENSE-criteo.md +0 -0
  56. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/editor-entry.js +0 -0
  57. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/LICENSE +0 -0
  58. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.0.woff2 +0 -0
  59. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.1.woff2 +0 -0
  60. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.10.woff2 +0 -0
  61. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.11.woff2 +0 -0
  62. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.12.woff2 +0 -0
  63. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.13.woff2 +0 -0
  64. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.14.woff2 +0 -0
  65. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.15.woff2 +0 -0
  66. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.16.woff2 +0 -0
  67. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.17.woff2 +0 -0
  68. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.18.woff2 +0 -0
  69. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.19.woff2 +0 -0
  70. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.2.woff2 +0 -0
  71. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.20.woff2 +0 -0
  72. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.21.woff2 +0 -0
  73. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.22.woff2 +0 -0
  74. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.23.woff2 +0 -0
  75. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.24.woff2 +0 -0
  76. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.25.woff2 +0 -0
  77. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.26.woff2 +0 -0
  78. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.27.woff2 +0 -0
  79. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.28.woff2 +0 -0
  80. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.29.woff2 +0 -0
  81. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.3.woff2 +0 -0
  82. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.30.woff2 +0 -0
  83. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.31.woff2 +0 -0
  84. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.32.woff2 +0 -0
  85. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.33.woff2 +0 -0
  86. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.34.woff2 +0 -0
  87. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.35.woff2 +0 -0
  88. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.36.woff2 +0 -0
  89. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.37.woff2 +0 -0
  90. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.38.woff2 +0 -0
  91. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.39.woff2 +0 -0
  92. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.4.woff2 +0 -0
  93. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.40.woff2 +0 -0
  94. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.41.woff2 +0 -0
  95. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.42.woff2 +0 -0
  96. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.43.woff2 +0 -0
  97. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.44.woff2 +0 -0
  98. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.45.woff2 +0 -0
  99. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.46.woff2 +0 -0
  100. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.47.woff2 +0 -0
  101. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.48.woff2 +0 -0
  102. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.49.woff2 +0 -0
  103. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.5.woff2 +0 -0
  104. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.50.woff2 +0 -0
  105. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.51.woff2 +0 -0
  106. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.52.woff2 +0 -0
  107. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.53.woff2 +0 -0
  108. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.54.woff2 +0 -0
  109. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.55.woff2 +0 -0
  110. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.56.woff2 +0 -0
  111. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.57.woff2 +0 -0
  112. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.58.woff2 +0 -0
  113. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.59.woff2 +0 -0
  114. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.6.woff2 +0 -0
  115. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.60.woff2 +0 -0
  116. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.61.woff2 +0 -0
  117. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.62.woff2 +0 -0
  118. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.63.woff2 +0 -0
  119. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.64.woff2 +0 -0
  120. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.65.woff2 +0 -0
  121. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.66.woff2 +0 -0
  122. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.67.woff2 +0 -0
  123. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.68.woff2 +0 -0
  124. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.69.woff2 +0 -0
  125. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.7.woff2 +0 -0
  126. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.70.woff2 +0 -0
  127. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.71.woff2 +0 -0
  128. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.72.woff2 +0 -0
  129. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.73.woff2 +0 -0
  130. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.74.woff2 +0 -0
  131. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.75.woff2 +0 -0
  132. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.76.woff2 +0 -0
  133. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.77.woff2 +0 -0
  134. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.78.woff2 +0 -0
  135. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.79.woff2 +0 -0
  136. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.8.woff2 +0 -0
  137. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.80.woff2 +0 -0
  138. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.81.woff2 +0 -0
  139. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.82.woff2 +0 -0
  140. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.83.woff2 +0 -0
  141. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.84.woff2 +0 -0
  142. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.85.woff2 +0 -0
  143. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.86.woff2 +0 -0
  144. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.87.woff2 +0 -0
  145. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.88.woff2 +0 -0
  146. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.89.woff2 +0 -0
  147. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.9.woff2 +0 -0
  148. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.90.woff2 +0 -0
  149. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/PretendardVariable.subset.91.woff2 +0 -0
  150. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/archivo-latin-ext-wdth-normal.woff2 +0 -0
  151. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/archivo-latin-wdth-normal.woff2 +0 -0
  152. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/geist-mono-latin-ext-wght-normal.woff2 +0 -0
  153. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/files/geist-mono-latin-wght-normal.woff2 +0 -0
  154. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/fonts/font.css +0 -0
  155. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/lib.js +0 -0
  156. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/vendor/LICENSES.txt +0 -0
  157. {upliftlab-0.26.2 → upliftlab-0.26.4}/web/vendor/editor.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: upliftlab
3
- Version: 0.26.2
3
+ Version: 0.26.4
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
@@ -20,7 +20,7 @@ License-File: web/vendor/LICENSES.txt
20
20
  License-File: web/data/LICENSE-criteo.md
21
21
  Requires-Dist: numpy>=1.21
22
22
  Requires-Dist: scikit-learn>=1.2
23
- Requires-Dist: econml>=0.16; platform_machine != "aarch64" and platform_machine != "ARM64"
23
+ Requires-Dist: econml>=0.16; platform_machine != "aarch64" and platform_machine != "ARM64" and (sys_platform != "darwin" or platform_machine == "arm64")
24
24
  Provides-Extra: gpu
25
25
  Requires-Dist: torch; extra == "gpu"
26
26
  Provides-Extra: templates
@@ -39,11 +39,12 @@ Dynamic: license-file
39
39
 
40
40
  [![Live demo](https://img.shields.io/badge/demo-uplift--lab.vercel.app-4d8dff?style=flat-square)](https://uplift-lab.vercel.app)
41
41
  [![PyPI](https://img.shields.io/pypi/v/upliftlab?style=flat-square&color=11141a&label=pypi)](https://pypi.org/project/upliftlab/)
42
+ [![Tests](https://img.shields.io/github/actions/workflow/status/Funbucket/upliftlab/test.yml?branch=main&style=flat-square&label=tests)](https://github.com/Funbucket/upliftlab/actions/workflows/test.yml)
42
43
  [![Built on EconML](https://img.shields.io/badge/built%20on-EconML-11141a?style=flat-square)](https://github.com/py-why/EconML)
43
44
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-11141a?style=flat-square)](https://github.com/Funbucket/upliftlab/blob/main/pyproject.toml)
44
45
  [![MIT License](https://img.shields.io/badge/license-MIT-11141a?style=flat-square)](https://github.com/Funbucket/upliftlab/blob/main/LICENSE)
45
46
 
46
- **[웹에서 바로 실행 →](https://uplift-lab.vercel.app)**  ·  [로컬 설치](#빠른-시작)  ·  [사용 안내](https://github.com/Funbucket/upliftlab/blob/main/docs/guide.md)
47
+ **[웹에서 바로 실행 →](https://uplift-lab.vercel.app)**  ·  [로컬 설치](#빠른-시작)  ·  [사용 안내](https://github.com/Funbucket/upliftlab/blob/main/docs/guide.md)  ·  [English](https://github.com/Funbucket/upliftlab/blob/main/README.en.md)
47
48
 
48
49
  <sub>위 순위표는 Criteo 광고 실험 199,837명을 예산 40%, 8사이클로 돌린 실제 결과예요(EconML). 사이트에서는 사이클 1부터 8까지 다시 보여 줘요.</sub>
49
50
 
@@ -53,6 +54,8 @@ Dynamic: license-file
53
54
 
54
55
  Qini가 가장 높은 모델이 운영에서도 가장 많이 남길까요? 오프라인 지표 하나로 모델을 고르면, 대상 선정과 재학습을 반복하면서 생기는 차이를 놓쳐요.
55
56
 
57
+ 비용이 사용자마다 다른 합성 쿠폰 시장에서 10개 시드 모두, Qini 1위 모델은 같은 예산에서 루프 1위보다 증분 성과가 평균 17% 적었어요. 비용을 보는 AUCC는 10번 모두 맞혔어요. Criteo에서는 시드마다 1위가 바뀌고, 확실한 차이는 Random과의 차이뿐이었어요. [실험과 재현 방법](https://github.com/Funbucket/upliftlab/blob/main/docs/offline-vs-loop.md)
58
+
56
59
  - **운영 루프를 그대로 돌려요.** 모델마다 대상을 고르고, 처치군과 대조군으로 효과를 재고, 재학습하는 과정을 사이클마다 반복해요.
57
60
  - **같은 조건에서만 비교해요.** 모든 모델이 같은 사용자, 같은 예산, 같은 무작위 Explore 그룹을 써요. 차이는 대상을 고른 방식에서만 생겨요.
58
61
  - **차이가 유의한지 보여 줘요.** 1위와의 차이를 95% CI로 그려서, 순위를 확정할 수 없는 박빙이면 그렇다고 적어요.
@@ -87,7 +90,7 @@ uv를 쓴다면 설치 없이 `uvx upliftlab` 한 줄이면 돼요. 처음 실
87
90
 
88
91
  | ① 데이터 넣기 | ② 비교 시작 | ③ 보고서 공유 |
89
92
  | :-- | :-- | :-- |
90
- | 실험 로그 CSV를 끌어다 놓으면 처치, 결과, 비용, 공변량 열을 알아서 골라요. | 모델, 선정 방식, 예산을 확인하고 `비교 시작`을 눌러요. 끝나면 결과로 바로 내려가요. | 1위 모델, Random 대비 증분 성과, 2위와의 차이를 HTML 보고서, PDF, 메신저 요약으로 보내요. |
93
+ | 실험 로그 CSV를 끌어다 놓으면 처치, 결과, 비용, 공변량 열을 알아서 골라요. | 모델, 선정 방식, 예산을 확인하고 `비교 시작`을 눌러요. 끝나면 결과로 바로 내려가요. | 1위 모델, Random 대비 증분 성과, 2위와의 차이를 HTML 보고서, PDF, 결과 이미지(PNG), 메신저 요약으로 보내요. |
91
94
 
92
95
  ## 기본 모델
93
96
 
@@ -133,7 +136,7 @@ upliftlab run --data my_experiment.csv --algorithm my_model.py
133
136
  | 비용 (선택) | `coupon_cost` | 처치로 쓴 비용. 없으면 처치 1건당 1로 계산해요 |
134
137
  | 공변량 | `visit_freq`, … | 처치 **전에** 측정한 사용자 특성 |
135
138
 
136
- 기본 데이터는 [Criteo](https://ailab.criteo.com/criteo-uplift-prediction-dataset/) 광고 실험이에요. 원본 1,398만 명에서 처치군과 대조군을 10만 명씩 뽑은 표본(199,837명, 결과는 사이트 방문)이 사이트와 패키지에 들어 있어요.
139
+ 기본 데이터는 [Criteo](https://ailab.criteo.com/criteo-uplift-prediction-dataset/) 광고 실험이에요. 원본 1,398만 명에서 처치군과 대조군을 10만 명씩 뽑은 표본(199,837명, 결과는 사이트 방문)이 사이트에 들어 있고, 로컬판은 처음 쓸 때 내려받아요.
137
140
 
138
141
  </details>
139
142
 
@@ -6,11 +6,12 @@
6
6
 
7
7
  [![Live demo](https://img.shields.io/badge/demo-uplift--lab.vercel.app-4d8dff?style=flat-square)](https://uplift-lab.vercel.app)
8
8
  [![PyPI](https://img.shields.io/pypi/v/upliftlab?style=flat-square&color=11141a&label=pypi)](https://pypi.org/project/upliftlab/)
9
+ [![Tests](https://img.shields.io/github/actions/workflow/status/Funbucket/upliftlab/test.yml?branch=main&style=flat-square&label=tests)](https://github.com/Funbucket/upliftlab/actions/workflows/test.yml)
9
10
  [![Built on EconML](https://img.shields.io/badge/built%20on-EconML-11141a?style=flat-square)](https://github.com/py-why/EconML)
10
11
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-11141a?style=flat-square)](https://github.com/Funbucket/upliftlab/blob/main/pyproject.toml)
11
12
  [![MIT License](https://img.shields.io/badge/license-MIT-11141a?style=flat-square)](https://github.com/Funbucket/upliftlab/blob/main/LICENSE)
12
13
 
13
- **[웹에서 바로 실행 →](https://uplift-lab.vercel.app)** &nbsp;·&nbsp; [로컬 설치](#빠른-시작) &nbsp;·&nbsp; [사용 안내](https://github.com/Funbucket/upliftlab/blob/main/docs/guide.md)
14
+ **[웹에서 바로 실행 →](https://uplift-lab.vercel.app)** &nbsp;·&nbsp; [로컬 설치](#빠른-시작) &nbsp;·&nbsp; [사용 안내](https://github.com/Funbucket/upliftlab/blob/main/docs/guide.md) &nbsp;·&nbsp; [English](https://github.com/Funbucket/upliftlab/blob/main/README.en.md)
14
15
 
15
16
  <sub>위 순위표는 Criteo 광고 실험 199,837명을 예산 40%, 8사이클로 돌린 실제 결과예요(EconML). 사이트에서는 사이클 1부터 8까지 다시 보여 줘요.</sub>
16
17
 
@@ -20,6 +21,8 @@
20
21
 
21
22
  Qini가 가장 높은 모델이 운영에서도 가장 많이 남길까요? 오프라인 지표 하나로 모델을 고르면, 대상 선정과 재학습을 반복하면서 생기는 차이를 놓쳐요.
22
23
 
24
+ 비용이 사용자마다 다른 합성 쿠폰 시장에서 10개 시드 모두, Qini 1위 모델은 같은 예산에서 루프 1위보다 증분 성과가 평균 17% 적었어요. 비용을 보는 AUCC는 10번 모두 맞혔어요. Criteo에서는 시드마다 1위가 바뀌고, 확실한 차이는 Random과의 차이뿐이었어요. [실험과 재현 방법](https://github.com/Funbucket/upliftlab/blob/main/docs/offline-vs-loop.md)
25
+
23
26
  - **운영 루프를 그대로 돌려요.** 모델마다 대상을 고르고, 처치군과 대조군으로 효과를 재고, 재학습하는 과정을 사이클마다 반복해요.
24
27
  - **같은 조건에서만 비교해요.** 모든 모델이 같은 사용자, 같은 예산, 같은 무작위 Explore 그룹을 써요. 차이는 대상을 고른 방식에서만 생겨요.
25
28
  - **차이가 유의한지 보여 줘요.** 1위와의 차이를 95% CI로 그려서, 순위를 확정할 수 없는 박빙이면 그렇다고 적어요.
@@ -54,7 +57,7 @@ uv를 쓴다면 설치 없이 `uvx upliftlab` 한 줄이면 돼요. 처음 실
54
57
 
55
58
  | ① 데이터 넣기 | ② 비교 시작 | ③ 보고서 공유 |
56
59
  | :-- | :-- | :-- |
57
- | 실험 로그 CSV를 끌어다 놓으면 처치, 결과, 비용, 공변량 열을 알아서 골라요. | 모델, 선정 방식, 예산을 확인하고 `비교 시작`을 눌러요. 끝나면 결과로 바로 내려가요. | 1위 모델, Random 대비 증분 성과, 2위와의 차이를 HTML 보고서, PDF, 메신저 요약으로 보내요. |
60
+ | 실험 로그 CSV를 끌어다 놓으면 처치, 결과, 비용, 공변량 열을 알아서 골라요. | 모델, 선정 방식, 예산을 확인하고 `비교 시작`을 눌러요. 끝나면 결과로 바로 내려가요. | 1위 모델, Random 대비 증분 성과, 2위와의 차이를 HTML 보고서, PDF, 결과 이미지(PNG), 메신저 요약으로 보내요. |
58
61
 
59
62
  ## 기본 모델
60
63
 
@@ -100,7 +103,7 @@ upliftlab run --data my_experiment.csv --algorithm my_model.py
100
103
  | 비용 (선택) | `coupon_cost` | 처치로 쓴 비용. 없으면 처치 1건당 1로 계산해요 |
101
104
  | 공변량 | `visit_freq`, … | 처치 **전에** 측정한 사용자 특성 |
102
105
 
103
- 기본 데이터는 [Criteo](https://ailab.criteo.com/criteo-uplift-prediction-dataset/) 광고 실험이에요. 원본 1,398만 명에서 처치군과 대조군을 10만 명씩 뽑은 표본(199,837명, 결과는 사이트 방문)이 사이트와 패키지에 들어 있어요.
106
+ 기본 데이터는 [Criteo](https://ailab.criteo.com/criteo-uplift-prediction-dataset/) 광고 실험이에요. 원본 1,398만 명에서 처치군과 대조군을 10만 명씩 뽑은 표본(199,837명, 결과는 사이트 방문)이 사이트에 들어 있고, 로컬판은 처음 쓸 때 내려받아요.
104
107
 
105
108
  </details>
106
109
 
@@ -2,4 +2,4 @@
2
2
 
3
3
  The same package runs in the browser (Pyodide) and natively (`python -m upliftlab serve`).
4
4
  """
5
- __version__ = '0.26.2'
5
+ __version__ = '0.26.4'
@@ -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
- by = result['summary']['by']
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.2"
7
+ version = "0.26.4"
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"
@@ -13,8 +13,10 @@ license-files = ["LICENSE", "web/fonts/LICENSE", "web/vendor/LICENSES.txt", "web
13
13
  keywords = ["uplift modeling", "causal inference", "CATE", "targeting", "marketing", "A/B testing"]
14
14
  classifiers = ["Programming Language :: Python :: 3", "Operating System :: OS Independent", "Intended Audience :: Science/Research", "Topic :: Scientific/Engineering"]
15
15
  # The engine needs only NumPy. The built-in algorithms use EconML (PyWhy) locally and fall back to their scikit-learn versions
16
- # (algorithms/lite) where EconML has no prebuilt wheel: Linux and Windows on ARM. Your own algorithms can use anything you install.
17
- dependencies = ["numpy>=1.21", "scikit-learn>=1.2", "econml>=0.16; platform_machine != 'aarch64' and platform_machine != 'ARM64'"]
16
+ # (algorithms/lite) where EconML has no prebuilt wheel: Linux and Windows on ARM, and Intel Macs. Your own algorithms can use
17
+ # anything you install.
18
+ dependencies = ["numpy>=1.21", "scikit-learn>=1.2",
19
+ "econml>=0.16; platform_machine != 'aarch64' and platform_machine != 'ARM64' and (sys_platform != 'darwin' or platform_machine == 'arm64')"]
18
20
 
19
21
  [project.optional-dependencies]
20
22
  gpu = ["torch"]
@@ -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.2
3
+ Version: 0.26.4
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
@@ -20,7 +20,7 @@ License-File: web/vendor/LICENSES.txt
20
20
  License-File: web/data/LICENSE-criteo.md
21
21
  Requires-Dist: numpy>=1.21
22
22
  Requires-Dist: scikit-learn>=1.2
23
- Requires-Dist: econml>=0.16; platform_machine != "aarch64" and platform_machine != "ARM64"
23
+ Requires-Dist: econml>=0.16; platform_machine != "aarch64" and platform_machine != "ARM64" and (sys_platform != "darwin" or platform_machine == "arm64")
24
24
  Provides-Extra: gpu
25
25
  Requires-Dist: torch; extra == "gpu"
26
26
  Provides-Extra: templates
@@ -39,11 +39,12 @@ Dynamic: license-file
39
39
 
40
40
  [![Live demo](https://img.shields.io/badge/demo-uplift--lab.vercel.app-4d8dff?style=flat-square)](https://uplift-lab.vercel.app)
41
41
  [![PyPI](https://img.shields.io/pypi/v/upliftlab?style=flat-square&color=11141a&label=pypi)](https://pypi.org/project/upliftlab/)
42
+ [![Tests](https://img.shields.io/github/actions/workflow/status/Funbucket/upliftlab/test.yml?branch=main&style=flat-square&label=tests)](https://github.com/Funbucket/upliftlab/actions/workflows/test.yml)
42
43
  [![Built on EconML](https://img.shields.io/badge/built%20on-EconML-11141a?style=flat-square)](https://github.com/py-why/EconML)
43
44
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-11141a?style=flat-square)](https://github.com/Funbucket/upliftlab/blob/main/pyproject.toml)
44
45
  [![MIT License](https://img.shields.io/badge/license-MIT-11141a?style=flat-square)](https://github.com/Funbucket/upliftlab/blob/main/LICENSE)
45
46
 
46
- **[웹에서 바로 실행 →](https://uplift-lab.vercel.app)** &nbsp;·&nbsp; [로컬 설치](#빠른-시작) &nbsp;·&nbsp; [사용 안내](https://github.com/Funbucket/upliftlab/blob/main/docs/guide.md)
47
+ **[웹에서 바로 실행 →](https://uplift-lab.vercel.app)** &nbsp;·&nbsp; [로컬 설치](#빠른-시작) &nbsp;·&nbsp; [사용 안내](https://github.com/Funbucket/upliftlab/blob/main/docs/guide.md) &nbsp;·&nbsp; [English](https://github.com/Funbucket/upliftlab/blob/main/README.en.md)
47
48
 
48
49
  <sub>위 순위표는 Criteo 광고 실험 199,837명을 예산 40%, 8사이클로 돌린 실제 결과예요(EconML). 사이트에서는 사이클 1부터 8까지 다시 보여 줘요.</sub>
49
50
 
@@ -53,6 +54,8 @@ Dynamic: license-file
53
54
 
54
55
  Qini가 가장 높은 모델이 운영에서도 가장 많이 남길까요? 오프라인 지표 하나로 모델을 고르면, 대상 선정과 재학습을 반복하면서 생기는 차이를 놓쳐요.
55
56
 
57
+ 비용이 사용자마다 다른 합성 쿠폰 시장에서 10개 시드 모두, Qini 1위 모델은 같은 예산에서 루프 1위보다 증분 성과가 평균 17% 적었어요. 비용을 보는 AUCC는 10번 모두 맞혔어요. Criteo에서는 시드마다 1위가 바뀌고, 확실한 차이는 Random과의 차이뿐이었어요. [실험과 재현 방법](https://github.com/Funbucket/upliftlab/blob/main/docs/offline-vs-loop.md)
58
+
56
59
  - **운영 루프를 그대로 돌려요.** 모델마다 대상을 고르고, 처치군과 대조군으로 효과를 재고, 재학습하는 과정을 사이클마다 반복해요.
57
60
  - **같은 조건에서만 비교해요.** 모든 모델이 같은 사용자, 같은 예산, 같은 무작위 Explore 그룹을 써요. 차이는 대상을 고른 방식에서만 생겨요.
58
61
  - **차이가 유의한지 보여 줘요.** 1위와의 차이를 95% CI로 그려서, 순위를 확정할 수 없는 박빙이면 그렇다고 적어요.
@@ -87,7 +90,7 @@ uv를 쓴다면 설치 없이 `uvx upliftlab` 한 줄이면 돼요. 처음 실
87
90
 
88
91
  | ① 데이터 넣기 | ② 비교 시작 | ③ 보고서 공유 |
89
92
  | :-- | :-- | :-- |
90
- | 실험 로그 CSV를 끌어다 놓으면 처치, 결과, 비용, 공변량 열을 알아서 골라요. | 모델, 선정 방식, 예산을 확인하고 `비교 시작`을 눌러요. 끝나면 결과로 바로 내려가요. | 1위 모델, Random 대비 증분 성과, 2위와의 차이를 HTML 보고서, PDF, 메신저 요약으로 보내요. |
93
+ | 실험 로그 CSV를 끌어다 놓으면 처치, 결과, 비용, 공변량 열을 알아서 골라요. | 모델, 선정 방식, 예산을 확인하고 `비교 시작`을 눌러요. 끝나면 결과로 바로 내려가요. | 1위 모델, Random 대비 증분 성과, 2위와의 차이를 HTML 보고서, PDF, 결과 이미지(PNG), 메신저 요약으로 보내요. |
91
94
 
92
95
  ## 기본 모델
93
96
 
@@ -133,7 +136,7 @@ upliftlab run --data my_experiment.csv --algorithm my_model.py
133
136
  | 비용 (선택) | `coupon_cost` | 처치로 쓴 비용. 없으면 처치 1건당 1로 계산해요 |
134
137
  | 공변량 | `visit_freq`, … | 처치 **전에** 측정한 사용자 특성 |
135
138
 
136
- 기본 데이터는 [Criteo](https://ailab.criteo.com/criteo-uplift-prediction-dataset/) 광고 실험이에요. 원본 1,398만 명에서 처치군과 대조군을 10만 명씩 뽑은 표본(199,837명, 결과는 사이트 방문)이 사이트와 패키지에 들어 있어요.
139
+ 기본 데이터는 [Criteo](https://ailab.criteo.com/criteo-uplift-prediction-dataset/) 광고 실험이에요. 원본 1,398만 명에서 처치군과 대조군을 10만 명씩 뽑은 표본(199,837명, 결과는 사이트 방문)이 사이트에 들어 있고, 로컬판은 처음 쓸 때 내려받아요.
137
140
 
138
141
  </details>
139
142
 
@@ -0,0 +1,16 @@
1
+ numpy>=1.21
2
+ scikit-learn>=1.2
3
+
4
+ [:platform_machine != "aarch64" and platform_machine != "ARM64" and (sys_platform != "darwin" or platform_machine == "arm64")]
5
+ econml>=0.16
6
+
7
+ [dev]
8
+ pytest
9
+ lightgbm>=4
10
+
11
+ [gpu]
12
+ torch
13
+
14
+ [templates]
15
+ lightgbm>=4
16
+ torch
@@ -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.2';
3
- import {state, subscribe, boot, running} from './store.js?v=0.26.2';
4
- import {workspaceView} from './views/workspace.js?v=0.26.2';
5
- import {algorithmsView} from './views/algorithms.js?v=0.26.2';
6
- import {dataView} from './views/data.js?v=0.26.2';
2
+ import {$, esc, go} from './lib.js?v=0.26.4';
3
+ import {state, subscribe, boot, running} from './store.js?v=0.26.4';
4
+ import {workspaceView} from './views/workspace.js?v=0.26.4';
5
+ import {algorithmsView} from './views/algorithms.js?v=0.26.4';
6
+ import {dataView} from './views/data.js?v=0.26.4';
7
7
 
8
8
  const routes = [
9
9
  [/^\/?$/, workspaceView, () => ({})],