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,215 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-01 13:15:00 (ywatanabe)"
4
+ # File: ./src/scitex/plt/ax/_plot/_plot_shaded_line.py
5
+
6
+ """Line plots with shaded uncertainty regions (e.g., confidence intervals)."""
7
+
8
+ from typing import Any, List, Optional, Tuple, Union
9
+
10
+ import numpy as np
11
+ import pandas as pd
12
+ from matplotlib.axes import Axes
13
+
14
+ from scitex.types import ColorLike
15
+ from ....plt.utils import assert_valid_axis
16
+
17
+
18
+ def _plot_single_shaded_line(
19
+ axis: Union[Axes, "AxisWrapper"],
20
+ xx: np.ndarray,
21
+ y_lower: np.ndarray,
22
+ y_middle: np.ndarray,
23
+ y_upper: np.ndarray,
24
+ color: Optional[ColorLike] = None,
25
+ alpha: float = 0.3,
26
+ **kwargs: Any,
27
+ ) -> Tuple[Union[Axes, "AxisWrapper"], pd.DataFrame]:
28
+ """Plot a single line with shaded area between y_lower and y_upper bounds.
29
+
30
+ Parameters
31
+ ----------
32
+ axis : matplotlib.axes.Axes or AxisWrapper
33
+ Axes to plot on.
34
+ xx : np.ndarray
35
+ X values.
36
+ y_lower : np.ndarray
37
+ Lower bound y values.
38
+ y_middle : np.ndarray
39
+ Middle (mean/median) y values.
40
+ y_upper : np.ndarray
41
+ Upper bound y values.
42
+ color : ColorLike, optional
43
+ Color for line and fill.
44
+ alpha : float, default 0.3
45
+ Transparency for shaded region.
46
+ **kwargs : dict
47
+ Additional keyword arguments passed to plot().
48
+
49
+ Returns
50
+ -------
51
+ axis : matplotlib.axes.Axes or AxisWrapper
52
+ The axes with the plot.
53
+ df : pd.DataFrame
54
+ DataFrame with x, y_lower, y_middle, y_upper columns.
55
+ """
56
+ assert_valid_axis(axis, "First argument must be a matplotlib axis or scitex axis wrapper")
57
+ assert (
58
+ len(xx) == len(y_middle) == len(y_lower) == len(y_upper)
59
+ ), "All arrays must have the same length"
60
+
61
+ label = kwargs.pop("label", None)
62
+ axis.plot(xx, y_middle, color=color, alpha=alpha, label=label, **kwargs)
63
+ kwargs["linewidth"] = 0
64
+ kwargs["edgecolor"] = "none" # Remove edge line
65
+ axis.fill_between(xx, y_lower, y_upper, alpha=alpha, color=color, **kwargs)
66
+
67
+ return axis, pd.DataFrame(
68
+ {"x": xx, "y_lower": y_lower, "y_middle": y_middle, "y_upper": y_upper}
69
+ )
70
+
71
+
72
+ def _plot_shaded_line(
73
+ axis: Union[Axes, "AxisWrapper"],
74
+ xs: List[np.ndarray],
75
+ ys_lower: List[np.ndarray],
76
+ ys_middle: List[np.ndarray],
77
+ ys_upper: List[np.ndarray],
78
+ color: Optional[Union[List[ColorLike], ColorLike]] = None,
79
+ **kwargs: Any,
80
+ ) -> Tuple[Union[Axes, "AxisWrapper"], List[pd.DataFrame]]:
81
+ """Plot multiple lines with shaded areas between ys_lower and ys_upper bounds.
82
+
83
+ Parameters
84
+ ----------
85
+ axis : matplotlib.axes.Axes or AxisWrapper
86
+ Axes to plot on.
87
+ xs : list of np.ndarray
88
+ List of x value arrays.
89
+ ys_lower : list of np.ndarray
90
+ List of lower bound y value arrays.
91
+ ys_middle : list of np.ndarray
92
+ List of middle y value arrays.
93
+ ys_upper : list of np.ndarray
94
+ List of upper bound y value arrays.
95
+ color : ColorLike or list of ColorLike, optional
96
+ Color(s) for lines and fills.
97
+ **kwargs : dict
98
+ Additional keyword arguments passed to plot().
99
+
100
+ Returns
101
+ -------
102
+ axis : matplotlib.axes.Axes or AxisWrapper
103
+ The axes with the plots.
104
+ results : list of pd.DataFrame
105
+ List of DataFrames with plot data.
106
+ """
107
+ assert_valid_axis(axis, "First argument must be a matplotlib axis or scitex axis wrapper")
108
+ assert (
109
+ len(xs) == len(ys_lower) == len(ys_middle) == len(ys_upper)
110
+ ), "All input lists must have the same length"
111
+
112
+ results = []
113
+ colors = color
114
+ color_list = colors
115
+
116
+ if colors is not None:
117
+ if not isinstance(colors, list):
118
+ color_list = [colors] * len(xs)
119
+ else:
120
+ assert len(colors) == len(xs), "Number of colors must match number of lines"
121
+ color_list = colors
122
+
123
+ for idx, (xx, y_lower, y_middle, y_upper) in enumerate(
124
+ zip(xs, ys_lower, ys_middle, ys_upper)
125
+ ):
126
+ this_kwargs = kwargs.copy()
127
+ this_kwargs["color"] = color_list[idx]
128
+ _, result_df = _plot_single_shaded_line(
129
+ axis, xx, y_lower, y_middle, y_upper, **this_kwargs
130
+ )
131
+ results.append(result_df)
132
+ else:
133
+ for xx, y_lower, y_middle, y_upper in zip(xs, ys_lower, ys_middle, ys_upper):
134
+ _, result_df = _plot_single_shaded_line(
135
+ axis, xx, y_lower, y_middle, y_upper, **kwargs
136
+ )
137
+ results.append(result_df)
138
+
139
+ return axis, results
140
+
141
+
142
+ def stx_shaded_line(
143
+ axis: Union[Axes, "AxisWrapper"],
144
+ xs: Union[np.ndarray, List[np.ndarray]],
145
+ ys_lower: Union[np.ndarray, List[np.ndarray]],
146
+ ys_middle: Union[np.ndarray, List[np.ndarray]],
147
+ ys_upper: Union[np.ndarray, List[np.ndarray]],
148
+ color: Optional[Union[ColorLike, List[ColorLike]]] = None,
149
+ **kwargs: Any,
150
+ ) -> Tuple[Union[Axes, "AxisWrapper"], Union[pd.DataFrame, List[pd.DataFrame]]]:
151
+ """Plot line(s) with shaded uncertainty regions.
152
+
153
+ Automatically handles both single and multiple line cases. Useful for
154
+ plotting mean/median with confidence intervals or standard deviation bands.
155
+
156
+ Parameters
157
+ ----------
158
+ axis : matplotlib.axes.Axes or AxisWrapper
159
+ Axes to plot on.
160
+ xs : np.ndarray or list of np.ndarray
161
+ X values (single array or list of arrays for multiple lines).
162
+ ys_lower : np.ndarray or list of np.ndarray
163
+ Lower bound y values.
164
+ ys_middle : np.ndarray or list of np.ndarray
165
+ Middle (mean/median) y values.
166
+ ys_upper : np.ndarray or list of np.ndarray
167
+ Upper bound y values.
168
+ color : ColorLike or list of ColorLike, optional
169
+ Color(s) for lines and shaded regions.
170
+ **kwargs : dict
171
+ Additional keyword arguments passed to plot().
172
+
173
+ Returns
174
+ -------
175
+ axis : matplotlib.axes.Axes or AxisWrapper
176
+ The axes with the plot(s).
177
+ data : pd.DataFrame or list of pd.DataFrame
178
+ DataFrame(s) containing plot data with columns:
179
+ x, y_lower, y_middle, y_upper.
180
+
181
+ Examples
182
+ --------
183
+ >>> import numpy as np
184
+ >>> import scitex as stx
185
+ >>> x = np.linspace(0, 10, 100)
186
+ >>> y_mean = np.sin(x)
187
+ >>> y_std = 0.2
188
+ >>> fig, ax = stx.plt.subplots()
189
+ >>> ax, df = stx.plt.ax.stx_shaded_line(
190
+ ... ax, x, y_mean - y_std, y_mean, y_mean + y_std,
191
+ ... color='blue', alpha=0.3
192
+ ... )
193
+ """
194
+ is_single = not (
195
+ isinstance(xs, list)
196
+ and isinstance(ys_lower, list)
197
+ and isinstance(ys_middle, list)
198
+ and isinstance(ys_upper, list)
199
+ )
200
+
201
+ if is_single:
202
+ assert (
203
+ len(xs) == len(ys_lower) == len(ys_middle) == len(ys_upper)
204
+ ), "All arrays must have the same length for single line plot"
205
+
206
+ return _plot_single_shaded_line(
207
+ axis, xs, ys_lower, ys_middle, ys_upper, color=color, **kwargs
208
+ )
209
+ else:
210
+ return _plot_shaded_line(
211
+ axis, xs, ys_lower, ys_middle, ys_upper, color=color, **kwargs
212
+ )
213
+
214
+
215
+ # EOF
@@ -17,9 +17,9 @@ import seaborn as sns
17
17
  from ....plt.utils import assert_valid_axis
