scitex 2.14.0__py3-none-any.whl → 2.15.3__py3-none-any.whl

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 (264) hide show
  1. scitex/__init__.py +71 -17
  2. scitex/_env_loader.py +156 -0
  3. scitex/_mcp_resources/__init__.py +37 -0
  4. scitex/_mcp_resources/_cheatsheet.py +135 -0
  5. scitex/_mcp_resources/_figrecipe.py +138 -0
  6. scitex/_mcp_resources/_formats.py +102 -0
  7. scitex/_mcp_resources/_modules.py +337 -0
  8. scitex/_mcp_resources/_session.py +149 -0
  9. scitex/_mcp_tools/__init__.py +4 -0
  10. scitex/_mcp_tools/audio.py +66 -0
  11. scitex/_mcp_tools/diagram.py +11 -95
  12. scitex/_mcp_tools/introspect.py +210 -0
  13. scitex/_mcp_tools/plt.py +260 -305
  14. scitex/_mcp_tools/scholar.py +74 -0
  15. scitex/_mcp_tools/social.py +27 -0
  16. scitex/_mcp_tools/template.py +24 -0
  17. scitex/_mcp_tools/writer.py +17 -210
  18. scitex/ai/_gen_ai/_PARAMS.py +10 -7
  19. scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
  20. scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
  21. scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
  22. scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
  23. scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
  24. scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
  25. scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
  26. scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
  27. scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
  28. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +30 -1550
  29. scitex/ai/classification/timeseries/_sliding_window_core.py +467 -0
  30. scitex/ai/classification/timeseries/_sliding_window_plotting.py +369 -0
  31. scitex/audio/README.md +40 -36
  32. scitex/audio/__init__.py +129 -61
  33. scitex/audio/_branding.py +185 -0
  34. scitex/audio/_mcp/__init__.py +32 -0
  35. scitex/audio/_mcp/handlers.py +59 -6
  36. scitex/audio/_mcp/speak_handlers.py +238 -0
  37. scitex/audio/_relay.py +225 -0
  38. scitex/audio/_tts.py +18 -10
  39. scitex/audio/engines/base.py +17 -10
  40. scitex/audio/engines/elevenlabs_engine.py +7 -2
  41. scitex/audio/mcp_server.py +228 -75
  42. scitex/canvas/README.md +1 -1
  43. scitex/canvas/editor/_dearpygui/__init__.py +25 -0
  44. scitex/canvas/editor/_dearpygui/_editor.py +147 -0
  45. scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
  46. scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
  47. scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
  48. scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
  49. scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
  50. scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
  51. scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
  52. scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
  53. scitex/canvas/editor/_dearpygui/_selection.py +295 -0
  54. scitex/canvas/editor/_dearpygui/_state.py +93 -0
  55. scitex/canvas/editor/_dearpygui/_utils.py +61 -0
  56. scitex/canvas/editor/flask_editor/_core/__init__.py +27 -0
  57. scitex/canvas/editor/flask_editor/_core/_bbox_extraction.py +200 -0
  58. scitex/canvas/editor/flask_editor/_core/_editor.py +173 -0
  59. scitex/canvas/editor/flask_editor/_core/_export_helpers.py +353 -0
  60. scitex/canvas/editor/flask_editor/_core/_routes_basic.py +190 -0
  61. scitex/canvas/editor/flask_editor/_core/_routes_export.py +332 -0
  62. scitex/canvas/editor/flask_editor/_core/_routes_panels.py +252 -0
  63. scitex/canvas/editor/flask_editor/_core/_routes_save.py +218 -0
  64. scitex/canvas/editor/flask_editor/_core.py +25 -1684
  65. scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
  66. scitex/cli/__init__.py +38 -43
  67. scitex/cli/audio.py +160 -41
  68. scitex/cli/capture.py +133 -20
  69. scitex/cli/introspect.py +488 -0
  70. scitex/cli/main.py +200 -109
  71. scitex/cli/mcp.py +60 -34
  72. scitex/cli/plt.py +414 -0
  73. scitex/cli/repro.py +15 -8
  74. scitex/cli/resource.py +15 -8
  75. scitex/cli/scholar/__init__.py +154 -8
  76. scitex/cli/scholar/_crossref_scitex.py +296 -0
  77. scitex/cli/scholar/_fetch.py +25 -3
  78. scitex/cli/social.py +355 -0
  79. scitex/cli/stats.py +136 -11
  80. scitex/cli/template.py +129 -12
  81. scitex/cli/tex.py +15 -8
  82. scitex/cli/writer.py +49 -299
  83. scitex/cloud/__init__.py +41 -2
  84. scitex/config/README.md +1 -1
  85. scitex/config/__init__.py +16 -2
  86. scitex/config/_env_registry.py +256 -0
  87. scitex/context/__init__.py +22 -0
  88. scitex/dev/__init__.py +20 -1
  89. scitex/diagram/__init__.py +42 -19
  90. scitex/diagram/mcp_server.py +13 -125
  91. scitex/gen/__init__.py +50 -14
  92. scitex/gen/_list_packages.py +4 -4
  93. scitex/introspect/__init__.py +82 -0
  94. scitex/introspect/_call_graph.py +303 -0
  95. scitex/introspect/_class_hierarchy.py +163 -0
  96. scitex/introspect/_core.py +41 -0
  97. scitex/introspect/_docstring.py +131 -0
  98. scitex/introspect/_examples.py +113 -0
  99. scitex/introspect/_imports.py +271 -0
  100. scitex/{gen/_inspect_module.py → introspect/_list_api.py} +48 -56
  101. scitex/introspect/_mcp/__init__.py +41 -0
  102. scitex/introspect/_mcp/handlers.py +233 -0
  103. scitex/introspect/_members.py +155 -0
  104. scitex/introspect/_resolve.py +89 -0
  105. scitex/introspect/_signature.py +131 -0
  106. scitex/introspect/_source.py +80 -0
  107. scitex/introspect/_type_hints.py +172 -0
  108. scitex/io/_save.py +1 -2
  109. scitex/io/bundle/README.md +1 -1
  110. scitex/logging/_formatters.py +19 -9
  111. scitex/mcp_server.py +98 -5
  112. scitex/os/__init__.py +4 -0
  113. scitex/{gen → os}/_check_host.py +4 -5
  114. scitex/plt/__init__.py +245 -550
  115. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
  116. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  117. scitex/plt/gallery/README.md +1 -1
  118. scitex/plt/utils/_hitmap/__init__.py +82 -0
  119. scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
  120. scitex/plt/utils/_hitmap/_color_application.py +346 -0
  121. scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
  122. scitex/plt/utils/_hitmap/_constants.py +40 -0
  123. scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
  124. scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
  125. scitex/plt/utils/_hitmap/_query.py +113 -0
  126. scitex/plt/utils/_hitmap.py +46 -1616
  127. scitex/plt/utils/_metadata/__init__.py +80 -0
  128. scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
  129. scitex/plt/utils/_metadata/_artists/_base.py +195 -0
  130. scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
  131. scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
  132. scitex/plt/utils/_metadata/_artists/_images.py +80 -0
  133. scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
  134. scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
  135. scitex/plt/utils/_metadata/_artists/_text.py +106 -0
  136. scitex/plt/utils/_metadata/_csv.py +416 -0
  137. scitex/plt/utils/_metadata/_detect.py +225 -0
  138. scitex/plt/utils/_metadata/_legend.py +127 -0
  139. scitex/plt/utils/_metadata/_rounding.py +117 -0
  140. scitex/plt/utils/_metadata/_verification.py +202 -0
  141. scitex/schema/README.md +1 -1
  142. scitex/scholar/__init__.py +8 -0
  143. scitex/scholar/_mcp/crossref_handlers.py +265 -0
  144. scitex/scholar/core/Scholar.py +63 -1700
  145. scitex/scholar/core/_mixins/__init__.py +36 -0
  146. scitex/scholar/core/_mixins/_enrichers.py +270 -0
  147. scitex/scholar/core/_mixins/_library_handlers.py +100 -0
  148. scitex/scholar/core/_mixins/_loaders.py +103 -0
  149. scitex/scholar/core/_mixins/_pdf_download.py +375 -0
  150. scitex/scholar/core/_mixins/_pipeline.py +312 -0
  151. scitex/scholar/core/_mixins/_project_handlers.py +125 -0
  152. scitex/scholar/core/_mixins/_savers.py +69 -0
  153. scitex/scholar/core/_mixins/_search.py +103 -0
  154. scitex/scholar/core/_mixins/_services.py +88 -0
  155. scitex/scholar/core/_mixins/_url_finding.py +105 -0
  156. scitex/scholar/crossref_scitex.py +367 -0
  157. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  158. scitex/scholar/examples/00_run_all.sh +120 -0
  159. scitex/scholar/jobs/_executors.py +27 -3
  160. scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
  161. scitex/scholar/pdf_download/_cli.py +154 -0
  162. scitex/scholar/pdf_download/strategies/__init__.py +11 -8
  163. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
  164. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
  165. scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
  166. scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
  167. scitex/scholar/pipelines/_single_steps.py +71 -36
  168. scitex/scholar/storage/_LibraryManager.py +97 -1695
  169. scitex/scholar/storage/_mixins/__init__.py +30 -0
  170. scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
  171. scitex/scholar/storage/_mixins/_library_operations.py +218 -0
  172. scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
  173. scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
  174. scitex/scholar/storage/_mixins/_resolution.py +376 -0
  175. scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
  176. scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
  177. scitex/security/README.md +3 -3
  178. scitex/session/README.md +1 -1
  179. scitex/session/__init__.py +26 -7
  180. scitex/session/_decorator.py +1 -1
  181. scitex/sh/README.md +1 -1
  182. scitex/sh/__init__.py +7 -4
  183. scitex/social/__init__.py +155 -0
  184. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  185. scitex/stats/_mcp/_handlers/__init__.py +31 -0
  186. scitex/stats/_mcp/_handlers/_corrections.py +113 -0
  187. scitex/stats/_mcp/_handlers/_descriptive.py +78 -0
  188. scitex/stats/_mcp/_handlers/_effect_size.py +106 -0
  189. scitex/stats/_mcp/_handlers/_format.py +94 -0
  190. scitex/stats/_mcp/_handlers/_normality.py +110 -0
  191. scitex/stats/_mcp/_handlers/_posthoc.py +224 -0
  192. scitex/stats/_mcp/_handlers/_power.py +247 -0
  193. scitex/stats/_mcp/_handlers/_recommend.py +102 -0
  194. scitex/stats/_mcp/_handlers/_run_test.py +279 -0
  195. scitex/stats/_mcp/_handlers/_stars.py +48 -0
  196. scitex/stats/_mcp/handlers.py +19 -1171
  197. scitex/stats/auto/_stat_style.py +175 -0
  198. scitex/stats/auto/_style_definitions.py +411 -0
  199. scitex/stats/auto/_styles.py +22 -620
  200. scitex/stats/descriptive/__init__.py +11 -8
  201. scitex/stats/descriptive/_ci.py +39 -0
  202. scitex/stats/power/_power.py +15 -4
  203. scitex/str/__init__.py +2 -1
  204. scitex/str/_title_case.py +63 -0
  205. scitex/template/README.md +1 -1
  206. scitex/template/__init__.py +25 -10
  207. scitex/template/_code_templates.py +147 -0
  208. scitex/template/_mcp/handlers.py +81 -0
  209. scitex/template/_mcp/tool_schemas.py +55 -0
  210. scitex/template/_templates/__init__.py +51 -0
  211. scitex/template/_templates/audio.py +233 -0
  212. scitex/template/_templates/canvas.py +312 -0
  213. scitex/template/_templates/capture.py +268 -0
  214. scitex/template/_templates/config.py +43 -0
  215. scitex/template/_templates/diagram.py +294 -0
  216. scitex/template/_templates/io.py +107 -0
  217. scitex/template/_templates/module.py +53 -0
  218. scitex/template/_templates/plt.py +202 -0
  219. scitex/template/_templates/scholar.py +267 -0
  220. scitex/template/_templates/session.py +130 -0
  221. scitex/template/_templates/session_minimal.py +43 -0
  222. scitex/template/_templates/session_plot.py +67 -0
  223. scitex/template/_templates/session_stats.py +77 -0
  224. scitex/template/_templates/stats.py +323 -0
  225. scitex/template/_templates/writer.py +296 -0
  226. scitex/template/clone_writer_directory.py +5 -5
  227. scitex/ui/_backends/_email.py +10 -2
  228. scitex/ui/_backends/_webhook.py +5 -1
  229. scitex/web/_search_pubmed.py +10 -6
  230. scitex/writer/README.md +1 -1
  231. scitex/writer/__init__.py +43 -34
  232. scitex/writer/_mcp/handlers.py +11 -744
  233. scitex/writer/_mcp/tool_schemas.py +5 -335
  234. scitex-2.15.3.dist-info/METADATA +667 -0
  235. {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/RECORD +241 -120
  236. scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
  237. scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
  238. scitex/diagram/_compile.py +0 -312
  239. scitex/diagram/_diagram.py +0 -355
  240. scitex/diagram/_mcp/__init__.py +0 -4
  241. scitex/diagram/_mcp/handlers.py +0 -400
  242. scitex/diagram/_mcp/tool_schemas.py +0 -157
  243. scitex/diagram/_presets.py +0 -173
  244. scitex/diagram/_schema.py +0 -182
  245. scitex/diagram/_split.py +0 -278
  246. scitex/gen/_ci.py +0 -12
  247. scitex/gen/_title_case.py +0 -89
  248. scitex/plt/_mcp/__init__.py +0 -4
  249. scitex/plt/_mcp/_handlers_annotation.py +0 -102
  250. scitex/plt/_mcp/_handlers_figure.py +0 -195
  251. scitex/plt/_mcp/_handlers_plot.py +0 -252
  252. scitex/plt/_mcp/_handlers_style.py +0 -219
  253. scitex/plt/_mcp/handlers.py +0 -74
  254. scitex/plt/_mcp/tool_schemas.py +0 -497
  255. scitex/plt/mcp_server.py +0 -231
  256. scitex/scholar/examples/SUGGESTIONS.md +0 -865
  257. scitex/scholar/examples/dev.py +0 -38
  258. scitex-2.14.0.dist-info/METADATA +0 -1238
  259. /scitex/{gen → context}/_detect_environment.py +0 -0
  260. /scitex/{gen → context}/_get_notebook_path.py +0 -0
  261. /scitex/{gen/_shell.py → sh/_shell_legacy.py} +0 -0
  262. {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/WHEEL +0 -0
  263. {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/entry_points.txt +0 -0
  264. {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-01-25
3
+ # File: src/scitex/stats/_mcp/_handlers/_run_test.py
4
+
5
+ """Statistical test execution handler."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ from datetime import datetime
11
+
12
+ import numpy as np
13
+
14
+ __all__ = ["run_test_handler"]
15
+
16
+
17
+ async def run_test_handler(
18
+ test_name: str,
19
+ data: list[list[float]],
20
+ alternative: str = "two-sided",
21
+ ) -> dict:
22
+ """Execute a statistical test on provided data."""
23
+ try:
24
+ from scipy import stats as scipy_stats
25
+
26
+ loop = asyncio.get_event_loop()
27
+
28
+ def do_test():
29
+ # Convert data to numpy arrays
30
+ groups = [np.array(g, dtype=float) for g in data]
31
+
32
+ result = {}
33
+
34
+ # Run the appropriate test
35
+ if test_name == "ttest_ind":
36
+ result = _run_ttest_ind(groups, alternative, scipy_stats)
37
+ elif test_name == "ttest_paired":
38
+ result = _run_ttest_paired(groups, alternative, scipy_stats)
39
+ elif test_name == "ttest_1samp":
40
+ result = _run_ttest_1samp(groups, alternative, scipy_stats)
41
+ elif test_name == "brunner_munzel":
42
+ result = _run_brunner_munzel(groups, alternative, scipy_stats)
43
+ elif test_name == "mannwhitneyu":
44
+ result = _run_mannwhitneyu(groups, alternative, scipy_stats)
45
+ elif test_name == "wilcoxon":
46
+ result = _run_wilcoxon(groups, alternative, scipy_stats)
47
+ elif test_name == "anova":
48
+ result = _run_anova(groups, scipy_stats)
49
+ elif test_name == "kruskal":
50
+ result = _run_kruskal(groups, scipy_stats)
51
+ elif test_name == "chi2":
52
+ result = _run_chi2(data, scipy_stats)
53
+ elif test_name == "fisher_exact":
54
+ result = _run_fisher_exact(data, alternative, scipy_stats)
55
+ elif test_name == "pearson":
56
+ result = _run_pearson(groups, scipy_stats)
57
+ elif test_name == "spearman":
58
+ result = _run_spearman(groups, scipy_stats)
59
+ elif test_name == "kendall":
60
+ result = _run_kendall(groups, scipy_stats)
61
+ else:
62
+ raise ValueError(f"Unknown test: {test_name}")
63
+
64
+ # Calculate effect size if applicable
65
+ if test_name in [
66
+ "ttest_ind",
67
+ "ttest_paired",
68
+ "brunner_munzel",
69
+ "mannwhitneyu",
70
+ ]:
71
+ result = _add_effect_size(result, groups)
72
+
73
+ # Add significance determination
74
+ alpha = 0.05
75
+ result["significant"] = result["p_value"] < alpha
76
+ result["alpha"] = alpha
77
+
78
+ return result
79
+
80
+ result = await loop.run_in_executor(None, do_test)
81
+
82
+ return {
83
+ "success": True,
84
+ "test_name": test_name,
85
+ "alternative": alternative,
86
+ **result,
87
+ "timestamp": datetime.now().isoformat(),
88
+ }
89
+
90
+ except Exception as e:
91
+ return {"success": False, "error": str(e)}
92
+
93
+
94
+ def _run_ttest_ind(groups, alternative, scipy_stats):
95
+ if len(groups) != 2:
96
+ raise ValueError("t-test requires exactly 2 groups")
97
+ stat, p_value = scipy_stats.ttest_ind(groups[0], groups[1], alternative=alternative)
98
+ df = len(groups[0]) + len(groups[1]) - 2
99
+ return {
100
+ "test": "Independent t-test",
101
+ "statistic": float(stat),
102
+ "statistic_name": "t",
103
+ "p_value": float(p_value),
104
+ "df": df,
105
+ }
106
+
107
+
108
+ def _run_ttest_paired(groups, alternative, scipy_stats):
109
+ if len(groups) != 2:
110
+ raise ValueError("Paired t-test requires exactly 2 groups")
111
+ stat, p_value = scipy_stats.ttest_rel(groups[0], groups[1], alternative=alternative)
112
+ df = len(groups[0]) - 1
113
+ return {
114
+ "test": "Paired t-test",
115
+ "statistic": float(stat),
116
+ "statistic_name": "t",
117
+ "p_value": float(p_value),
118
+ "df": df,
119
+ }
120
+
121
+
122
+ def _run_ttest_1samp(groups, alternative, scipy_stats):
123
+ if len(groups) != 1:
124
+ raise ValueError("One-sample t-test requires exactly 1 group")
125
+ stat, p_value = scipy_stats.ttest_1samp(groups[0], 0, alternative=alternative)
126
+ df = len(groups[0]) - 1
127
+ return {
128
+ "test": "One-sample t-test",
129
+ "statistic": float(stat),
130
+ "statistic_name": "t",
131
+ "p_value": float(p_value),
132
+ "df": df,
133
+ }
134
+
135
+
136
+ def _run_brunner_munzel(groups, alternative, scipy_stats):
137
+ if len(groups) != 2:
138
+ raise ValueError("Brunner-Munzel requires exactly 2 groups")
139
+ res = scipy_stats.brunnermunzel(groups[0], groups[1], alternative=alternative)
140
+ return {
141
+ "test": "Brunner-Munzel test",
142
+ "statistic": float(res.statistic),
143
+ "statistic_name": "BM",
144
+ "p_value": float(res.pvalue),
145
+ }
146
+
147
+
148
+ def _run_mannwhitneyu(groups, alternative, scipy_stats):
149
+ if len(groups) != 2:
150
+ raise ValueError("Mann-Whitney U requires exactly 2 groups")
151
+ stat, p_value = scipy_stats.mannwhitneyu(
152
+ groups[0], groups[1], alternative=alternative
153
+ )
154
+ return {
155
+ "test": "Mann-Whitney U test",
156
+ "statistic": float(stat),
157
+ "statistic_name": "U",
158
+ "p_value": float(p_value),
159
+ }
160
+
161
+
162
+ def _run_wilcoxon(groups, alternative, scipy_stats):
163
+ if len(groups) != 2:
164
+ raise ValueError("Wilcoxon requires exactly 2 paired groups")
165
+ stat, p_value = scipy_stats.wilcoxon(groups[0], groups[1], alternative=alternative)
166
+ return {
167
+ "test": "Wilcoxon signed-rank test",
168
+ "statistic": float(stat),
169
+ "statistic_name": "W",
170
+ "p_value": float(p_value),
171
+ }
172
+
173
+
174
+ def _run_anova(groups, scipy_stats):
175
+ if len(groups) < 2:
176
+ raise ValueError("ANOVA requires at least 2 groups")
177
+ stat, p_value = scipy_stats.f_oneway(*groups)
178
+ df_between = len(groups) - 1
179
+ df_within = sum(len(g) for g in groups) - len(groups)
180
+ return {
181
+ "test": "One-way ANOVA",
182
+ "statistic": float(stat),
183
+ "statistic_name": "F",
184
+ "p_value": float(p_value),
185
+ "df_between": df_between,
186
+ "df_within": df_within,
187
+ }
188
+
189
+
190
+ def _run_kruskal(groups, scipy_stats):
191
+ if len(groups) < 2:
192
+ raise ValueError("Kruskal-Wallis requires at least 2 groups")
193
+ stat, p_value = scipy_stats.kruskal(*groups)
194
+ return {
195
+ "test": "Kruskal-Wallis H test",
196
+ "statistic": float(stat),
197
+ "statistic_name": "H",
198
+ "p_value": float(p_value),
199
+ "df": len(groups) - 1,
200
+ }
201
+
202
+
203
+ def _run_chi2(data, scipy_stats):
204
+ table = np.array(data)
205
+ chi2, p_value, dof, expected = scipy_stats.chi2_contingency(table)
206
+ return {
207
+ "test": "Chi-square test of independence",
208
+ "statistic": float(chi2),
209
+ "statistic_name": "chi2",
210
+ "p_value": float(p_value),
211
+ "df": int(dof),
212
+ "expected_frequencies": expected.tolist(),
213
+ }
214
+
215
+
216
+ def _run_fisher_exact(data, alternative, scipy_stats):
217
+ table = np.array(data)
218
+ if table.shape != (2, 2):
219
+ raise ValueError("Fisher's exact test requires a 2x2 table")
220
+ odds_ratio, p_value = scipy_stats.fisher_exact(table, alternative=alternative)
221
+ return {
222
+ "test": "Fisher's exact test",
223
+ "statistic": float(odds_ratio),
224
+ "statistic_name": "odds_ratio",
225
+ "p_value": float(p_value),
226
+ }
227
+
228
+
229
+ def _run_pearson(groups, scipy_stats):
230
+ if len(groups) != 2:
231
+ raise ValueError("Pearson correlation requires exactly 2 variables")
232
+ r, p_value = scipy_stats.pearsonr(groups[0], groups[1])
233
+ return {
234
+ "test": "Pearson correlation",
235
+ "statistic": float(r),
236
+ "statistic_name": "r",
237
+ "p_value": float(p_value),
238
+ }
239
+
240
+
241
+ def _run_spearman(groups, scipy_stats):
242
+ if len(groups) != 2:
243
+ raise ValueError("Spearman correlation requires exactly 2 variables")
244
+ r, p_value = scipy_stats.spearmanr(groups[0], groups[1])
245
+ return {
246
+ "test": "Spearman correlation",
247
+ "statistic": float(r),
248
+ "statistic_name": "rho",
249
+ "p_value": float(p_value),
250
+ }
251
+
252
+
253
+ def _run_kendall(groups, scipy_stats):
254
+ if len(groups) != 2:
255
+ raise ValueError("Kendall correlation requires exactly 2 variables")
256
+ tau, p_value = scipy_stats.kendalltau(groups[0], groups[1])
257
+ return {
258
+ "test": "Kendall tau correlation",
259
+ "statistic": float(tau),
260
+ "statistic_name": "tau",
261
+ "p_value": float(p_value),
262
+ }
263
+
264
+
265
+ def _add_effect_size(result, groups):
266
+ """Add effect size calculations to result."""
267
+ from scitex.stats.effect_sizes import cliffs_delta, cohens_d
268
+
269
+ if len(groups) == 2:
270
+ d = cohens_d(groups[0], groups[1])
271
+ delta = cliffs_delta(groups[0], groups[1])
272
+ result["effect_size"] = {
273
+ "cohens_d": float(d),
274
+ "cliffs_delta": float(delta),
275
+ }
276
+ return result
277
+
278
+
279
+ # EOF
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-01-25
3
+ # File: src/scitex/stats/_mcp/_handlers/_stars.py
4
+
5
+ """P-value to stars conversion handler."""
6
+
7
+ from __future__ import annotations
8
+
9
+ from datetime import datetime
10
+
11
+ __all__ = ["p_to_stars_handler"]
12
+
13
+
14
+ async def p_to_stars_handler(
15
+ p_value: float,
16
+ thresholds: list[float] | None = None,
17
+ ) -> dict:
18
+ """Convert p-value to significance stars."""
19
+ try:
20
+ thresh = thresholds or [0.001, 0.01, 0.05]
21
+
22
+ if p_value < thresh[0]:
23
+ stars = "***"
24
+ significance = f"p < {thresh[0]}"
25
+ elif p_value < thresh[1]:
26
+ stars = "**"
27
+ significance = f"p < {thresh[1]}"
28
+ elif p_value < thresh[2]:
29
+ stars = "*"
30
+ significance = f"p < {thresh[2]}"
31
+ else:
32
+ stars = "ns"
33
+ significance = f"p >= {thresh[2]} (not significant)"
34
+
35
+ return {
36
+ "success": True,
37
+ "p_value": p_value,
38
+ "stars": stars,
39
+ "significance": significance,
40
+ "thresholds": thresh,
41
+ "timestamp": datetime.now().isoformat(),
42
+ }
43
+
44
+ except Exception as e:
45
+ return {"success": False, "error": str(e)}
46
+
47
+
48
+ # EOF