scitex 2.14.0__py3-none-any.whl → 2.15.1__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 (218) hide show
  1. scitex/__init__.py +47 -0
  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 +191 -0
  13. scitex/_mcp_tools/plt.py +260 -305
  14. scitex/_mcp_tools/scholar.py +74 -0
  15. scitex/_mcp_tools/social.py +244 -0
  16. scitex/_mcp_tools/writer.py +21 -204
  17. scitex/ai/_gen_ai/_PARAMS.py +10 -7
  18. scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
  19. scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
  20. scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
  21. scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
  22. scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
  23. scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
  24. scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
  25. scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
  26. scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
  27. scitex/audio/README.md +40 -36
  28. scitex/audio/__init__.py +127 -59
  29. scitex/audio/_branding.py +185 -0
  30. scitex/audio/_mcp/__init__.py +32 -0
  31. scitex/audio/_mcp/handlers.py +59 -6
  32. scitex/audio/_mcp/speak_handlers.py +238 -0
  33. scitex/audio/_relay.py +225 -0
  34. scitex/audio/engines/elevenlabs_engine.py +6 -1
  35. scitex/audio/mcp_server.py +228 -75
  36. scitex/canvas/README.md +1 -1
  37. scitex/canvas/editor/_dearpygui/__init__.py +25 -0
  38. scitex/canvas/editor/_dearpygui/_editor.py +147 -0
  39. scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
  40. scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
  41. scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
  42. scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
  43. scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
  44. scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
  45. scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
  46. scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
  47. scitex/canvas/editor/_dearpygui/_selection.py +295 -0
  48. scitex/canvas/editor/_dearpygui/_state.py +93 -0
  49. scitex/canvas/editor/_dearpygui/_utils.py +61 -0
  50. scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
  51. scitex/cli/__init__.py +38 -43
  52. scitex/cli/audio.py +76 -27
  53. scitex/cli/capture.py +13 -20
  54. scitex/cli/introspect.py +443 -0
  55. scitex/cli/main.py +198 -109
  56. scitex/cli/mcp.py +60 -34
  57. scitex/cli/scholar/__init__.py +8 -0
  58. scitex/cli/scholar/_crossref_scitex.py +296 -0
  59. scitex/cli/scholar/_fetch.py +25 -3
  60. scitex/cli/social.py +314 -0
  61. scitex/cli/writer.py +117 -0
  62. scitex/config/README.md +1 -1
  63. scitex/config/__init__.py +16 -2
  64. scitex/config/_env_registry.py +191 -0
  65. scitex/diagram/__init__.py +42 -19
  66. scitex/diagram/mcp_server.py +13 -125
  67. scitex/introspect/__init__.py +75 -0
  68. scitex/introspect/_call_graph.py +303 -0
  69. scitex/introspect/_class_hierarchy.py +163 -0
  70. scitex/introspect/_core.py +42 -0
  71. scitex/introspect/_docstring.py +131 -0
  72. scitex/introspect/_examples.py +113 -0
  73. scitex/introspect/_imports.py +271 -0
  74. scitex/introspect/_mcp/__init__.py +37 -0
  75. scitex/introspect/_mcp/handlers.py +208 -0
  76. scitex/introspect/_members.py +151 -0
  77. scitex/introspect/_resolve.py +89 -0
  78. scitex/introspect/_signature.py +131 -0
  79. scitex/introspect/_source.py +80 -0
  80. scitex/introspect/_type_hints.py +172 -0
  81. scitex/io/bundle/README.md +1 -1
  82. scitex/mcp_server.py +98 -5
  83. scitex/plt/__init__.py +248 -550
  84. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
  85. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  86. scitex/plt/gallery/README.md +1 -1
  87. scitex/plt/utils/_hitmap/__init__.py +82 -0
  88. scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
  89. scitex/plt/utils/_hitmap/_color_application.py +346 -0
  90. scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
  91. scitex/plt/utils/_hitmap/_constants.py +40 -0
  92. scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
  93. scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
  94. scitex/plt/utils/_hitmap/_query.py +113 -0
  95. scitex/plt/utils/_hitmap.py +46 -1616
  96. scitex/plt/utils/_metadata/__init__.py +80 -0
  97. scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
  98. scitex/plt/utils/_metadata/_artists/_base.py +195 -0
  99. scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
  100. scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
  101. scitex/plt/utils/_metadata/_artists/_images.py +80 -0
  102. scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
  103. scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
  104. scitex/plt/utils/_metadata/_artists/_text.py +106 -0
  105. scitex/plt/utils/_metadata/_csv.py +416 -0
  106. scitex/plt/utils/_metadata/_detect.py +225 -0
  107. scitex/plt/utils/_metadata/_legend.py +127 -0
  108. scitex/plt/utils/_metadata/_rounding.py +117 -0
  109. scitex/plt/utils/_metadata/_verification.py +202 -0
  110. scitex/schema/README.md +1 -1
  111. scitex/scholar/__init__.py +8 -0
  112. scitex/scholar/_mcp/crossref_handlers.py +265 -0
  113. scitex/scholar/core/Scholar.py +63 -1700
  114. scitex/scholar/core/_mixins/__init__.py +36 -0
  115. scitex/scholar/core/_mixins/_enrichers.py +270 -0
  116. scitex/scholar/core/_mixins/_library_handlers.py +100 -0
  117. scitex/scholar/core/_mixins/_loaders.py +103 -0
  118. scitex/scholar/core/_mixins/_pdf_download.py +375 -0
  119. scitex/scholar/core/_mixins/_pipeline.py +312 -0
  120. scitex/scholar/core/_mixins/_project_handlers.py +125 -0
  121. scitex/scholar/core/_mixins/_savers.py +69 -0
  122. scitex/scholar/core/_mixins/_search.py +103 -0
  123. scitex/scholar/core/_mixins/_services.py +88 -0
  124. scitex/scholar/core/_mixins/_url_finding.py +105 -0
  125. scitex/scholar/crossref_scitex.py +367 -0
  126. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  127. scitex/scholar/examples/00_run_all.sh +120 -0
  128. scitex/scholar/jobs/_executors.py +27 -3
  129. scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
  130. scitex/scholar/pdf_download/_cli.py +154 -0
  131. scitex/scholar/pdf_download/strategies/__init__.py +11 -8
  132. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
  133. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
  134. scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
  135. scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
  136. scitex/scholar/pipelines/_single_steps.py +71 -36
  137. scitex/scholar/storage/_LibraryManager.py +97 -1695
  138. scitex/scholar/storage/_mixins/__init__.py +30 -0
  139. scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
  140. scitex/scholar/storage/_mixins/_library_operations.py +218 -0
  141. scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
  142. scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
  143. scitex/scholar/storage/_mixins/_resolution.py +376 -0
  144. scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
  145. scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
  146. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
  147. scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
  148. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
  149. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
  150. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
  151. scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
  152. scitex/security/README.md +3 -3
  153. scitex/session/README.md +1 -1
  154. scitex/sh/README.md +1 -1
  155. scitex/social/__init__.py +153 -0
  156. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  157. scitex/template/README.md +1 -1
  158. scitex/template/clone_writer_directory.py +5 -5
  159. scitex/writer/README.md +1 -1
  160. scitex/writer/_mcp/handlers.py +11 -744
  161. scitex/writer/_mcp/tool_schemas.py +5 -335
  162. scitex-2.15.1.dist-info/METADATA +648 -0
  163. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/RECORD +166 -111
  164. scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
  165. scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
  166. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
  167. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
  168. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
  169. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
  170. scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
  171. scitex/diagram/_compile.py +0 -312
  172. scitex/diagram/_diagram.py +0 -355
  173. scitex/diagram/_mcp/__init__.py +0 -4
  174. scitex/diagram/_mcp/handlers.py +0 -400
  175. scitex/diagram/_mcp/tool_schemas.py +0 -157
  176. scitex/diagram/_presets.py +0 -173
  177. scitex/diagram/_schema.py +0 -182
  178. scitex/diagram/_split.py +0 -278
  179. scitex/plt/_mcp/__init__.py +0 -4
  180. scitex/plt/_mcp/_handlers_annotation.py +0 -102
  181. scitex/plt/_mcp/_handlers_figure.py +0 -195
  182. scitex/plt/_mcp/_handlers_plot.py +0 -252
  183. scitex/plt/_mcp/_handlers_style.py +0 -219
  184. scitex/plt/_mcp/handlers.py +0 -74
  185. scitex/plt/_mcp/tool_schemas.py +0 -497
  186. scitex/plt/mcp_server.py +0 -231
  187. scitex/scholar/data/.gitkeep +0 -0
  188. scitex/scholar/data/README.md +0 -44
  189. scitex/scholar/data/bib_files/bibliography.bib +0 -1952
  190. scitex/scholar/data/bib_files/neurovista.bib +0 -277
  191. scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
  192. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
  193. scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
  194. scitex/scholar/data/bib_files/openaccess.bib +0 -89
  195. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
  196. scitex/scholar/data/bib_files/pac.bib +0 -698
  197. scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
  198. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  199. scitex/scholar/data/bib_files/pac_titles.txt +0 -75
  200. scitex/scholar/data/bib_files/paywalled.bib +0 -98
  201. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
  202. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
  203. scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
  204. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  205. scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
  206. scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
  207. scitex/scholar/data/bib_files/test_seizure.bib +0 -46
  208. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  209. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  210. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  211. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  212. scitex/scholar/data/impact_factor.db +0 -0
  213. scitex/scholar/examples/SUGGESTIONS.md +0 -865
  214. scitex/scholar/examples/dev.py +0 -38
  215. scitex-2.14.0.dist-info/METADATA +0 -1238
  216. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/WHEEL +0 -0
  217. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/entry_points.txt +0 -0
  218. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-01-24 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/plt/utils/_metadata/_artists/_text.py