18
18
 
19
19
 
20
- def plot_violin(
20
+ def stx_violin(
21
21
  ax,
22
- data_list,
22
+ values_list,
23
23
  labels=None,
24
24
  colors=None,
25
25
  half=False,
@@ -32,10 +32,10 @@ def plot_violin(
32
32
  ----------
33
33
  ax : matplotlib.axes.Axes or scitex.plt._subplots.AxisWrapper
34
34
  The axes to plot on
35
- data_list : list
36
- List of arrays to plot as violins
35
+ values_list : list of array-like, shape (n_groups,) where each element is (n_samples,)
36
+ List of 1D arrays to plot as violins, one per group
37
37
  labels : list, optional
38
- Labels for each array in data_list
38
+ Labels for each array in values_list
39
39
  colors : list, optional
40
40
  Colors for each violin
41
41
  half : bool, optional
@@ -48,11 +48,18 @@ def plot_violin(
48
48
  ax : matplotlib.axes.Axes or scitex.plt._subplots.AxisWrapper
49
49
  The axes object with the plot
50
50
  """
51
+ # Add sample size to label if provided (show range if variable)
52
+ if kwargs.get("label"):
53
+ n_per_group = [len(g) for g in values_list]
54
+ n_min, n_max = min(n_per_group), max(n_per_group)
55
+ n_str = str(n_min) if n_min == n_max else f"{n_min}-{n_max}"
56
+ kwargs["label"] = f"{kwargs['label']} ($n$={n_str})"
57
+
51
58
  # Convert list-style data to DataFrame
52
59
  all_values = []
53
60
  all_groups = []
54
61
 
55
- for idx, values in enumerate(data_list):
62
+ for idx, values in enumerate(values_list):
56
63
  all_values.extend(values)
57
64
  group_label = labels[idx] if labels and idx < len(labels) else f"x {idx}"
58
65
  all_groups.extend([group_label] * len(values))
@@ -35,5 +35,8 @@ from ._share_axes import (
35
35
  set_ylims,
36
36
  )
37
37
  from ._shift import shift
38
+ from ._show_spines import show_spines
39
+ from ._style_boxplot import style_boxplot
40
+ from ._style_violinplot import style_violinplot
38
41
 
39
42
  # EOF
@@ -1,18 +1,25 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
- # Timestamp: "2025-11-19 14:52:00 (ywatanabe)"
3
+ # Timestamp: "2025-12-01 20:00:00 (ywatanabe)"
4
4
  # File: ./src/scitex/plt/ax/_style/_style_barplot.py
5
5
 
6
6
  """
7
7
  Style bar plot elements with millimeter-based control.
8
+
9
+ Default values are loaded from SCITEX_STYLE.yaml via presets.py.
8
10
  """
9
11
 
10
12
  from typing import Optional, Union, List
11
13
 
14
+ from scitex.plt.styles.presets import SCITEX_STYLE
15
+
16
+ # Get defaults from centralized config
17
+ _DEFAULT_EDGE_THICKNESS_MM = SCITEX_STYLE.get("bar_edge_thickness_mm", 0.2)
18
+
12
19
 
13
20
  def style_barplot(
14
21
  bar_container,
15
- edge_thickness_mm: float = 0.2,
22
+ edge_thickness_mm: float = None,
16
23
  edgecolor: Optional[Union[str, List[str]]] = 'black',
17
24
  ):
18
25
  """
@@ -40,6 +47,10 @@ def style_barplot(
40
47
  """
41
48
  from scitex.plt.utils import mm_to_pt
42
49
 
50
+ # Use centralized default if not specified
51
+ if edge_thickness_mm is None:
52
+ edge_thickness_mm = _DEFAULT_EDGE_THICKNESS_MM
53
+
43
54
  # Convert mm to points
44
55
  lw_pt = mm_to_pt(edge_thickness_mm)
45
56
 
@@ -1,77 +1,123 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
- # Timestamp: "2025-11-19 14:30:00 (ywatanabe)"
3
+ # Timestamp: "2025-12-01 20:00:00 (ywatanabe)"
4
4
  # File: ./src/scitex/plt/ax/_style/_style_boxplot.py
5
5
 
6
6
  """
7
7
  Style boxplot elements with millimeter-based control.
8
+
9
+ Default values are loaded from SCITEX_STYLE.yaml via presets.py.
8
10
  """
9
11
 
10
12
  from typing import Dict, Optional
11
13
  import matplotlib.pyplot as plt
12
14
 
15
+ from scitex.plt.styles.presets import SCITEX_STYLE
16
+
17
+ # Get defaults from centralized config
18
+ _DEFAULT_LINEWIDTH_MM = SCITEX_STYLE.get("trace_thickness_mm", 0.2)
19
+ _DEFAULT_FLIER_SIZE_MM = SCITEX_STYLE.get("marker_size_mm", 0.8)
20
+
13
21
 
14
22
  def style_boxplot(
15
23
  boxplot_dict,
16
- linewidth_mm: float = 0.8,
24
+ linewidth_mm: float = None,
25
+ flier_size_mm: float = None,
26
+ median_color: str = "black",
27
+ edge_color: str = "black",
17
28
  colors: Optional[list] = None,
18
29
  add_legend: bool = False,
19
30
  labels: Optional[list] = None,
20
31
  ):
21
- """
22
- Apply consistent styling to matplotlib boxplot elements.
32
+ """Apply publication-quality styling to matplotlib boxplot elements.
33
+
34
+ This function modifies boxplots to:
35
+ - Set consistent line widths for all elements
36
+ - Set median line to black for visibility
37
+ - Set edge colors to black
38
+ - Apply consistent outlier marker styling
39
+ - Use scitex color palette by default for box fills
23
40
 
24
41
  Parameters
25
42
  ----------
26
43
  boxplot_dict : dict
27
- Dictionary returned by ax.boxplot()
28
- linewidth_mm : float, optional
29
- Line width in millimeters (default: 0.8mm for balanced appearance)
44
+ Dictionary returned by ax.boxplot().
45
+ linewidth_mm : float, default 0.2
46
+ Line width in millimeters for all elements.
47
+ flier_size_mm : float, default 0.8
48
+ Outlier (flier) marker size in millimeters.
49
+ median_color : str, default "black"
50
+ Color for the median line inside boxes.
51
+ edge_color : str, default "black"
52
+ Color for box edges, whiskers, and caps.
30
53
  colors : list, optional
31
- List of colors for each box. If None, uses default matplotlib colors.
32
- add_legend : bool, optional
33
- Whether to add a legend (default: False)
54
+ List of colors for each box fill. If None, uses scitex color palette.
55
+ add_legend : bool, default False
56
+ Whether to add a legend.
34
57
  labels : list, optional
35
- Labels for legend entries (required if add_legend=True)
58
+ Labels for legend entries (required if add_legend=True).
36
59
 
37
60
  Returns
38
61
  -------
39
62
  boxplot_dict : dict
40
- The styled boxplot dictionary
63
+ The styled boxplot dictionary.
41
64
 
42
65
  Examples
43
66
  --------
44
- >>> fig, ax = stx.plt.subplots(**stx.plt.presets.NATURE_STYLE)
67
+ >>> import scitex as stx
68
+ >>> import numpy as np
69
+ >>> fig, ax = stx.plt.subplots()
45
70
  >>> box_data = [np.random.normal(0, 1, 100) for _ in range(4)]
46
- >>> bp = ax.boxplot(box_data)
47
- >>> stx.ax.style_boxplot(bp, linewidth_mm=0.8, colors=['blue', 'red', 'green', 'orange'])
71
+ >>> bp = ax.boxplot(box_data, patch_artist=True)
72
+ >>> stx.plt.ax.style_boxplot(bp, median_color="black")
48
73
  """
49
74
  from scitex.plt.utils import mm_to_pt
75
+ from scitex.plt.color._PARAMS import HEX
76
+
77
+ # Use centralized defaults if not specified
78
+ if linewidth_mm is None:
79
+ linewidth_mm = _DEFAULT_LINEWIDTH_MM
80
+ if flier_size_mm is None:
81
+ flier_size_mm = _DEFAULT_FLIER_SIZE_MM
50
82
 
51
83
  # Convert mm to points
52
84
  lw_pt = mm_to_pt(linewidth_mm)
85
+ flier_size_pt = mm_to_pt(flier_size_mm)
86
+
87
+ # Use scitex color palette by default
88
+ if colors is None:
89
+ colors = [
90
+ HEX["blue"], HEX["red"], HEX["green"], HEX["yellow"],
91
+ HEX["purple"], HEX["orange"], HEX["lightblue"], HEX["pink"],
92
+ ]
53
93
 
54
- # Style box elements
55
- for element_name in ['boxes', 'whiskers', 'caps', 'medians', 'fliers']:
94
+ # Style box elements with line width
95
+ for element_name in ['boxes', 'whiskers', 'caps']:
56
96
  if element_name in boxplot_dict:
57
97
  for element in boxplot_dict[element_name]:
58
98
  element.set_linewidth(lw_pt)
99
+ element.set_color(edge_color)
100
+
101
+ # Style medians with specified color
102
+ if 'medians' in boxplot_dict:
103
+ for median in boxplot_dict['medians']:
104
+ median.set_linewidth(lw_pt)
105
+ median.set_color(median_color)
106
+
107
+ # Style fliers (outliers) with marker size
108
+ if 'fliers' in boxplot_dict:
109
+ for flier in boxplot_dict['fliers']:
110
+ flier.set_markersize(flier_size_pt)
111
+ flier.set_markeredgewidth(lw_pt)
112
+ flier.set_markeredgecolor(edge_color)
113
+ flier.set_markerfacecolor('none') # Open circles
59
114
 
60
- # Apply colors if provided
61
- if colors is not None:
62
- n_boxes = len(boxplot_dict.get('boxes', []))
63
- for i, box in enumerate(boxplot_dict.get('boxes', [])):
64
- color = colors[i % len(colors)]
65
- box.set_edgecolor(color)
66
- # Also color the associated whiskers, caps, and median
67
- if 'whiskers' in boxplot_dict:
68
- boxplot_dict['whiskers'][i*2].set_color(color)
69
- boxplot_dict['whiskers'][i*2+1].set_color(color)
70
- if 'caps' in boxplot_dict:
71
- boxplot_dict['caps'][i*2].set_color(color)
72
- boxplot_dict['caps'][i*2+1].set_color(color)
73
- if 'medians' in boxplot_dict and i < len(boxplot_dict['medians']):
74
- boxplot_dict['medians'][i].set_color(color)
115
+ # Apply fill colors to boxes
116
+ for i, box in enumerate(boxplot_dict.get('boxes', [])):
117
+ color = colors[i % len(colors)]
118
+ if hasattr(box, 'set_facecolor'):
119
+ box.set_facecolor(color)
120
+ box.set_edgecolor(edge_color)
75
121
 
76
122
  # Add legend if requested
77
123
  if add_legend and labels is not None:
@@ -1,19 +1,27 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
- # Timestamp: "2025-11-19 14:50:00 (ywatanabe)"
3
+ # Timestamp: "2025-12-01 20:00:00 (ywatanabe)"
4
4
  # File: ./src/scitex/plt/ax/_style/_style_errorbar.py
5
5
 
6
6
  """
7
7
  Style error bar elements with millimeter-based control.
8
+
9
+ Default values are loaded from SCITEX_STYLE.yaml via presets.py.
8
10
  """
9
11
 
10
12
  from typing import Optional
11
13
 
14
+ from scitex.plt.styles.presets import SCITEX_STYLE
15
+
16
+ # Get defaults from centralized config
17
+ _DEFAULT_THICKNESS_MM = SCITEX_STYLE.get("trace_thickness_mm", 0.2)
18
+ _DEFAULT_CAP_WIDTH_MM = SCITEX_STYLE.get("errorbar_cap_width_mm", 0.8)
19
+
12
20
 
13
21
  def style_errorbar(
14
22
  errorbar_container,
15
- thickness_mm: float = 0.2,
16
- cap_width_mm: float = 0.8,
23
+ thickness_mm: float = None,
24
+ cap_width_mm: float = None,
17
25
  ):
18
26
  """
19
27
  Apply consistent styling to matplotlib errorbar elements.
@@ -40,6 +48,12 @@ def style_errorbar(
40
48
  """
41
49
  from scitex.plt.utils import mm_to_pt
42
50
 
51
+ # Use centralized defaults if not specified
52
+ if thickness_mm is None:
53
+ thickness_mm = _DEFAULT_THICKNESS_MM
54
+ if cap_width_mm is None:
55
+ cap_width_mm = _DEFAULT_CAP_WIDTH_MM
56
+
43
57
  # Convert mm to points
44
58
  lw_pt = mm_to_pt(thickness_mm)
45
59
  cap_width_pt = mm_to_pt(cap_width_mm)
@@ -1,19 +1,27 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
- # Timestamp: "2025-11-19 15:00:00 (ywatanabe)"
3
+ # Timestamp: "2025-12-01 20:00:00 (ywatanabe)"
4
4
  # File: ./src/scitex/plt/ax/_style/_style_scatter.py
5
5
 
6
6
  """
7
7
  Style scatter plot elements with millimeter-based control.
8
+
9
+ Default values are loaded from SCITEX_STYLE.yaml via presets.py.
8
10
  """
9
11
 
10
12
  from typing import Optional
11
13
 
14
+ from scitex.plt.styles.presets import SCITEX_STYLE
15
+
16
+ # Get defaults from centralized config
17
+ _DEFAULT_SIZE_MM = SCITEX_STYLE.get("scatter_size_mm", 0.8)
18
+ _DEFAULT_EDGE_THICKNESS_MM = SCITEX_STYLE.get("marker_edge_width_mm", 0.0)
19
+
12
20
 
13
21
  def style_scatter(
14
22
  path_collection,
15
- size_mm: float = 0.8,
16
- edge_thickness_mm: float = 0.0,
23
+ size_mm: float = None,
24
+ edge_thickness_mm: float = None,
17
25
  ):
18
26
  """
19
27
  Apply consistent styling to matplotlib scatter plot elements.
@@ -46,6 +54,12 @@ def style_scatter(
46
54
  """
47
55
  from scitex.plt.utils import mm_to_pt
48
56
 
57
+ # Use centralized defaults if not specified
58
+ if size_mm is None:
59
+ size_mm = _DEFAULT_SIZE_MM
60
+ if edge_thickness_mm is None:
61
+ edge_thickness_mm = _DEFAULT_EDGE_THICKNESS_MM
62
+
49
63
  # Convert mm to points
50
64
  size_pt = mm_to_pt(size_mm)
51
65
 
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-01 20:00:00 (ywatanabe)"
4
+ # File: ./src/scitex/plt/ax/_style/_style_violinplot.py
5
+
6
+ """Style violin plot elements with millimeter-based control.
7
+
8
+ Default values are loaded from SCITEX_STYLE.yaml via presets.py.
9
+ """
10
+
11
+ from typing import Optional, Union
12
+
13
+ from matplotlib.axes import Axes
14
+
15
+ from scitex.plt.styles.presets import SCITEX_STYLE
16
+
17
+ # Get defaults from centralized config
18
+ _DEFAULT_LINEWIDTH_MM = SCITEX_STYLE.get("trace_thickness_mm", 0.2)
19
+
20
+
21
+ def style_violinplot(
22
+ ax: Union[Axes, "AxisWrapper"],
23
+ linewidth_mm: float = None,
24
+ edge_color: str = "black",
25
+ median_color: str = "black",
26
+ remove_caps: bool = True,
27
+ ) -> Union[Axes, "AxisWrapper"]:
28
+ """Apply publication-quality styling to seaborn violin plots.
29
+
30
+ This function modifies violin plots created by seaborn.violinplot() to:
31
+ - Add borders to the KDE (violin body) edges
32
+ - Remove caps from the internal boxplot whiskers
33
+ - Set median line to black for better visibility
34
+ - Apply consistent line widths
35
+
36
+ Parameters
37
+ ----------
38
+ ax : matplotlib.axes.Axes or AxisWrapper
39
+ The axes containing the violin plot.
40
+ linewidth_mm : float, default 0.2
41
+ Line width in millimeters for violin edges and boxplot elements.
42
+ edge_color : str, default "black"
43
+ Color for the violin body edges.
44
+ median_color : str, default "black"
45
+ Color for the median line inside the boxplot.
46
+ remove_caps : bool, default True
47
+ Whether to remove the caps (horizontal lines) from boxplot whiskers.
48
+
49
+ Returns
50
+ -------
51
+ ax : matplotlib.axes.Axes or AxisWrapper
52
+ The axes with styled violin plot.
53
+
54
+ Examples
55
+ --------
56
+ >>> import seaborn as sns
57
+ >>> import scitex as stx
58
+ >>> fig, ax = stx.plt.subplots()
59
+ >>> sns.violinplot(data=df, x="group", y="value", ax=ax)
60
+ >>> stx.plt.ax.style_violinplot(ax)
61
+ """
62
+ from scitex.plt.utils import mm_to_pt
63
+
64
+ # Use centralized default if not specified
65
+ if linewidth_mm is None:
66
+ linewidth_mm = _DEFAULT_LINEWIDTH_MM
67
+
68
+ lw_pt = mm_to_pt(linewidth_mm)
69
+
70
+ # Style violin bodies (PolyCollection)
71
+ for collection in ax.collections:
72
+ # Check if it's a violin body (PolyCollection with filled area)
73
+ if hasattr(collection, 'set_edgecolor'):
74
+ collection.set_edgecolor(edge_color)
75
+ collection.set_linewidth(lw_pt)
76
+
77
+ # Style internal boxplot elements (Line2D objects)
78
+ # Seaborn violin plot lines: whiskers (vertical), caps (horizontal), median (short horizontal)
79
+ lines = list(ax.lines)
80
+ n_violins = len([c for c in ax.collections if hasattr(c, 'get_paths') and len(c.get_paths()) > 0])
81
+
82
+ for line in lines:
83
+ # Get line data to identify element type
84
+ xdata = line.get_xdata()
85
+ ydata = line.get_ydata()
86
+
87
+ if len(ydata) != 2:
88
+ continue
89
+
90
+ # Caps are horizontal lines (same y-value for both points) with wider x-span
91
+ is_horizontal = ydata[0] == ydata[1]
92
+ x_span = abs(xdata[1] - xdata[0]) if len(xdata) == 2 else 0
93
+
94
+ if is_horizontal:
95
+ if remove_caps and x_span > 0.05:
96
+ # This is likely a cap (wider horizontal line at whisker ends)
97
+ line.set_visible(False)
98
+ else:
99
+ # This is likely a median line (short horizontal line)
100
+ line.set_color(median_color)
101
+ line.set_linewidth(lw_pt)
102
+ else:
103
+ # Vertical lines (whiskers)
104
+ line.set_linewidth(lw_pt)
105
+
106
+ return ax
107
+
108
+
109
+ # EOF
@@ -28,18 +28,18 @@ def vizualize_colors(colors):
28
28
  xx, yy, ss = gen_rand_sample()
29
29
 
30
30
  # # Box color plot
31
- # ax.plot_rectangle(
31
+ # ax.stx_rectangle(
32
32
  # xx=ii, yy=0, width=1, height=1, color=rgba, label=color_str
33
33
  # )
34
34
 
35
35
  # Line plot
36
- ax.plot_shaded_line(xx, yy - ss, yy, yy + ss, color=rgba, label=color_str)
36
+ ax.stx_shaded_line(xx, yy - ss, yy, yy + ss, color=rgba, label=color_str)
37
37
 
38
38
  # # Scatter plot
39
39
  # axes[2].scatter(xx, yy, color=rgba, label=color_str)
40
40
 
41
41
  # # KDE plot
42
- # axes[3].plot_kde(yy, color=rgba, label=color_str)
42
+ # axes[3].stx_kde(yy, color=rgba, label=color_str)
43
43
 
44
44
  # for ax in axes.flat:
45
45
  # # ax.axis("off")