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.
- scitex/__init__.py +71 -17
- scitex/_env_loader.py +156 -0
- scitex/_mcp_resources/__init__.py +37 -0
- scitex/_mcp_resources/_cheatsheet.py +135 -0
- scitex/_mcp_resources/_figrecipe.py +138 -0
- scitex/_mcp_resources/_formats.py +102 -0
- scitex/_mcp_resources/_modules.py +337 -0
- scitex/_mcp_resources/_session.py +149 -0
- scitex/_mcp_tools/__init__.py +4 -0
- scitex/_mcp_tools/audio.py +66 -0
- scitex/_mcp_tools/diagram.py +11 -95
- scitex/_mcp_tools/introspect.py +210 -0
- scitex/_mcp_tools/plt.py +260 -305
- scitex/_mcp_tools/scholar.py +74 -0
- scitex/_mcp_tools/social.py +27 -0
- scitex/_mcp_tools/template.py +24 -0
- scitex/_mcp_tools/writer.py +17 -210
- scitex/ai/_gen_ai/_PARAMS.py +10 -7
- scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
- scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
- scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
- scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
- scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
- scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
- scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
- scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
- scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +30 -1550
- scitex/ai/classification/timeseries/_sliding_window_core.py +467 -0
- scitex/ai/classification/timeseries/_sliding_window_plotting.py +369 -0
- scitex/audio/README.md +40 -36
- scitex/audio/__init__.py +129 -61
- scitex/audio/_branding.py +185 -0
- scitex/audio/_mcp/__init__.py +32 -0
- scitex/audio/_mcp/handlers.py +59 -6
- scitex/audio/_mcp/speak_handlers.py +238 -0
- scitex/audio/_relay.py +225 -0
- scitex/audio/_tts.py +18 -10
- scitex/audio/engines/base.py +17 -10
- scitex/audio/engines/elevenlabs_engine.py +7 -2
- scitex/audio/mcp_server.py +228 -75
- scitex/canvas/README.md +1 -1
- scitex/canvas/editor/_dearpygui/__init__.py +25 -0
- scitex/canvas/editor/_dearpygui/_editor.py +147 -0
- scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
- scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
- scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
- scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
- scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
- scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
- scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
- scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
- scitex/canvas/editor/_dearpygui/_selection.py +295 -0
- scitex/canvas/editor/_dearpygui/_state.py +93 -0
- scitex/canvas/editor/_dearpygui/_utils.py +61 -0
- scitex/canvas/editor/flask_editor/_core/__init__.py +27 -0
- scitex/canvas/editor/flask_editor/_core/_bbox_extraction.py +200 -0
- scitex/canvas/editor/flask_editor/_core/_editor.py +173 -0
- scitex/canvas/editor/flask_editor/_core/_export_helpers.py +353 -0
- scitex/canvas/editor/flask_editor/_core/_routes_basic.py +190 -0
- scitex/canvas/editor/flask_editor/_core/_routes_export.py +332 -0
- scitex/canvas/editor/flask_editor/_core/_routes_panels.py +252 -0
- scitex/canvas/editor/flask_editor/_core/_routes_save.py +218 -0
- scitex/canvas/editor/flask_editor/_core.py +25 -1684
- scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
- scitex/cli/__init__.py +38 -43
- scitex/cli/audio.py +160 -41
- scitex/cli/capture.py +133 -20
- scitex/cli/introspect.py +488 -0
- scitex/cli/main.py +200 -109
- scitex/cli/mcp.py +60 -34
- scitex/cli/plt.py +414 -0
- scitex/cli/repro.py +15 -8
- scitex/cli/resource.py +15 -8
- scitex/cli/scholar/__init__.py +154 -8
- scitex/cli/scholar/_crossref_scitex.py +296 -0
- scitex/cli/scholar/_fetch.py +25 -3
- scitex/cli/social.py +355 -0
- scitex/cli/stats.py +136 -11
- scitex/cli/template.py +129 -12
- scitex/cli/tex.py +15 -8
- scitex/cli/writer.py +49 -299
- scitex/cloud/__init__.py +41 -2
- scitex/config/README.md +1 -1
- scitex/config/__init__.py +16 -2
- scitex/config/_env_registry.py +256 -0
- scitex/context/__init__.py +22 -0
- scitex/dev/__init__.py +20 -1
- scitex/diagram/__init__.py +42 -19
- scitex/diagram/mcp_server.py +13 -125
- scitex/gen/__init__.py +50 -14
- scitex/gen/_list_packages.py +4 -4
- scitex/introspect/__init__.py +82 -0
- scitex/introspect/_call_graph.py +303 -0
- scitex/introspect/_class_hierarchy.py +163 -0
- scitex/introspect/_core.py +41 -0
- scitex/introspect/_docstring.py +131 -0
- scitex/introspect/_examples.py +113 -0
- scitex/introspect/_imports.py +271 -0
- scitex/{gen/_inspect_module.py → introspect/_list_api.py} +48 -56
- scitex/introspect/_mcp/__init__.py +41 -0
- scitex/introspect/_mcp/handlers.py +233 -0
- scitex/introspect/_members.py +155 -0
- scitex/introspect/_resolve.py +89 -0
- scitex/introspect/_signature.py +131 -0
- scitex/introspect/_source.py +80 -0
- scitex/introspect/_type_hints.py +172 -0
- scitex/io/_save.py +1 -2
- scitex/io/bundle/README.md +1 -1
- scitex/logging/_formatters.py +19 -9
- scitex/mcp_server.py +98 -5
- scitex/os/__init__.py +4 -0
- scitex/{gen → os}/_check_host.py +4 -5
- scitex/plt/__init__.py +245 -550
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
- scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/plt/gallery/README.md +1 -1
- scitex/plt/utils/_hitmap/__init__.py +82 -0
- scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
- scitex/plt/utils/_hitmap/_color_application.py +346 -0
- scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
- scitex/plt/utils/_hitmap/_constants.py +40 -0
- scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
- scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
- scitex/plt/utils/_hitmap/_query.py +113 -0
- scitex/plt/utils/_hitmap.py +46 -1616
- scitex/plt/utils/_metadata/__init__.py +80 -0
- scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
- scitex/plt/utils/_metadata/_artists/_base.py +195 -0
- scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
- scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
- scitex/plt/utils/_metadata/_artists/_images.py +80 -0
- scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
- scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
- scitex/plt/utils/_metadata/_artists/_text.py +106 -0
- scitex/plt/utils/_metadata/_csv.py +416 -0
- scitex/plt/utils/_metadata/_detect.py +225 -0
- scitex/plt/utils/_metadata/_legend.py +127 -0
- scitex/plt/utils/_metadata/_rounding.py +117 -0
- scitex/plt/utils/_metadata/_verification.py +202 -0
- scitex/schema/README.md +1 -1
- scitex/scholar/__init__.py +8 -0
- scitex/scholar/_mcp/crossref_handlers.py +265 -0
- scitex/scholar/core/Scholar.py +63 -1700
- scitex/scholar/core/_mixins/__init__.py +36 -0
- scitex/scholar/core/_mixins/_enrichers.py +270 -0
- scitex/scholar/core/_mixins/_library_handlers.py +100 -0
- scitex/scholar/core/_mixins/_loaders.py +103 -0
- scitex/scholar/core/_mixins/_pdf_download.py +375 -0
- scitex/scholar/core/_mixins/_pipeline.py +312 -0
- scitex/scholar/core/_mixins/_project_handlers.py +125 -0
- scitex/scholar/core/_mixins/_savers.py +69 -0
- scitex/scholar/core/_mixins/_search.py +103 -0
- scitex/scholar/core/_mixins/_services.py +88 -0
- scitex/scholar/core/_mixins/_url_finding.py +105 -0
- scitex/scholar/crossref_scitex.py +367 -0
- scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/scholar/examples/00_run_all.sh +120 -0
- scitex/scholar/jobs/_executors.py +27 -3
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
- scitex/scholar/pdf_download/_cli.py +154 -0
- scitex/scholar/pdf_download/strategies/__init__.py +11 -8
- scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
- scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
- scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
- scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
- scitex/scholar/pipelines/_single_steps.py +71 -36
- scitex/scholar/storage/_LibraryManager.py +97 -1695
- scitex/scholar/storage/_mixins/__init__.py +30 -0
- scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
- scitex/scholar/storage/_mixins/_library_operations.py +218 -0
- scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
- scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
- scitex/scholar/storage/_mixins/_resolution.py +376 -0
- scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
- scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
- scitex/security/README.md +3 -3
- scitex/session/README.md +1 -1
- scitex/session/__init__.py +26 -7
- scitex/session/_decorator.py +1 -1
- scitex/sh/README.md +1 -1
- scitex/sh/__init__.py +7 -4
- scitex/social/__init__.py +155 -0
- scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/stats/_mcp/_handlers/__init__.py +31 -0
- scitex/stats/_mcp/_handlers/_corrections.py +113 -0
- scitex/stats/_mcp/_handlers/_descriptive.py +78 -0
- scitex/stats/_mcp/_handlers/_effect_size.py +106 -0
- scitex/stats/_mcp/_handlers/_format.py +94 -0
- scitex/stats/_mcp/_handlers/_normality.py +110 -0
- scitex/stats/_mcp/_handlers/_posthoc.py +224 -0
- scitex/stats/_mcp/_handlers/_power.py +247 -0
- scitex/stats/_mcp/_handlers/_recommend.py +102 -0
- scitex/stats/_mcp/_handlers/_run_test.py +279 -0
- scitex/stats/_mcp/_handlers/_stars.py +48 -0
- scitex/stats/_mcp/handlers.py +19 -1171
- scitex/stats/auto/_stat_style.py +175 -0
- scitex/stats/auto/_style_definitions.py +411 -0
- scitex/stats/auto/_styles.py +22 -620
- scitex/stats/descriptive/__init__.py +11 -8
- scitex/stats/descriptive/_ci.py +39 -0
- scitex/stats/power/_power.py +15 -4
- scitex/str/__init__.py +2 -1
- scitex/str/_title_case.py +63 -0
- scitex/template/README.md +1 -1
- scitex/template/__init__.py +25 -10
- scitex/template/_code_templates.py +147 -0
- scitex/template/_mcp/handlers.py +81 -0
- scitex/template/_mcp/tool_schemas.py +55 -0
- scitex/template/_templates/__init__.py +51 -0
- scitex/template/_templates/audio.py +233 -0
- scitex/template/_templates/canvas.py +312 -0
- scitex/template/_templates/capture.py +268 -0
- scitex/template/_templates/config.py +43 -0
- scitex/template/_templates/diagram.py +294 -0
- scitex/template/_templates/io.py +107 -0
- scitex/template/_templates/module.py +53 -0
- scitex/template/_templates/plt.py +202 -0
- scitex/template/_templates/scholar.py +267 -0
- scitex/template/_templates/session.py +130 -0
- scitex/template/_templates/session_minimal.py +43 -0
- scitex/template/_templates/session_plot.py +67 -0
- scitex/template/_templates/session_stats.py +77 -0
- scitex/template/_templates/stats.py +323 -0
- scitex/template/_templates/writer.py +296 -0
- scitex/template/clone_writer_directory.py +5 -5
- scitex/ui/_backends/_email.py +10 -2
- scitex/ui/_backends/_webhook.py +5 -1
- scitex/web/_search_pubmed.py +10 -6
- scitex/writer/README.md +1 -1
- scitex/writer/__init__.py +43 -34
- scitex/writer/_mcp/handlers.py +11 -744
- scitex/writer/_mcp/tool_schemas.py +5 -335
- scitex-2.15.3.dist-info/METADATA +667 -0
- {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/RECORD +241 -120
- scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
- scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
- scitex/diagram/_compile.py +0 -312
- scitex/diagram/_diagram.py +0 -355
- scitex/diagram/_mcp/__init__.py +0 -4
- scitex/diagram/_mcp/handlers.py +0 -400
- scitex/diagram/_mcp/tool_schemas.py +0 -157
- scitex/diagram/_presets.py +0 -173
- scitex/diagram/_schema.py +0 -182
- scitex/diagram/_split.py +0 -278
- scitex/gen/_ci.py +0 -12
- scitex/gen/_title_case.py +0 -89
- scitex/plt/_mcp/__init__.py +0 -4
- scitex/plt/_mcp/_handlers_annotation.py +0 -102
- scitex/plt/_mcp/_handlers_figure.py +0 -195
- scitex/plt/_mcp/_handlers_plot.py +0 -252
- scitex/plt/_mcp/_handlers_style.py +0 -219
- scitex/plt/_mcp/handlers.py +0 -74
- scitex/plt/_mcp/tool_schemas.py +0 -497
- scitex/plt/mcp_server.py +0 -231
- scitex/scholar/examples/SUGGESTIONS.md +0 -865
- scitex/scholar/examples/dev.py +0 -38
- scitex-2.14.0.dist-info/METADATA +0 -1238
- /scitex/{gen → context}/_detect_environment.py +0 -0
- /scitex/{gen → context}/_get_notebook_path.py +0 -0
- /scitex/{gen/_shell.py → sh/_shell_legacy.py} +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/WHEEL +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/entry_points.txt +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.3.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
|