scitex 2.14.0__py3-none-any.whl → 2.15.2__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 +244 -0
- scitex/_mcp_tools/template.py +24 -0
- scitex/_mcp_tools/writer.py +21 -204
- 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 +76 -27
- scitex/cli/capture.py +13 -20
- scitex/cli/introspect.py +481 -0
- scitex/cli/main.py +200 -109
- scitex/cli/mcp.py +60 -34
- scitex/cli/plt.py +357 -0
- scitex/cli/repro.py +15 -8
- scitex/cli/resource.py +15 -8
- scitex/cli/scholar/__init__.py +23 -8
- scitex/cli/scholar/_crossref_scitex.py +296 -0
- scitex/cli/scholar/_fetch.py +25 -3
- scitex/cli/social.py +314 -0
- scitex/cli/stats.py +15 -8
- scitex/cli/template.py +129 -12
- scitex/cli/tex.py +15 -8
- scitex/cli/writer.py +132 -8
- 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} +43 -54
- 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/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
- scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
- scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
- scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
- scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
- scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -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/_mcp/handlers.py +11 -744
- scitex/writer/_mcp/tool_schemas.py +5 -335
- scitex-2.15.2.dist-info/METADATA +648 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/RECORD +246 -150
- scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
- scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
- scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
- scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
- scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
- scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
- scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
- 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/data/.gitkeep +0 -0
- scitex/scholar/data/README.md +0 -44
- scitex/scholar/data/bib_files/bibliography.bib +0 -1952
- scitex/scholar/data/bib_files/neurovista.bib +0 -277
- scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
- scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
- scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
- scitex/scholar/data/bib_files/openaccess.bib +0 -89
- scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
- scitex/scholar/data/bib_files/pac.bib +0 -698
- scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
- scitex/scholar/data/bib_files/pac_processed.bib +0 -0
- scitex/scholar/data/bib_files/pac_titles.txt +0 -75
- scitex/scholar/data/bib_files/paywalled.bib +0 -98
- scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
- scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
- scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
- scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
- scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
- scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
- scitex/scholar/data/bib_files/test_seizure.bib +0 -46
- scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
- scitex/scholar/data/impact_factor.db +0 -0
- 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.2.dist-info}/WHEEL +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/entry_points.txt +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-24 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-python/src/scitex/plt/utils/_metadata/__init__.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Figure metadata extraction package.
|
|
7
|
+
|
|
8
|
+
This package provides modular utilities for extracting metadata from
|
|
9
|
+
matplotlib figures, split from the original _collect_figure_metadata.py.
|
|
10
|
+
|
|
11
|
+
Modules:
|
|
12
|
+
- _rounding: Precision-controlled rounding utilities
|
|
13
|
+
- _detect: Plot type detection
|
|
14
|
+
- _csv: CSV column naming and hash computation
|
|
15
|
+
- _verification: CSV/JSON consistency verification
|
|
16
|
+
- _legend: Legend extraction
|
|
17
|
+
- _artists: Artist extraction (lines, collections, patches, images, text)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Rounding utilities
|
|
21
|
+
# Artist extraction
|
|
22
|
+
from ._artists import _extract_artists, _extract_traces
|
|
23
|
+
|
|
24
|
+
# CSV utilities
|
|
25
|
+
from ._csv import (
|
|
26
|
+
_compute_csv_hash,
|
|
27
|
+
_compute_csv_hash_from_df,
|
|
28
|
+
_extract_csv_columns_from_history,
|
|
29
|
+
_get_csv_column_names,
|
|
30
|
+
_get_csv_columns_for_method,
|
|
31
|
+
_get_csv_columns_for_method_with_index,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Plot type detection
|
|
35
|
+
from ._detect import _detect_plot_type
|
|
36
|
+
|
|
37
|
+
# Legend extraction
|
|
38
|
+
from ._legend import _extract_legend_info
|
|
39
|
+
from ._rounding import (
|
|
40
|
+
PRECISION,
|
|
41
|
+
FixedFloat,
|
|
42
|
+
_round_dict,
|
|
43
|
+
_round_list,
|
|
44
|
+
_round_value,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Verification
|
|
48
|
+
from ._verification import (
|
|
49
|
+
assert_csv_json_consistency,
|
|
50
|
+
verify_csv_json_consistency,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
# Rounding
|
|
55
|
+
"PRECISION",
|
|
56
|
+
"FixedFloat",
|
|
57
|
+
"_round_value",
|
|
58
|
+
"_round_list",
|
|
59
|
+
"_round_dict",
|
|
60
|
+
# Detection
|
|
61
|
+
"_detect_plot_type",
|
|
62
|
+
# CSV
|
|
63
|
+
"_get_csv_column_names",
|
|
64
|
+
"_extract_csv_columns_from_history",
|
|
65
|
+
"_get_csv_columns_for_method_with_index",
|
|
66
|
+
"_get_csv_columns_for_method",
|
|
67
|
+
"_compute_csv_hash_from_df",
|
|
68
|
+
"_compute_csv_hash",
|
|
69
|
+
# Verification
|
|
70
|
+
"assert_csv_json_consistency",
|
|
71
|
+
"verify_csv_json_consistency",
|
|
72
|
+
# Legend
|
|
73
|
+
"_extract_legend_info",
|
|
74
|
+
# Artists
|
|
75
|
+
"_extract_artists",
|
|
76
|
+
"_extract_traces",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# EOF
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-24 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-python/src/scitex/plt/utils/_metadata/_artists/__init__.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Artist extraction for figure metadata.
|
|
7
|
+
|
|
8
|
+
This package splits the large _extract_artists function into logical modules:
|
|
9
|
+
- _base: Common utilities and context setup
|
|
10
|
+
- _lines: Line2D artist extraction (including boxplot/violin/stem semantics)
|
|
11
|
+
- _collections: Collection artist extraction (scatter, hexbin, violin bodies)
|
|
12
|
+
- _patches: Patch artist extraction (bar, pie, histogram)
|
|
13
|
+
- _images: Image artist extraction
|
|
14
|
+
- _text: Text artist extraction
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from ._extract import _extract_artists
|
|
18
|
+
|
|
19
|
+
# Backward compatibility alias
|
|
20
|
+
_extract_traces = _extract_artists
|
|
21
|
+
|
|
22
|
+
__all__ = ["_extract_artists", "_extract_traces"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# EOF
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-24 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-python/src/scitex/plt/utils/_metadata/_artists/_base.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Base utilities for artist extraction.
|
|
7
|
+
|
|
8
|
+
Provides common context and helper functions used across artist extractors.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
import matplotlib.colors as mcolors
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class ExtractionContext:
|
|
19
|
+
"""Context for artist extraction operations."""
|
|
20
|
+
|
|
21
|
+
ax: Any # matplotlib axes
|
|
22
|
+
mpl_ax: Any # raw matplotlib axes
|
|
23
|
+
ax_for_detection: Any # axes for plot type detection
|
|
24
|
+
ax_row: int = 0
|
|
25
|
+
ax_col: int = 0
|
|
26
|
+
plot_type: Optional[str] = None
|
|
27
|
+
method: Optional[str] = None
|
|
28
|
+
skip_unlabeled: bool = False
|
|
29
|
+
id_to_history: Dict[str, tuple] = field(default_factory=dict)
|
|
30
|
+
|
|
31
|
+
# Boxplot specific
|
|
32
|
+
is_boxplot: bool = False
|
|
33
|
+
num_boxes: int = 0
|
|
34
|
+
boxplot_data: Optional[List] = None
|
|
35
|
+
boxplot_stats: List[dict] = field(default_factory=list)
|
|
36
|
+
|
|
37
|
+
# Violin specific
|
|
38
|
+
is_violin: bool = False
|
|
39
|
+
|
|
40
|
+
# Stem specific
|
|
41
|
+
is_stem: bool = False
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def create_extraction_context(ax) -> ExtractionContext:
|
|
45
|
+
"""Create an extraction context from axes."""
|
|
46
|
+
from .._detect import _detect_plot_type
|
|
47
|
+
|
|
48
|
+
# Get axes position for CSV column naming
|
|
49
|
+
ax_row, ax_col = 0, 0
|
|
50
|
+
if hasattr(ax, "_scitex_metadata") and "position_in_grid" in ax._scitex_metadata:
|
|
51
|
+
pos = ax._scitex_metadata["position_in_grid"]
|
|
52
|
+
ax_row, ax_col = pos[0], pos[1]
|
|
53
|
+
|
|
54
|
+
# Get the raw matplotlib axes
|
|
55
|
+
mpl_ax = ax._axis_mpl if hasattr(ax, "_axis_mpl") else ax
|
|
56
|
+
|
|
57
|
+
# Try to find scitex wrapper for plot type detection
|
|
58
|
+
ax_for_detection = ax
|
|
59
|
+
if not hasattr(ax, "history") and hasattr(mpl_ax, "_scitex_wrapper"):
|
|
60
|
+
ax_for_detection = mpl_ax._scitex_wrapper
|
|
61
|
+
|
|
62
|
+
# Detect plot type
|
|
63
|
+
plot_type, method = _detect_plot_type(ax_for_detection)
|
|
64
|
+
|
|
65
|
+
# Plot types where internal artists should be hidden
|
|
66
|
+
internal_plot_types = {
|
|
67
|
+
"boxplot",
|
|
68
|
+
"violin",
|
|
69
|
+
"hist",
|
|
70
|
+
"bar",
|
|
71
|
+
"image",
|
|
72
|
+
"heatmap",
|
|
73
|
+
"kde",
|
|
74
|
+
"ecdf",
|
|
75
|
+
"errorbar",
|
|
76
|
+
"fill",
|
|
77
|
+
"stem",
|
|
78
|
+
"contour",
|
|
79
|
+
"pie",
|
|
80
|
+
"quiver",
|
|
81
|
+
"stream",
|
|
82
|
+
}
|
|
83
|
+
skip_unlabeled = plot_type in internal_plot_types
|
|
84
|
+
|
|
85
|
+
# Build history map
|
|
86
|
+
id_to_history = {}
|
|
87
|
+
if hasattr(ax_for_detection, "history"):
|
|
88
|
+
for record_id, record in ax_for_detection.history.items():
|
|
89
|
+
if isinstance(record, tuple) and len(record) >= 2:
|
|
90
|
+
tracking_id = record[0]
|
|
91
|
+
id_to_history[tracking_id] = record
|
|
92
|
+
|
|
93
|
+
ctx = ExtractionContext(
|
|
94
|
+
ax=ax,
|
|
95
|
+
mpl_ax=mpl_ax,
|
|
96
|
+
ax_for_detection=ax_for_detection,
|
|
97
|
+
ax_row=ax_row,
|
|
98
|
+
ax_col=ax_col,
|
|
99
|
+
plot_type=plot_type,
|
|
100
|
+
method=method,
|
|
101
|
+
skip_unlabeled=skip_unlabeled,
|
|
102
|
+
id_to_history=id_to_history,
|
|
103
|
+
is_boxplot=plot_type == "boxplot",
|
|
104
|
+
is_violin=plot_type == "violin",
|
|
105
|
+
is_stem=plot_type == "stem",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Extract boxplot info
|
|
109
|
+
if ctx.is_boxplot:
|
|
110
|
+
_extract_boxplot_info(ctx)
|
|
111
|
+
|
|
112
|
+
return ctx
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _extract_boxplot_info(ctx: ExtractionContext) -> None:
|
|
116
|
+
"""Extract boxplot specific information."""
|
|
117
|
+
import numpy as np
|
|
118
|
+
|
|
119
|
+
if not hasattr(ctx.ax_for_detection, "history"):
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
for record in ctx.ax_for_detection.history.values():
|
|
123
|
+
if isinstance(record, tuple) and len(record) >= 3:
|
|
124
|
+
method_name = record[1]
|
|
125
|
+
if method_name == "boxplot":
|
|
126
|
+
tracked_dict = record[2]
|
|
127
|
+
args = tracked_dict.get("args", [])
|
|
128
|
+
if args and len(args) > 0:
|
|
129
|
+
data = args[0]
|
|
130
|
+
if hasattr(data, "__len__") and not isinstance(data, str):
|
|
131
|
+
if hasattr(data[0], "__len__") and not isinstance(data[0], str):
|
|
132
|
+
ctx.num_boxes = len(data)
|
|
133
|
+
ctx.boxplot_data = data
|
|
134
|
+
else:
|
|
135
|
+
ctx.num_boxes = 1
|
|
136
|
+
ctx.boxplot_data = [data]
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
# Compute boxplot statistics
|
|
140
|
+
if ctx.boxplot_data is not None:
|
|
141
|
+
for box_idx, box_data in enumerate(ctx.boxplot_data):
|
|
142
|
+
try:
|
|
143
|
+
arr = np.asarray(box_data)
|
|
144
|
+
arr = arr[~np.isnan(arr)]
|
|
145
|
+
if len(arr) > 0:
|
|
146
|
+
q1 = float(np.percentile(arr, 25))
|
|
147
|
+
median = float(np.median(arr))
|
|
148
|
+
q3 = float(np.percentile(arr, 75))
|
|
149
|
+
iqr = q3 - q1
|
|
150
|
+
whisker_low = float(max(arr.min(), q1 - 1.5 * iqr))
|
|
151
|
+
whisker_high = float(min(arr.max(), q3 + 1.5 * iqr))
|
|
152
|
+
fliers = arr[(arr < whisker_low) | (arr > whisker_high)]
|
|
153
|
+
ctx.boxplot_stats.append(
|
|
154
|
+
{
|
|
155
|
+
"box_index": box_idx,
|
|
156
|
+
"median": median,
|
|
157
|
+
"q1": q1,
|
|
158
|
+
"q3": q3,
|
|
159
|
+
"whisker_low": whisker_low,
|
|
160
|
+
"whisker_high": whisker_high,
|
|
161
|
+
"n_fliers": int(len(fliers)),
|
|
162
|
+
"n_samples": int(len(arr)),
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
except (ValueError, TypeError):
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def color_to_hex(color) -> Optional[str]:
|
|
170
|
+
"""Convert color to hex string."""
|
|
171
|
+
try:
|
|
172
|
+
return mcolors.to_hex(color, keep_alpha=False)
|
|
173
|
+
except (ValueError, TypeError):
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def get_artist_id(
|
|
178
|
+
obj,
|
|
179
|
+
index: int,
|
|
180
|
+
prefix: str,
|
|
181
|
+
scitex_id: Optional[str] = None,
|
|
182
|
+
label: Optional[str] = None,
|
|
183
|
+
semantic_id: Optional[str] = None,
|
|
184
|
+
) -> str:
|
|
185
|
+
"""Get a unique ID for an artist."""
|
|
186
|
+
if scitex_id:
|
|
187
|
+
return scitex_id
|
|
188
|
+
if semantic_id:
|
|
189
|
+
return semantic_id
|
|
190
|
+
if label and not label.startswith("_"):
|
|
191
|
+
return label
|
|
192
|
+
return f"{prefix}_{index}"
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# EOF
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-24 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-python/src/scitex/plt/utils/_metadata/_artists/_collections.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Collection artist extraction.
|
|
7
|
+
|
|
8
|
+
Handles PathCollection (scatter), PolyCollection (hexbin, violin),
|
|
9
|
+
QuadMesh (hist2d), and LineCollection (errorbar) extraction.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import List
|
|
13
|
+
|
|
14
|
+
from ._base import ExtractionContext, color_to_hex
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def extract_collections(ctx: ExtractionContext) -> List[dict]:
|
|
18
|
+
"""Extract collection artists from axes."""
|
|
19
|
+
artists = []
|
|
20
|
+
|
|
21
|
+
for i, coll in enumerate(ctx.mpl_ax.collections):
|
|
22
|
+
coll_type = type(coll).__name__
|
|
23
|
+
|
|
24
|
+
if "PathCollection" in coll_type:
|
|
25
|
+
artist = _extract_scatter(ctx, i, coll)
|
|
26
|
+
if artist:
|
|
27
|
+
artists.append(artist)
|
|
28
|
+
elif "PolyCollection" in coll_type:
|
|
29
|
+
artist = _extract_poly_collection(ctx, i, coll)
|
|
30
|
+
if artist:
|
|
31
|
+
artists.append(artist)
|
|
32
|
+
elif "QuadMesh" in coll_type:
|
|
33
|
+
artist = _extract_quadmesh(ctx, i, coll)
|
|
34
|
+
if artist:
|
|
35
|
+
artists.append(artist)
|
|
36
|
+
elif coll_type == "LineCollection":
|
|
37
|
+
artist = _extract_line_collection(ctx, i, coll)
|
|
38
|
+
if artist:
|
|
39
|
+
artists.append(artist)
|
|
40
|
+
|
|
41
|
+
return artists
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _extract_scatter(ctx: ExtractionContext, index: int, coll) -> dict:
|
|
45
|
+
"""Extract PathCollection (scatter) artist."""
|
|
46
|
+
from .._csv import _get_csv_column_names
|
|
47
|
+
|
|
48
|
+
artist = {}
|
|
49
|
+
scitex_id = getattr(coll, "_scitex_id", None)
|
|
50
|
+
label = coll.get_label()
|
|
51
|
+
|
|
52
|
+
if scitex_id:
|
|
53
|
+
artist["id"] = scitex_id
|
|
54
|
+
elif label and not label.startswith("_"):
|
|
55
|
+
artist["id"] = label
|
|
56
|
+
else:
|
|
57
|
+
artist["id"] = f"scatter_{index}"
|
|
58
|
+
|
|
59
|
+
artist["mark"] = "scatter"
|
|
60
|
+
|
|
61
|
+
if label and not label.startswith("_"):
|
|
62
|
+
artist["label"] = label
|
|
63
|
+
artist["legend_included"] = True
|
|
64
|
+
else:
|
|
65
|
+
artist["legend_included"] = False
|
|
66
|
+
|
|
67
|
+
artist["zorder"] = coll.get_zorder()
|
|
68
|
+
|
|
69
|
+
# Backend layer
|
|
70
|
+
backend = {
|
|
71
|
+
"name": "matplotlib",
|
|
72
|
+
"artist_class": type(coll).__name__,
|
|
73
|
+
"props": {},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
facecolors = coll.get_facecolor()
|
|
78
|
+
if len(facecolors) > 0:
|
|
79
|
+
backend["props"]["facecolor"] = color_to_hex(facecolors[0])
|
|
80
|
+
except (ValueError, TypeError, IndexError):
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
edgecolors = coll.get_edgecolor()
|
|
85
|
+
if len(edgecolors) > 0:
|
|
86
|
+
backend["props"]["edgecolor"] = color_to_hex(edgecolors[0])
|
|
87
|
+
except (ValueError, TypeError, IndexError):
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
sizes = coll.get_sizes()
|
|
92
|
+
if len(sizes) > 0:
|
|
93
|
+
backend["props"]["size"] = float(sizes[0])
|
|
94
|
+
except (ValueError, TypeError, IndexError):
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
artist["backend"] = backend
|
|
98
|
+
|
|
99
|
+
# Data reference
|
|
100
|
+
artist_id = artist.get("id", str(index))
|
|
101
|
+
artist["data_ref"] = _get_csv_column_names(artist_id, ctx.ax_row, ctx.ax_col)
|
|
102
|
+
|
|
103
|
+
return artist
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _extract_poly_collection(ctx: ExtractionContext, index: int, coll) -> dict:
|
|
107
|
+
"""Extract PolyCollection (hexbin, violin body) artist."""
|
|
108
|
+
coll_type = type(coll).__name__
|
|
109
|
+
|
|
110
|
+
# Check if hexbin
|
|
111
|
+
if hasattr(coll, "get_array"):
|
|
112
|
+
arr = coll.get_array()
|
|
113
|
+
if arr is not None and len(arr) > 0 and ctx.plot_type != "violin":
|
|
114
|
+
return _extract_hexbin(ctx, index, coll, arr)
|
|
115
|
+
|
|
116
|
+
# Violin body
|
|
117
|
+
if ctx.plot_type == "violin":
|
|
118
|
+
return _extract_violin_body(ctx, index, coll)
|
|
119
|
+
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _extract_hexbin(ctx: ExtractionContext, index: int, coll, arr) -> dict:
|
|
124
|
+
"""Extract hexbin PolyCollection."""
|
|
125
|
+
artist = {}
|
|
126
|
+
scitex_id = getattr(coll, "_scitex_id", None)
|
|
127
|
+
label = coll.get_label() if hasattr(coll, "get_label") else ""
|
|
128
|
+
|
|
129
|
+
if scitex_id:
|
|
130
|
+
artist["id"] = scitex_id
|
|
131
|
+
elif label and not label.startswith("_"):
|
|
132
|
+
artist["id"] = label
|
|
133
|
+
else:
|
|
134
|
+
artist["id"] = f"hexbin_{index}"
|
|
135
|
+
|
|
136
|
+
artist["mark"] = "hexbin"
|
|
137
|
+
artist["role"] = "hexbin"
|
|
138
|
+
artist["legend_included"] = False
|
|
139
|
+
artist["zorder"] = coll.get_zorder()
|
|
140
|
+
|
|
141
|
+
# Backend layer
|
|
142
|
+
backend = {
|
|
143
|
+
"name": "matplotlib",
|
|
144
|
+
"artist_class": type(coll).__name__,
|
|
145
|
+
"props": {},
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
cmap = coll.get_cmap()
|
|
150
|
+
if cmap:
|
|
151
|
+
backend["props"]["cmap"] = cmap.name
|
|
152
|
+
except (ValueError, TypeError, AttributeError):
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
backend["props"]["vmin"] = float(coll.norm.vmin) if coll.norm else None
|
|
157
|
+
backend["props"]["vmax"] = float(coll.norm.vmax) if coll.norm else None
|
|
158
|
+
except (ValueError, TypeError, AttributeError):
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
artist["backend"] = backend
|
|
162
|
+
|
|
163
|
+
# Result info
|
|
164
|
+
try:
|
|
165
|
+
artist["result"] = {
|
|
166
|
+
"n_hexagons": int(len(arr)),
|
|
167
|
+
"count_range": [float(arr.min()), float(arr.max())]
|
|
168
|
+
if len(arr) > 0
|
|
169
|
+
else None,
|
|
170
|
+
"total_count": int(arr.sum()),
|
|
171
|
+
}
|
|
172
|
+
except (TypeError, AttributeError, ValueError):
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
return artist
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _extract_violin_body(ctx: ExtractionContext, index: int, coll) -> dict:
|
|
179
|
+
"""Extract violin body PolyCollection."""
|
|
180
|
+
artist = {}
|
|
181
|
+
scitex_id = getattr(coll, "_scitex_id", None)
|
|
182
|
+
|
|
183
|
+
if scitex_id:
|
|
184
|
+
artist["id"] = f"{scitex_id}_body_{index}"
|
|
185
|
+
artist["group_id"] = scitex_id
|
|
186
|
+
else:
|
|
187
|
+
artist["id"] = f"violin_body_{index}"
|
|
188
|
+
|
|
189
|
+
artist["mark"] = "polygon"
|
|
190
|
+
artist["role"] = "violin_body"
|
|
191
|
+
artist["legend_included"] = False
|
|
192
|
+
artist["zorder"] = coll.get_zorder()
|
|
193
|
+
|
|
194
|
+
# Backend layer
|
|
195
|
+
backend = {
|
|
196
|
+
"name": "matplotlib",
|
|
197
|
+
"artist_class": type(coll).__name__,
|
|
198
|
+
"props": {},
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
facecolors = coll.get_facecolor()
|
|
203
|
+
if len(facecolors) > 0:
|
|
204
|
+
backend["props"]["facecolor"] = color_to_hex(facecolors[0])
|
|
205
|
+
except (ValueError, TypeError, IndexError):
|
|
206
|
+
pass
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
edgecolors = coll.get_edgecolor()
|
|
210
|
+
if len(edgecolors) > 0:
|
|
211
|
+
backend["props"]["edgecolor"] = color_to_hex(edgecolors[0])
|
|
212
|
+
except (ValueError, TypeError, IndexError):
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
artist["backend"] = backend
|
|
216
|
+
|
|
217
|
+
return artist
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _extract_quadmesh(ctx: ExtractionContext, index: int, coll) -> dict:
|
|
221
|
+
"""Extract QuadMesh (hist2d) artist."""
|
|
222
|
+
artist = {}
|
|
223
|
+
scitex_id = getattr(coll, "_scitex_id", None)
|
|
224
|
+
label = coll.get_label() if hasattr(coll, "get_label") else ""
|
|
225
|
+
|
|
226
|
+
if scitex_id:
|
|
227
|
+
artist["id"] = scitex_id
|
|
228
|
+
elif label and not label.startswith("_"):
|
|
229
|
+
artist["id"] = label
|
|
230
|
+
else:
|
|
231
|
+
artist["id"] = f"hist2d_{index}"
|
|
232
|
+
|
|
233
|
+
artist["mark"] = "hist2d"
|
|
234
|
+
artist["role"] = "hist2d"
|
|
235
|
+
artist["legend_included"] = False
|
|
236
|
+
artist["zorder"] = coll.get_zorder()
|
|
237
|
+
|
|
238
|
+
# Backend layer
|
|
239
|
+
backend = {
|
|
240
|
+
"name": "matplotlib",
|
|
241
|
+
"artist_class": type(coll).__name__,
|
|
242
|
+
"props": {},
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
cmap = coll.get_cmap()
|
|
247
|
+
if cmap:
|
|
248
|
+
backend["props"]["cmap"] = cmap.name
|
|
249
|
+
except (ValueError, TypeError, AttributeError):
|
|
250
|
+
pass
|
|
251
|
+
|
|
252
|
+
artist["backend"] = backend
|
|
253
|
+
|
|
254
|
+
return artist
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _extract_line_collection(ctx: ExtractionContext, index: int, coll) -> dict:
|
|
258
|
+
"""Extract LineCollection (errorbar, stem) artist."""
|
|
259
|
+
artist = {}
|
|
260
|
+
scitex_id = getattr(coll, "_scitex_id", None)
|
|
261
|
+
label = coll.get_label() if hasattr(coll, "get_label") else ""
|
|
262
|
+
|
|
263
|
+
if scitex_id:
|
|
264
|
+
artist["id"] = scitex_id
|
|
265
|
+
elif label and not label.startswith("_"):
|
|
266
|
+
artist["id"] = label
|
|
267
|
+
else:
|
|
268
|
+
artist["id"] = f"linecollection_{index}"
|
|
269
|
+
|
|
270
|
+
artist["mark"] = "line"
|
|
271
|
+
|
|
272
|
+
# Determine role
|
|
273
|
+
if ctx.plot_type == "bar" or ctx.method == "barh":
|
|
274
|
+
artist["role"] = "errorbar"
|
|
275
|
+
elif ctx.plot_type == "stem":
|
|
276
|
+
artist["role"] = "stem_stem"
|
|
277
|
+
artist["id"] = "stem_lines"
|
|
278
|
+
else:
|
|
279
|
+
artist["role"] = "line_collection"
|
|
280
|
+
|
|
281
|
+
artist["legend_included"] = False
|
|
282
|
+
artist["zorder"] = coll.get_zorder()
|
|
283
|
+
|
|
284
|
+
# Backend layer
|
|
285
|
+
backend = {
|
|
286
|
+
"name": "matplotlib",
|
|
287
|
+
"artist_class": type(coll).__name__,
|
|
288
|
+
"props": {},
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
try:
|
|
292
|
+
colors = coll.get_colors()
|
|
293
|
+
if len(colors) > 0:
|
|
294
|
+
backend["props"]["color"] = color_to_hex(colors[0])
|
|
295
|
+
except (ValueError, TypeError, IndexError):
|
|
296
|
+
pass
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
linewidths = coll.get_linewidths()
|
|
300
|
+
if len(linewidths) > 0:
|
|
301
|
+
backend["props"]["linewidth_pt"] = float(linewidths[0])
|
|
302
|
+
except (ValueError, TypeError, IndexError):
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
artist["backend"] = backend
|
|
306
|
+
|
|
307
|
+
# Data reference for errorbar/stem
|
|
308
|
+
if artist["role"] == "errorbar":
|
|
309
|
+
_add_errorbar_data_ref(ctx, artist)
|
|
310
|
+
elif artist["role"] == "stem_stem":
|
|
311
|
+
_add_stem_data_ref(ctx, artist)
|
|
312
|
+
|
|
313
|
+
return artist
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _add_errorbar_data_ref(ctx: ExtractionContext, artist: dict) -> None:
|
|
317
|
+
"""Add data_ref for errorbar LineCollection."""
|
|
318
|
+
from .._csv import _get_csv_column_names
|
|
319
|
+
|
|
320
|
+
errorbar_trace_id = None
|
|
321
|
+
error_var = "yerr" if ctx.method == "bar" else "xerr"
|
|
322
|
+
|
|
323
|
+
if hasattr(ctx.ax_for_detection, "history"):
|
|
324
|
+
for record in ctx.ax_for_detection.history.values():
|
|
325
|
+
if isinstance(record, tuple) and len(record) >= 2:
|
|
326
|
+
method_name = record[1]
|
|
327
|
+
if method_name in ("bar", "barh"):
|
|
328
|
+
errorbar_trace_id = record[0]
|
|
329
|
+
break
|
|
330
|
+
|
|
331
|
+
if errorbar_trace_id:
|
|
332
|
+
base_ref = _get_csv_column_names(errorbar_trace_id, ctx.ax_row, ctx.ax_col)
|
|
333
|
+
artist["data_ref"] = {
|
|
334
|
+
"x": base_ref.get("x"),
|
|
335
|
+
"y": base_ref.get("y"),
|
|
336
|
+
error_var: f"ax-row-{ctx.ax_row}-col-{ctx.ax_col}_trace-id-{errorbar_trace_id}_variable-{error_var}",
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _add_stem_data_ref(ctx: ExtractionContext, artist: dict) -> None:
|
|
341
|
+
"""Add data_ref for stem LineCollection."""
|
|
342
|
+
from .._csv import _get_csv_column_names
|
|
343
|
+
|
|
344
|
+
if hasattr(ctx.ax_for_detection, "history"):
|
|
345
|
+
for record in ctx.ax_for_detection.history.values():
|
|
346
|
+
if isinstance(record, tuple) and len(record) >= 2:
|
|
347
|
+
method_name = record[1]
|
|
348
|
+
if method_name == "stem":
|
|
349
|
+
stem_trace_id = record[0]
|
|
350
|
+
artist["data_ref"] = _get_csv_column_names(
|
|
351
|
+
stem_trace_id, ctx.ax_row, ctx.ax_col
|
|
352
|
+
)
|
|
353
|
+
break
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
# EOF
|