scitex 2.3.0__py3-none-any.whl → 2.4.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 (99) hide show
  1. scitex/ai/classification/reporters/reporter_utils/_Plotter.py +1 -1
  2. scitex/ai/plt/__init__.py +2 -2
  3. scitex/ai/plt/{_plot_conf_mat.py → _stx_conf_mat.py} +3 -3
  4. scitex/config/PriorityConfig.py +195 -0
  5. scitex/config/__init__.py +24 -0
  6. scitex/io/_save.py +125 -34
  7. scitex/io/_save_modules/_image.py +37 -20
  8. scitex/plt/__init__.py +470 -17
  9. scitex/plt/_subplots/_AxisWrapper.py +98 -50
  10. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +559 -124
  11. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +49 -8
  12. scitex/plt/_subplots/_SubplotsWrapper.py +76 -91
  13. scitex/plt/_subplots/_export_as_csv.py +127 -58
  14. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +25 -16
  15. scitex/plt/_subplots/_export_as_csv_formatters/_format_contourf.py +54 -0
  16. scitex/plt/_subplots/_export_as_csv_formatters/_format_hexbin.py +41 -0
  17. scitex/plt/_subplots/_export_as_csv_formatters/_format_hist2d.py +41 -0
  18. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +59 -47
  19. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +42 -0
  20. scitex/plt/_subplots/_export_as_csv_formatters/_format_pie.py +42 -0
  21. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +72 -35
  22. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +1 -1
  23. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +2 -2
  24. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +53 -0
  25. scitex/plt/_subplots/_export_as_csv_formatters/_format_stem.py +42 -0
  26. scitex/plt/_subplots/_export_as_csv_formatters/_format_step.py +42 -0
  27. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +48 -0
  28. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_conf_mat.py → _format_stx_conf_mat.py} +2 -2
  29. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_ecdf.py → _format_stx_ecdf.py} +2 -2
  30. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_fillv.py → _format_stx_fillv.py} +2 -2
  31. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_heatmap.py → _format_stx_heatmap.py} +2 -2
  32. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_image.py → _format_stx_image.py} +2 -2
  33. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_joyplot.py → _format_stx_joyplot.py} +2 -2
  34. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_line.py → _format_stx_line.py} +3 -3
  35. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_mean_ci.py → _format_stx_mean_ci.py} +2 -2
  36. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_mean_std.py → _format_stx_mean_std.py} +2 -2
  37. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_median_iqr.py → _format_stx_median_iqr.py} +2 -2
  38. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_raster.py → _format_stx_raster.py} +2 -2
  39. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_rectangle.py → _format_stx_rectangle.py} +1 -1
  40. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_scatter_hist.py → _format_stx_scatter_hist.py} +2 -2
  41. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_shaded_line.py → _format_stx_shaded_line.py} +2 -2
  42. scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_violin.py → _format_stx_violin.py} +2 -2
  43. scitex/plt/_subplots/_export_as_csv_formatters/verify_formatters.py +23 -23
  44. scitex/plt/ax/__init__.py +16 -15
  45. scitex/plt/ax/_plot/__init__.py +30 -30
  46. scitex/plt/ax/_plot/_add_fitted_line.py +65 -11
  47. scitex/plt/ax/_plot/_plot_statistical_shaded_line.py +104 -76
  48. scitex/plt/ax/_plot/{_plot_conf_mat.py → _stx_conf_mat.py} +10 -10
  49. scitex/plt/ax/_plot/_stx_ecdf.py +109 -0
  50. scitex/plt/ax/_plot/{_plot_fillv.py → _stx_fillv.py} +7 -7
  51. scitex/plt/ax/_plot/_stx_heatmap.py +366 -0
  52. scitex/plt/ax/_plot/{_plot_image.py → _stx_image.py} +1 -1
  53. scitex/plt/ax/_plot/_stx_joyplot.py +113 -0
  54. scitex/plt/ax/_plot/{_plot_raster.py → _stx_raster.py} +37 -25
  55. scitex/plt/ax/_plot/{_plot_rectangle.py → _stx_rectangle.py} +10 -9
  56. scitex/plt/ax/_plot/{_plot_scatter_hist.py → _stx_scatter_hist.py} +1 -1
  57. scitex/plt/ax/_plot/_stx_shaded_line.py +215 -0
  58. scitex/plt/ax/_plot/{_plot_violin.py → _stx_violin.py} +13 -6
  59. scitex/plt/ax/_style/__init__.py +3 -0
  60. scitex/plt/ax/_style/_style_barplot.py +13 -2
  61. scitex/plt/ax/_style/_style_boxplot.py +78 -32
  62. scitex/plt/ax/_style/_style_errorbar.py +17 -3
  63. scitex/plt/ax/_style/_style_scatter.py +17 -3
  64. scitex/plt/ax/_style/_style_violinplot.py +109 -0
  65. scitex/plt/color/_vizualize_colors.py +3 -3
  66. scitex/plt/styles/SCITEX_STYLE.yaml +104 -0
  67. scitex/plt/styles/__init__.py +57 -0
  68. scitex/plt/styles/_plot_defaults.py +209 -0
  69. scitex/plt/styles/_plot_postprocess.py +518 -0
  70. scitex/plt/styles/_style_loader.py +268 -0
  71. scitex/plt/styles/presets.py +208 -0
  72. scitex/plt/utils/_collect_figure_metadata.py +160 -18
  73. scitex/plt/utils/_colorbar.py +72 -10
  74. scitex/plt/utils/_configure_mpl.py +108 -52
  75. scitex/plt/utils/_crop.py +21 -7
  76. scitex/plt/utils/_figure_mm.py +21 -7
  77. scitex/stats/__init__.py +13 -1
  78. scitex/stats/_schema.py +578 -0
  79. scitex/stats/tests/__init__.py +13 -0
  80. scitex/stats/tests/correlation/__init__.py +13 -0
  81. scitex/stats/tests/correlation/_test_pearson.py +262 -0
  82. scitex/vis/__init__.py +6 -0
  83. scitex/vis/editor/__init__.py +23 -0
  84. scitex/vis/editor/_defaults.py +205 -0
  85. scitex/vis/editor/_edit.py +342 -0
  86. scitex/vis/editor/_mpl_editor.py +231 -0
  87. scitex/vis/editor/_tkinter_editor.py +466 -0
  88. scitex/vis/editor/_web_editor.py +1440 -0
  89. scitex/vis/model/plot_types.py +15 -15
  90. {scitex-2.3.0.dist-info → scitex-2.4.1.dist-info}/METADATA +2 -1
  91. {scitex-2.3.0.dist-info → scitex-2.4.1.dist-info}/RECORD +94 -67
  92. {scitex-2.3.0.dist-info → scitex-2.4.1.dist-info}/WHEEL +1 -1
  93. scitex/plt/ax/_plot/_plot_ecdf.py +0 -84
  94. scitex/plt/ax/_plot/_plot_heatmap.py +0 -277
  95. scitex/plt/ax/_plot/_plot_joyplot.py +0 -77
  96. scitex/plt/ax/_plot/_plot_shaded_line.py +0 -142
  97. scitex/plt/presets.py +0 -224
  98. {scitex-2.3.0.dist-info → scitex-2.4.1.dist-info}/entry_points.txt +0 -0
  99. {scitex-2.3.0.dist-info → scitex-2.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-11-29 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/stats/tests/correlation/_test_pearson.py
5
+
6
+ """
7
+ Pearson correlation test with publication-ready output.
8
+
9
+ Functionalities:
10
+ - Compute Pearson correlation coefficient and p-value
11
+ - Calculate confidence intervals using Fisher's z-transformation
12
+ - Optional scatter plot with regression line and statistics
13
+ - Return standardized result dictionary
14
+
15
+ Dependencies:
16
+ - packages: numpy, pandas, scipy
17
+
18
+ IO:
19
+ - input: Two continuous variables (arrays or Series)
20
+ - output: Correlation result dictionary with r, p-value, CI
21
+ """
22
+
23
+ import argparse
24
+ from typing import Optional, Union, Tuple
25
+
26
+ import numpy as np
27
+ import pandas as pd
28
+ from scipy import stats as scipy_stats
29
+
30
+ import scitex as stx
31
+ from scitex.logging import getLogger
32
+
33
+ logger = getLogger(__name__)
34
+
35
+
36
+ def test_pearson(
37
+ x: Union[np.ndarray, pd.Series],
38
+ y: Union[np.ndarray, pd.Series],
39
+ var_x: Optional[str] = None,
40
+ var_y: Optional[str] = None,
41
+ alpha: float = 0.05,
42
+ plot: bool = False,
43
+ **plot_kwargs
44
+ ) -> Union['StatResult', Tuple['StatResult', 'matplotlib.figure.Figure']]:
45
+ """
46
+ Pearson correlation test for linear relationship between two continuous variables.
47
+
48
+ Parameters
49
+ ----------
50
+ x : array or Series
51
+ First variable (continuous)
52
+ y : array or Series
53
+ Second variable (continuous)
54
+ var_x : str, optional
55
+ Name of first variable for display
56
+ var_y : str, optional
57
+ Name of second variable for display
58
+ alpha : float, default 0.05
59
+ Significance level for confidence intervals
60
+ plot : bool, default False
61
+ Whether to create scatter plot with regression line
62
+ **plot_kwargs
63
+ Additional arguments passed to plotting function
64
+
65
+ Returns
66
+ -------
67
+ result : StatResult
68
+ StatResult instance containing:
69
+ - statistic: Pearson's r coefficient
70
+ - p_value: Two-tailed p-value
71
+ - stars: Significance stars (*, **, ***, ns)
72
+ - ci_95: Confidence interval [lower, upper]
73
+ - effect_size: R² and interpretation
74
+ - samples: Sample information
75
+ Access as attributes or use .to_dict() for dictionary
76
+ fig : matplotlib.figure.Figure, optional
77
+ Figure object if plot=True
78
+
79
+ Notes
80
+ -----
81
+ Pearson correlation coefficient r is calculated as:
82
+
83
+ .. math::
84
+ r = \\frac{\\sum_{i=1}^{n}(x_i - \\bar{x})(y_i - \\bar{y})}{\\sqrt{\\sum_{i=1}^{n}(x_i - \\bar{x})^2 \\sum_{i=1}^{n}(y_i - \\bar{y})^2}}
85
+
86
+ Confidence intervals use Fisher's z-transformation:
87
+
88
+ .. math::
89
+ z = 0.5 \\ln\\left(\\frac{1+r}{1-r}\\right)
90
+
91
+ Interpretation guidelines (Cohen, 1988):
92
+ - |r| < 0.1: Negligible
93
+ - |r| < 0.3: Small
94
+ - |r| < 0.5: Medium
95
+ - |r| ≥ 0.5: Large
96
+
97
+ Examples
98
+ --------
99
+ >>> import numpy as np
100
+ >>> from scitex.stats.tests.correlation import test_pearson
101
+ >>>
102
+ >>> # Generate correlated data
103
+ >>> np.random.seed(42)
104
+ >>> x = np.random.randn(100)
105
+ >>> y = 2 * x + np.random.randn(100) * 0.5
106
+ >>>
107
+ >>> # Basic usage
108
+ >>> result = test_pearson(x, y)
109
+ >>> print(f"r = {result.statistic['value']:.3f}, p = {result.p_value:.3e}")
110
+ >>>
111
+ >>> # With variable names and plot
112
+ >>> result, fig = test_pearson(
113
+ ... x, y,
114
+ ... var_x='Height',
115
+ ... var_y='Weight',
116
+ ... plot=True
117
+ ... )
118
+ >>>
119
+ >>> # Check significance
120
+ >>> if result.p_value < 0.05:
121
+ ... print(f"Significant correlation: r = {result.statistic['value']:.3f}{result.stars}")
122
+ >>>
123
+ >>> # Use as dictionary (backward compatibility)
124
+ >>> result_dict = result.to_dict()
125
+ >>> print(result_dict['statistic']['value'])
126
+ """
127
+ from scitex.stats.utils._formatters import p2stars
128
+ from scitex.stats._schema import StatResult
129
+
130
+ # Convert to numpy arrays and remove NaN
131
+ x = np.asarray(x)
132
+ y = np.asarray(y)
133
+
134
+ # Check for matching lengths
135
+ if len(x) != len(y):
136
+ raise ValueError(f"x and y must have same length (got {len(x)} and {len(y)})")
137
+
138
+ # Remove NaN pairs
139
+ mask = ~(np.isnan(x) | np.isnan(y))
140
+ x_clean = x[mask]
141
+ y_clean = y[mask]
142
+ n = len(x_clean)
143
+
144
+ if n < 3:
145
+ raise ValueError(f"Need at least 3 valid pairs (got {n})")
146
+
147
+ # Compute Pearson correlation
148
+ r, p_value = scipy_stats.pearsonr(x_clean, y_clean)
149
+
150
+ # Calculate confidence interval using Fisher's z-transformation
151
+ z = np.arctanh(r) # Fisher's z = 0.5 * ln((1+r)/(1-r))
152
+ se = 1 / np.sqrt(n - 3) # Standard error of z
153
+ z_crit = scipy_stats.norm.ppf(1 - alpha/2) # Critical value for two-tailed test
154
+
155
+ ci_lower_z = z - z_crit * se
156
+ ci_upper_z = z + z_crit * se
157
+
158
+ # Transform back to r scale
159
+ ci_lower = np.tanh(ci_lower_z)
160
+ ci_upper = np.tanh(ci_upper_z)
161
+
162
+ # Convert p-value to stars
163
+ stars = p2stars(p_value, ns_symbol=False)
164
+ if not stars:
165
+ stars = 'ns'
166
+
167
+ # Calculate R²
168
+ r_squared = r ** 2
169
+
170
+ # Interpret effect size
171
+ r_abs = abs(r)
172
+ if r_abs < 0.1:
173
+ interpretation = "negligible"
174
+ elif r_abs < 0.3:
175
+ interpretation = "small"
176
+ elif r_abs < 0.5:
177
+ interpretation = "medium"
178
+ else:
179
+ interpretation = "large"
180
+
181
+ # Set variable names
182
+ if var_x is None:
183
+ var_x = 'X'
184
+ if var_y is None:
185
+ var_y = 'Y'
186
+
187
+ # Build StatResult instance
188
+ result = StatResult(
189
+ test_type='pearson',
190
+ test_category='correlation',
191
+ statistic={'name': 'r', 'value': r},
192
+ p_value=p_value,
193
+ stars=stars,
194
+ effect_size={
195
+ 'name': 'r_squared',
196
+ 'value': r_squared,
197
+ 'interpretation': interpretation,
198
+ 'ci_95': [ci_lower, ci_upper]
199
+ },
200
+ samples={
201
+ 'n_total': n,
202
+ 'n_valid': n,
203
+ 'var_x': var_x,
204
+ 'var_y': var_y
205
+ },
206
+ ci_95=[ci_lower, ci_upper],
207
+ extra={
208
+ 'test': 'Pearson correlation',
209
+ 'ci_level': 1 - alpha,
210
+ 'method': 'pearson',
211
+ 'alpha': alpha
212
+ },
213
+ software_version=stx.__version__
214
+ )
215
+
216
+ # Create plot if requested
217
+ if plot:
218
+ fig = _plot_pearson(x_clean, y_clean, result, **plot_kwargs)
219
+ return result, fig
220
+
221
+ return result
222
+
223
+
224
+ def _plot_pearson(
225
+ x: np.ndarray,
226
+ y: np.ndarray,
227
+ result: 'StatResult',
228
+ **kwargs
229
+ ) -> 'matplotlib.figure.Figure':
230
+ """
231
+ Create scatter plot with regression line for Pearson correlation.
232
+
233
+ Internal function called by test_pearson when plot=True.
234
+ """
235
+ # Create figure
236
+ fig, ax = stx.plt.subplots(**stx.plt.presets.SCITEX_STYLE)
237
+
238
+ # Scatter plot
239
+ scatter = ax.scatter(x, y, alpha=0.6, c="steelblue", label="Data")
240
+ stx.plt.ax.style_scatter(scatter, size_mm=0.8)
241
+
242
+ # Add fitted line with statistics
243
+ stx.plt.ax.add_fitted_line(
244
+ ax, x, y,
245
+ color='black',
246
+ linestyle='--',
247
+ label='Fit',
248
+ show_stats=True
249
+ )
250
+
251
+ # Labels
252
+ var_x = result.samples.get('var_x', 'X')
253
+ var_y = result.samples.get('var_y', 'Y')
254
+ ax.set_xlabel(stx.plt.ax.format_label(var_x, ""))
255
+ ax.set_ylabel(stx.plt.ax.format_label(var_y, ""))
256
+ ax.set_title(f"Pearson Correlation (r = {result.statistic['value']:.3f}{result.stars})")
257
+ ax.legend(frameon=False, fontsize=6)
258
+
259
+ return fig
260
+
261
+
262
+ # EOF
scitex/vis/__init__.py CHANGED
@@ -54,6 +54,7 @@ from . import model
54
54
  from . import backend
55
55
  from . import io
56
56
  from . import utils
57
+ from . import editor
57
58
 
58
59
  # Convenient top-level imports for common use cases
59
60
  from .model import FigureModel, AxesModel, PlotModel, GuideModel, AnnotationModel
@@ -79,12 +80,17 @@ from .utils import (
79
80
  NATURE_DOUBLE_COLUMN_MM,
80
81
  )
81
82
 
83
+ from .editor import edit
84
+
82
85
  __all__ = [
83
86
  # Submodules
84
87
  "model",
85
88
  "backend",
86
89
  "io",
87
90
  "utils",
91
+ "editor",
92
+ # Editor
93
+ "edit",
88
94
  # Models
89
95
  "FigureModel",
90
96
  "AxesModel",
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # File: ./src/scitex/vis/editor/__init__.py
4
+ """
5
+ SciTeX Visual Editor Module
6
+
7
+ Provides interactive GUI for editing figure styles and annotations.
8
+ Supports multiple backends with graceful degradation:
9
+ - web: Browser-based (Flask/FastAPI) - modern UI
10
+ - dearpygui: GPU-accelerated desktop (fast)
11
+ - qt: Rich desktop (PyQt/PySide)
12
+ - tkinter: Built-in Python (works everywhere)
13
+ - mpl: Minimal matplotlib (always works)
14
+ """
15
+
16
+ from ._edit import edit, save_manual_overrides
17
+
18
+ __all__ = [
19
+ "edit",
20
+ "save_manual_overrides",
21
+ ]
22
+
23
+ # EOF
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # File: ./src/scitex/vis/editor/_defaults.py
4
+ """Default style settings for SciTeX visual editor."""
5
+
6
+
7
+ def get_scitex_defaults():
8
+ """
9
+ Get default style values based on SciTeX publication standards.
10
+
11
+ Returns
12
+ -------
13
+ dict
14
+ Dictionary of default style settings
15
+ """
16
+ return {
17
+ # Font sizes (from _configure_mpl.py)
18
+ 'fontsize': 7, # Base font size for publication
19
+ 'title_fontsize': 8, # Title size
20
+ 'axis_fontsize': 7, # Axis label size
21
+ 'tick_fontsize': 7, # Tick label size
22
+ 'legend_fontsize': 6, # Legend font size
23
+
24
+ # Line settings (in points, 1mm = 2.83465pt)
25
+ 'linewidth': 0.57, # Default line width (0.2mm)
26
+
27
+ # Tick settings
28
+ 'n_ticks': 4, # Number of ticks
29
+ 'tick_length': 0.8, # Tick length in mm
30
+ 'tick_width': 0.2, # Tick width in mm
31
+ 'tick_direction': 'out', # Tick direction
32
+
33
+ # Axes settings
34
+ 'axis_width': 0.2, # Spine/axes line width in mm
35
+ 'hide_top_spine': True, # Hide top spine
36
+ 'hide_right_spine': True, # Hide right spine
37
+
38
+ # Grid settings
39
+ 'grid': False, # Grid off by default
40
+ 'grid_linewidth': 0.6,
41
+ 'grid_alpha': 0.3,
42
+
43
+ # Figure settings
44
+ 'dpi': 300, # Default DPI
45
+ 'fig_size': [3.15, 2.68], # Default figure size in inches
46
+
47
+ # Colors (SciTeX defaults)
48
+ 'facecolor': '#ffffff', # White background
49
+ 'transparent': True, # Transparent background
50
+
51
+ # Legend
52
+ 'legend_visible': True,
53
+ 'legend_frameon': False,
54
+ 'legend_loc': 'best',
55
+
56
+ # Font family
57
+ 'font_family': 'Arial', # Sans-serif for publication
58
+
59
+ # Annotations
60
+ 'annotations': [],
61
+ }
62
+
63
+
64
+ def get_scitex_colors():
65
+ """
66
+ Get SciTeX color palette.
67
+
68
+ Returns
69
+ -------
70
+ dict
71
+ Dictionary of named colors with hex values
72
+ """
73
+ # Based on scitex.plt.color.PARAMS
74
+ return {
75
+ 'blue': '#1f77b4',
76
+ 'orange': '#ff7f0e',
77
+ 'green': '#2ca02c',
78
+ 'red': '#d62728',
79
+ 'purple': '#9467bd',
80
+ 'brown': '#8c564b',
81
+ 'pink': '#e377c2',
82
+ 'gray': '#7f7f7f',
83
+ 'olive': '#bcbd22',
84
+ 'cyan': '#17becf',
85
+ # Additional publication colors
86
+ 'black': '#000000',
87
+ 'white': '#ffffff',
88
+ 'dark_gray': '#404040',
89
+ 'light_gray': '#d0d0d0',
90
+ }
91
+
92
+
93
+ def extract_defaults_from_metadata(metadata):
94
+ """
95
+ Extract style defaults from loaded figure metadata.
96
+
97
+ Parameters
98
+ ----------
99
+ metadata : dict
100
+ Figure metadata loaded from JSON
101
+
102
+ Returns
103
+ -------
104
+ dict
105
+ Dictionary of style values extracted from metadata
106
+ """
107
+ defaults = get_scitex_defaults()
108
+
109
+ if not metadata:
110
+ return defaults
111
+
112
+ # Extract from scitex section (new format)
113
+ scitex_meta = metadata.get('scitex', {})
114
+ style_mm = scitex_meta.get('style_mm', {})
115
+
116
+ # Font sizes from style_mm
117
+ if 'axis_font_size_pt' in style_mm:
118
+ defaults['axis_fontsize'] = style_mm['axis_font_size_pt']
119
+ if 'tick_font_size_pt' in style_mm:
120
+ defaults['tick_fontsize'] = style_mm['tick_font_size_pt']
121
+ if 'title_font_size_pt' in style_mm:
122
+ defaults['title_fontsize'] = style_mm['title_font_size_pt']
123
+ if 'legend_font_size_pt' in style_mm:
124
+ defaults['legend_fontsize'] = style_mm['legend_font_size_pt']
125
+
126
+ # Line/axis thickness from metadata (in mm)
127
+ if 'trace_thickness_mm' in style_mm:
128
+ defaults['linewidth'] = style_mm['trace_thickness_mm'] * 2.83465 # mm to pt
129
+ if 'axis_thickness_mm' in style_mm:
130
+ defaults['axis_width'] = style_mm['axis_thickness_mm']
131
+ if 'tick_length_mm' in style_mm:
132
+ defaults['tick_length'] = style_mm['tick_length_mm']
133
+ if 'tick_thickness_mm' in style_mm:
134
+ defaults['tick_width'] = style_mm['tick_thickness_mm']
135
+ if 'n_ticks' in style_mm:
136
+ defaults['n_ticks'] = style_mm['n_ticks']
137
+
138
+ # Dimensions from metadata
139
+ dimensions = metadata.get('dimensions', {})
140
+ if 'dpi' in dimensions:
141
+ defaults['dpi'] = dimensions['dpi']
142
+ if 'figure_size_inch' in dimensions:
143
+ defaults['fig_size'] = dimensions['figure_size_inch']
144
+
145
+ # Axis labels from metadata
146
+ if 'xlabel' in metadata:
147
+ defaults['xlabel'] = metadata['xlabel']
148
+ if 'ylabel' in metadata:
149
+ defaults['ylabel'] = metadata['ylabel']
150
+ if 'title' in metadata:
151
+ defaults['title'] = metadata['title']
152
+
153
+ # Try to extract from axes if present
154
+ axes = metadata.get('axes', {})
155
+ if isinstance(axes, dict):
156
+ # New format: {'x': {'label': ...}, 'y': {'label': ...}}
157
+ x_axis = axes.get('x', {})
158
+ y_axis = axes.get('y', {})
159
+ if 'label' in x_axis:
160
+ unit = x_axis.get('unit', '')
161
+ label = x_axis['label']
162
+ if unit:
163
+ defaults['xlabel'] = f"{label} [{unit}]"
164
+ else:
165
+ defaults['xlabel'] = label
166
+ if 'label' in y_axis:
167
+ unit = y_axis.get('unit', '')
168
+ label = y_axis['label']
169
+ if unit:
170
+ defaults['ylabel'] = f"{label} [{unit}]"
171
+ else:
172
+ defaults['ylabel'] = label
173
+ # Extract axis limits
174
+ if 'lim' in x_axis:
175
+ defaults['xlim'] = x_axis['lim']
176
+ if 'lim' in y_axis:
177
+ defaults['ylim'] = y_axis['lim']
178
+ elif isinstance(axes, list) and len(axes) > 0:
179
+ # Legacy format: list of axes
180
+ ax = axes[0]
181
+ if 'xlabel' in ax:
182
+ defaults['xlabel'] = ax['xlabel']
183
+ if 'ylabel' in ax:
184
+ defaults['ylabel'] = ax['ylabel']
185
+ if 'title' in ax:
186
+ defaults['title'] = ax['title']
187
+ if 'grid' in ax:
188
+ defaults['grid'] = ax['grid']
189
+
190
+ # Extract traces information
191
+ traces = metadata.get('traces', [])
192
+ if traces:
193
+ defaults['traces'] = traces
194
+
195
+ # Extract legend information
196
+ legend = metadata.get('legend', {})
197
+ if legend:
198
+ defaults['legend_visible'] = legend.get('visible', True)
199
+ defaults['legend_frameon'] = legend.get('frameon', False)
200
+ defaults['legend_loc'] = legend.get('loc', 'best')
201
+
202
+ return defaults
203
+
204
+
205
+ # EOF