4
+
5
+ """
6
+ Text artist extraction.
7
+
8
+ Handles Text annotations and labels.
9
+ """
10
+
11
+ from typing import List
12
+
13
+ from ._base import ExtractionContext, color_to_hex
14
+
15
+
16
+ def extract_text(ctx: ExtractionContext) -> List[dict]:
17
+ """Extract text artists from axes."""
18
+ artists = []
19
+ text_count = 0
20
+
21
+ for i, text_obj in enumerate(ctx.mpl_ax.texts):
22
+ text_content = text_obj.get_text()
23
+ if not text_content or text_content.strip() == "":
24
+ continue
25
+
26
+ artist = _extract_text_artist(ctx, text_count, text_obj, text_content)
27
+ if artist:
28
+ artists.append(artist)
29
+ text_count += 1
30
+
31
+ return artists
32
+
33
+
34
+ def _extract_text_artist(
35
+ ctx: ExtractionContext, index: int, text_obj, text_content: str
36
+ ) -> dict:
37
+ """Extract Text artist."""
38
+ artist = {}
39
+
40
+ scitex_id = getattr(text_obj, "_scitex_id", None)
41
+
42
+ if scitex_id:
43
+ artist["id"] = scitex_id
44
+ else:
45
+ artist["id"] = f"text_{index}"
46
+
47
+ # Semantic layer
48
+ artist["mark"] = "text"
49
+
50
+ # Determine role from content
51
+ pos = text_obj.get_position()
52
+ if any(kw in text_content.lower() for kw in ["r=", "p=", "r²=", "n="]):
53
+ artist["role"] = "stats_annotation"
54
+ else:
55
+ artist["role"] = "annotation"
56
+
57
+ artist["legend_included"] = False
58
+ artist["zorder"] = text_obj.get_zorder()
59
+
60
+ # Geometry
61
+ artist["geometry"] = {
62
+ "x": pos[0],
63
+ "y": pos[1],
64
+ }
65
+
66
+ # Text content
67
+ artist["text"] = text_content
68
+
69
+ # Backend layer
70
+ backend = {
71
+ "name": "matplotlib",
72
+ "artist_class": type(text_obj).__name__,
73
+ "props": {},
74
+ }
75
+
76
+ try:
77
+ color = text_obj.get_color()
78
+ backend["props"]["color"] = color_to_hex(color)
79
+ except (ValueError, TypeError):
80
+ pass
81
+
82
+ try:
83
+ backend["props"]["fontsize_pt"] = text_obj.get_fontsize()
84
+ except (ValueError, TypeError):
85
+ pass
86
+
87
+ try:
88
+ backend["props"]["ha"] = text_obj.get_ha()
89
+ backend["props"]["va"] = text_obj.get_va()
90
+ except (ValueError, TypeError):
91
+ pass
92
+
93
+ artist["backend"] = backend
94
+
95
+ # Data reference for tracked text
96
+ if scitex_id:
97
+ artist["data_ref"] = {
98
+ "x": f"text_{index}_x",
99
+ "y": f"text_{index}_y",
100
+ "content": f"text_{index}_content",
101
+ }
102
+
103
+ return artist
104
+
105
+
106
+ # EOF
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-01-24 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/plt/utils/_metadata/_csv.py
4
+
5
+ """
6
+ CSV column naming and hash utilities for figure metadata.
7
+
8
+ Provides functions for extracting CSV column information from scitex history
9
+ and computing data hashes for reproducibility verification.
10
+ """
11
+
12
+ from typing import List, Optional
13
+
14
+
15
+ def _get_csv_column_names(
16
+ trace_id: str, ax_row: int = 0, ax_col: int = 0, variables: list = None
17
+ ) -> dict:
18
+ """
19
+ Get the CSV column names for a given trace.
20
+
21
+ Parameters
22
+ ----------
23
+ trace_id : str
24
+ Unique ID of the trace (e.g., "plot_0", "scatter_1")
25
+ ax_row : int
26
+ Row position of axes in grid (default: 0)
27
+ ax_col : int
28
+ Column position of axes in grid (default: 0)
29
+ variables : list, optional
30
+ List of variable names (default: ["x", "y"])
31
+
32
+ Returns
33
+ -------
34
+ dict
35
+ Dictionary mapping variable names to CSV column names
36
+ """
37
+ from .._csv_column_naming import get_csv_column_name
38
+
39
+ if variables is None:
40
+ variables = ["x", "y"]
41
+
42
+ data_ref = {}
43
+ for var in variables:
44
+ data_ref[var] = get_csv_column_name(var, ax_row, ax_col, trace_id=trace_id)
45
+
46
+ return data_ref
47
+
48
+
49
+ def _extract_csv_columns_from_history(ax) -> list:
50
+ """
51
+ Extract CSV column names from scitex history for all plot types.
52
+
53
+ Parameters
54
+ ----------
55
+ ax : AxisWrapper or matplotlib.axes.Axes
56
+ The axes to extract CSV column info from
57
+
58
+ Returns
59
+ -------
60
+ list
61
+ List of dictionaries containing CSV column mappings for each tracked plot
62
+ """
63
+ # Get axes position for CSV column naming
64
+ ax_row, ax_col = 0, 0
65
+ if hasattr(ax, "_scitex_metadata") and "position_in_grid" in ax._scitex_metadata:
66
+ pos = ax._scitex_metadata["position_in_grid"]
67
+ ax_row, ax_col = pos[0], pos[1]
68
+
69
+ csv_columns_list = []
70
+
71
+ if not hasattr(ax, "history") or len(ax.history) == 0:
72
+ return csv_columns_list
73
+
74
+ for trace_index, (record_id, record) in enumerate(ax.history.items()):
75
+ if not isinstance(record, tuple) or len(record) < 4:
76
+ continue
77
+
78
+ id_val, method, tracked_dict, kwargs = record
79
+
80
+ columns = _get_csv_columns_for_method_with_index(
81
+ id_val, method, tracked_dict, kwargs, ax_row, ax_col, trace_index
82
+ )
83
+
84
+ if columns:
85
+ csv_columns_list.append(
86
+ {
87
+ "id": id_val,
88
+ "method": method,
89
+ "columns": columns,
90
+ }
91
+ )
92
+
93
+ return csv_columns_list
94
+
95
+
96
+ def _get_csv_columns_for_method_with_index(
97
+ id_val, method, tracked_dict, kwargs, ax_row: int, ax_col: int, trace_index: int
98
+ ) -> List[str]:
99
+ """
100
+ Get CSV column names for a specific plotting method using trace index.
101
+
102
+ Parameters
103
+ ----------
104
+ id_val : str
105
+ The plot ID
106
+ method : str
107
+ The plotting method name
108
+ tracked_dict : dict
109
+ The tracked data dictionary
110
+ kwargs : dict
111
+ The keyword arguments passed to the plot
112
+ ax_row : int
113
+ Row index of axes in grid
114
+ ax_col : int
115
+ Column index of axes in grid
116
+ trace_index : int
117
+ Index of this trace
118
+
119
+ Returns
120
+ -------
121
+ list
122
+ List of column names that will be in the CSV
123
+ """
124
+ from .._csv_column_naming import get_csv_column_name
125
+
126
+ columns = []
127
+
128
+ method_columns = {
129
+ ("plot", "stx_line"): ["x", "y"],
130
+ ("scatter", "plot_scatter"): ["x", "y"],
131
+ ("bar", "barh"): ["x", "height"],
132
+ ("hist",): ["bins", "counts"],
133
+ ("boxplot", "stx_box"): ["data"],
134
+ ("violinplot", "stx_violin"): ["data"],
135
+ ("errorbar",): ["x", "y", "yerr"],
136
+ ("fill_between",): ["x", "y1", "y2"],
137
+ ("imshow", "stx_heatmap", "stx_image"): ["data"],
138
+ ("stx_kde", "stx_ecdf"): ["x", "y"],
139
+ ("stx_mean_std", "stx_mean_ci", "stx_median_iqr", "stx_shaded_line"): [
140
+ "x",
141
+ "y",
142
+ "lower",
143
+ "upper",
144
+ ],
145
+ }
146
+
147
+ for methods, vars in method_columns.items():
148
+ if method in methods:
149
+ columns = [
150
+ get_csv_column_name(v, ax_row, ax_col, trace_index=trace_index)
151
+ for v in vars
152
+ ]
153
+ return columns
154
+
155
+ # Handle seaborn methods
156
+ if method.startswith("sns_"):
157
+ sns_type = method.replace("sns_", "")
158
+ sns_columns = {
159
+ ("boxplot", "violinplot"): ["data"],
160
+ ("scatterplot", "lineplot"): ["x", "y"],
161
+ ("barplot",): ["x", "y"],
162
+ ("histplot",): ["bins", "counts"],
163
+ ("kdeplot",): ["x", "y"],
164
+ }
165
+ for types, vars in sns_columns.items():
166
+ if sns_type in types:
167
+ columns = [
168
+ get_csv_column_name(v, ax_row, ax_col, trace_index=trace_index)
169
+ for v in vars
170
+ ]
171
+ return columns
172
+
173
+ return columns
174
+
175
+
176
+ def _get_csv_columns_for_method(
177
+ id_val, method, tracked_dict, kwargs, ax_index: int
178
+ ) -> List[str]:
179
+ """
180
+ Get CSV column names for a specific plotting method.
181
+
182
+ Uses the same formatters that generate the CSV to ensure consistency.
183
+
184
+ Parameters
185
+ ----------
186
+ id_val : str
187
+ The plot ID
188
+ method : str
189
+ The plotting method name
190
+ tracked_dict : dict
191
+ The tracked data dictionary
192
+ kwargs : dict
193
+ The keyword arguments passed to the plot
194
+ ax_index : int
195
+ Flattened index of axes
196
+
197
+ Returns
198
+ -------
199
+ list
200
+ List of column names that will be in the CSV
201
+ """
202
+ try:
203
+ from scitex.plt._subplots._export_as_csv import format_record
204
+
205
+ record = (id_val, method, tracked_dict, kwargs)
206
+ df = format_record(record)
207
+
208
+ if df is not None and not df.empty:
209
+ prefix = f"ax_{ax_index:02d}_"
210
+ columns = []
211
+ for col in df.columns:
212
+ col_str = str(col)
213
+ if not col_str.startswith(prefix):
214
+ col_str = f"{prefix}{col_str}"
215
+ columns.append(col_str)
216
+ return columns
217
+
218
+ except Exception:
219
+ pass
220
+
221
+ # Fallback: Pattern-based column name generation
222
+ return _get_csv_columns_fallback(id_val, method, tracked_dict, kwargs, ax_index)
223
+
224
+
225
+ def _get_csv_columns_fallback(
226
+ id_val, method, tracked_dict, kwargs, ax_index: int
227
+ ) -> List[str]:
228
+ """Fallback pattern-based CSV column name generation."""
229
+ prefix = f"ax_{ax_index:02d}_"
230
+ columns = []
231
+ args = tracked_dict.get("args", []) if tracked_dict else []
232
+
233
+ if method in ("boxplot", "stx_box"):
234
+ columns = _get_boxplot_columns(args, kwargs, prefix, id_val)
235
+ elif method in ("plot", "stx_line"):
236
+ columns = [f"{prefix}{id_val}_plot_x", f"{prefix}{id_val}_plot_y"]
237
+ elif method in ("scatter", "plot_scatter"):
238
+ columns = [f"{prefix}{id_val}_scatter_x", f"{prefix}{id_val}_scatter_y"]
239
+ elif method in ("bar", "barh"):
240
+ columns = [f"{prefix}{id_val}_bar_x", f"{prefix}{id_val}_bar_height"]
241
+ elif method == "hist":
242
+ columns = [f"{prefix}{id_val}_hist_bins", f"{prefix}{id_val}_hist_counts"]
243
+ elif method in ("violinplot", "stx_violin"):
244
+ columns = _get_violin_columns(args, prefix, id_val)
245
+ elif method == "errorbar":
246
+ columns = [
247
+ f"{prefix}{id_val}_errorbar_x",
248
+ f"{prefix}{id_val}_errorbar_y",
249
+ f"{prefix}{id_val}_errorbar_yerr",
250
+ ]
251
+ elif method == "fill_between":
252
+ columns = [
253
+ f"{prefix}{id_val}_fill_x",
254
+ f"{prefix}{id_val}_fill_y1",
255
+ f"{prefix}{id_val}_fill_y2",
256
+ ]
257
+ elif method in ("imshow", "stx_heatmap", "stx_image"):
258
+ if args and hasattr(args[0], "shape") and len(args[0].shape) >= 2:
259
+ columns = [f"{prefix}{id_val}_image_data"]
260
+ elif method in ("stx_kde", "stx_ecdf"):
261
+ suffix = method.replace("stx_", "")
262
+ columns = [f"{prefix}{id_val}_{suffix}_x", f"{prefix}{id_val}_{suffix}_y"]
263
+ elif method in ("stx_mean_std", "stx_mean_ci", "stx_median_iqr", "stx_shaded_line"):
264
+ suffix = method.replace("stx_", "")
265
+ columns = [
266
+ f"{prefix}{id_val}_{suffix}_x",
267
+ f"{prefix}{id_val}_{suffix}_y",
268
+ f"{prefix}{id_val}_{suffix}_lower",
269
+ f"{prefix}{id_val}_{suffix}_upper",
270
+ ]
271
+ elif method.startswith("sns_"):
272
+ columns = _get_seaborn_columns(method, prefix, id_val)
273
+
274
+ return columns
275
+
276
+
277
+ def _get_boxplot_columns(args, kwargs, prefix, id_val) -> List[str]:
278
+ """Get columns for boxplot data."""
279
+ import numpy as np
280
+
281
+ columns = []
282
+ if len(args) >= 1:
283
+ data = args[0]
284
+ labels = kwargs.get("labels", None) if kwargs else None
285
+
286
+ from scitex.types import is_listed_X as scitex_types_is_listed_X
287
+
288
+ if isinstance(data, np.ndarray) or scitex_types_is_listed_X(data, [float, int]):
289
+ if labels and len(labels) == 1:
290
+ columns.append(f"{prefix}{id_val}_{labels[0]}")
291
+ else:
292
+ columns.append(f"{prefix}{id_val}_boxplot_0")
293
+ else:
294
+ try:
295
+ num_boxes = len(data)
296
+ if labels and len(labels) == num_boxes:
297
+ for label in labels:
298
+ columns.append(f"{prefix}{id_val}_{label}")
299
+ else:
300
+ for i in range(num_boxes):
301
+ columns.append(f"{prefix}{id_val}_boxplot_{i}")
302
+ except TypeError:
303
+ columns.append(f"{prefix}{id_val}_boxplot_0")
304
+
305
+ return columns
306
+
307
+
308
+ def _get_violin_columns(args, prefix, id_val) -> List[str]:
309
+ """Get columns for violin plot data."""
310
+ columns = []
311
+ if len(args) >= 1:
312
+ data = args[0]
313
+ try:
314
+ num_violins = len(data)
315
+ for i in range(num_violins):
316
+ columns.append(f"{prefix}{id_val}_violin_{i}")
317
+ except TypeError:
318
+ columns.append(f"{prefix}{id_val}_violin_0")
319
+ return columns
320
+
321
+
322
+ def _get_seaborn_columns(method, prefix, id_val) -> List[str]:
323
+ """Get columns for seaborn plots."""
324
+ sns_type = method.replace("sns_", "")
325
+ if sns_type in ("boxplot", "violinplot"):
326
+ return [f"{prefix}{id_val}_{sns_type}_data"]
327
+ elif sns_type in ("scatterplot", "lineplot"):
328
+ return [f"{prefix}{id_val}_{sns_type}_x", f"{prefix}{id_val}_{sns_type}_y"]
329
+ elif sns_type == "barplot":
330
+ return [f"{prefix}{id_val}_barplot_x", f"{prefix}{id_val}_barplot_y"]
331
+ elif sns_type == "histplot":
332
+ return [f"{prefix}{id_val}_histplot_bins", f"{prefix}{id_val}_histplot_counts"]
333
+ elif sns_type == "kdeplot":
334
+ return [f"{prefix}{id_val}_kdeplot_x", f"{prefix}{id_val}_kdeplot_y"]
335
+ return []
336
+
337
+
338
+ def _compute_csv_hash_from_df(df) -> Optional[str]:
339
+ """
340
+ Compute a hash of CSV data from a DataFrame.
341
+
342
+ Parameters
343
+ ----------
344
+ df : pandas.DataFrame
345
+ The DataFrame to compute hash from
346
+
347
+ Returns
348
+ -------
349
+ str or None
350
+ SHA256 hash (first 16 chars) or None if unable to compute
351
+ """
352
+ import hashlib
353
+
354
+ try:
355
+ if df is None or df.empty:
356
+ return None
357
+
358
+ csv_string = df.to_csv(index=False)
359
+ hash_obj = hashlib.sha256(csv_string.encode("utf-8"))
360
+ return hash_obj.hexdigest()[:16]
361
+
362
+ except Exception:
363
+ return None
364
+
365
+
366
+ def _compute_csv_hash(ax_or_df) -> Optional[str]:
367
+ """
368
+ Compute a hash of the CSV data for reproducibility verification.
369
+
370
+ Parameters
371
+ ----------
372
+ ax_or_df : AxisWrapper, matplotlib.axes.Axes, or pandas.DataFrame
373
+ The axes or DataFrame to compute hash from
374
+
375
+ Returns
376
+ -------
377
+ str or None
378
+ SHA256 hash (first 16 chars) or None if unable to compute
379
+ """
380
+ import hashlib
381
+
382
+ import pandas as pd
383
+
384
+ if isinstance(ax_or_df, pd.DataFrame):
385
+ return _compute_csv_hash_from_df(ax_or_df)
386
+
387
+ ax = ax_or_df
388
+
389
+ if not hasattr(ax, "export_as_csv"):
390
+ return None
391
+
392
+ try:
393
+ ax_index = 0
394
+ df = ax.export_as_csv()
395
+
396
+ if df is None or df.empty:
397
+ return None
398
+
399
+ prefix = f"ax_{ax_index:02d}_"
400
+ new_cols = []
401
+ for col in df.columns:
402
+ col_str = str(col)
403
+ if not col_str.startswith(prefix):
404
+ col_str = f"{prefix}{col_str}"
405
+ new_cols.append(col_str)
406
+ df.columns = new_cols
407
+
408
+ csv_string = df.to_csv(index=False)
409
+ hash_obj = hashlib.sha256(csv_string.encode("utf-8"))
410
+ return hash_obj.hexdigest()[:16]
411
+
412
+ except Exception:
413
+ return None
414
+
415
+
416
+ # EOF