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.
- scitex/ai/classification/reporters/reporter_utils/_Plotter.py +1 -1
- scitex/ai/plt/__init__.py +2 -2
- scitex/ai/plt/{_plot_conf_mat.py → _stx_conf_mat.py} +3 -3
- scitex/config/PriorityConfig.py +195 -0
- scitex/config/__init__.py +24 -0
- scitex/io/_save.py +125 -34
- scitex/io/_save_modules/_image.py +37 -20
- scitex/plt/__init__.py +470 -17
- scitex/plt/_subplots/_AxisWrapper.py +98 -50
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +559 -124
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +49 -8
- scitex/plt/_subplots/_SubplotsWrapper.py +76 -91
- scitex/plt/_subplots/_export_as_csv.py +127 -58
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +25 -16
- scitex/plt/_subplots/_export_as_csv_formatters/_format_contourf.py +54 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hexbin.py +41 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hist2d.py +41 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +59 -47
- scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +42 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_pie.py +42 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +72 -35
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +1 -1
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +53 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stem.py +42 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_step.py +42 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +48 -0
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_conf_mat.py → _format_stx_conf_mat.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_ecdf.py → _format_stx_ecdf.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_fillv.py → _format_stx_fillv.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_heatmap.py → _format_stx_heatmap.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_image.py → _format_stx_image.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_joyplot.py → _format_stx_joyplot.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_line.py → _format_stx_line.py} +3 -3
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_mean_ci.py → _format_stx_mean_ci.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_mean_std.py → _format_stx_mean_std.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_median_iqr.py → _format_stx_median_iqr.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_raster.py → _format_stx_raster.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_rectangle.py → _format_stx_rectangle.py} +1 -1
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_scatter_hist.py → _format_stx_scatter_hist.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_shaded_line.py → _format_stx_shaded_line.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/{_format_plot_violin.py → _format_stx_violin.py} +2 -2
- scitex/plt/_subplots/_export_as_csv_formatters/verify_formatters.py +23 -23
- scitex/plt/ax/__init__.py +16 -15
- scitex/plt/ax/_plot/__init__.py +30 -30
- scitex/plt/ax/_plot/_add_fitted_line.py +65 -11
- scitex/plt/ax/_plot/_plot_statistical_shaded_line.py +104 -76
- scitex/plt/ax/_plot/{_plot_conf_mat.py → _stx_conf_mat.py} +10 -10
- scitex/plt/ax/_plot/_stx_ecdf.py +109 -0
- scitex/plt/ax/_plot/{_plot_fillv.py → _stx_fillv.py} +7 -7
- scitex/plt/ax/_plot/_stx_heatmap.py +366 -0
- scitex/plt/ax/_plot/{_plot_image.py → _stx_image.py} +1 -1
- scitex/plt/ax/_plot/_stx_joyplot.py +113 -0
- scitex/plt/ax/_plot/{_plot_raster.py → _stx_raster.py} +37 -25
- scitex/plt/ax/_plot/{_plot_rectangle.py → _stx_rectangle.py} +10 -9
- scitex/plt/ax/_plot/{_plot_scatter_hist.py → _stx_scatter_hist.py} +1 -1
- scitex/plt/ax/_plot/_stx_shaded_line.py +215 -0
- scitex/plt/ax/_plot/{_plot_violin.py → _stx_violin.py} +13 -6
- scitex/plt/ax/_style/__init__.py +3 -0
- scitex/plt/ax/_style/_style_barplot.py +13 -2
- scitex/plt/ax/_style/_style_boxplot.py +78 -32
- scitex/plt/ax/_style/_style_errorbar.py +17 -3
- scitex/plt/ax/_style/_style_scatter.py +17 -3
- scitex/plt/ax/_style/_style_violinplot.py +109 -0
- scitex/plt/color/_vizualize_colors.py +3 -3
- scitex/plt/styles/SCITEX_STYLE.yaml +104 -0
- scitex/plt/styles/__init__.py +57 -0
- scitex/plt/styles/_plot_defaults.py +209 -0
- scitex/plt/styles/_plot_postprocess.py +518 -0
- scitex/plt/styles/_style_loader.py +268 -0
- scitex/plt/styles/presets.py +208 -0
- scitex/plt/utils/_collect_figure_metadata.py +160 -18
- scitex/plt/utils/_colorbar.py +72 -10
- scitex/plt/utils/_configure_mpl.py +108 -52
- scitex/plt/utils/_crop.py +21 -7
- scitex/plt/utils/_figure_mm.py +21 -7
- scitex/stats/__init__.py +13 -1
- scitex/stats/_schema.py +578 -0
- scitex/stats/tests/__init__.py +13 -0
- scitex/stats/tests/correlation/__init__.py +13 -0
- scitex/stats/tests/correlation/_test_pearson.py +262 -0
- scitex/vis/__init__.py +6 -0
- scitex/vis/editor/__init__.py +23 -0
- scitex/vis/editor/_defaults.py +205 -0
- scitex/vis/editor/_edit.py +342 -0
- scitex/vis/editor/_mpl_editor.py +231 -0
- scitex/vis/editor/_tkinter_editor.py +466 -0
- scitex/vis/editor/_web_editor.py +1440 -0
- scitex/vis/model/plot_types.py +15 -15
- {scitex-2.3.0.dist-info → scitex-2.4.1.dist-info}/METADATA +2 -1
- {scitex-2.3.0.dist-info → scitex-2.4.1.dist-info}/RECORD +94 -67
- {scitex-2.3.0.dist-info → scitex-2.4.1.dist-info}/WHEEL +1 -1
- scitex/plt/ax/_plot/_plot_ecdf.py +0 -84
- scitex/plt/ax/_plot/_plot_heatmap.py +0 -277
- scitex/plt/ax/_plot/_plot_joyplot.py +0 -77
- scitex/plt/ax/_plot/_plot_shaded_line.py +0 -142
- scitex/plt/presets.py +0 -224
- {scitex-2.3.0.dist-info → scitex-2.4.1.dist-info}/entry_points.txt +0 -0
- {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
|
|
20
|
+
def stx_violin(
|
|
21
21
|
ax,
|
|
22
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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))
|
scitex/plt/ax/_style/__init__.py
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
# Timestamp: "2025-
|
|
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 =
|
|
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-
|
|
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 =
|
|
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
|
-
|
|
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,
|
|
29
|
-
Line width in millimeters
|
|
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
|
|
32
|
-
add_legend : bool,
|
|
33
|
-
Whether to add a legend
|
|
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
|
-
>>>
|
|
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,
|
|
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'
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
color
|
|
65
|
-
|
|
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-
|
|
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 =
|
|
16
|
-
cap_width_mm: float =
|
|
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-
|
|
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 =
|
|
16
|
-
edge_thickness_mm: float =
|
|
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.
|
|
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.
|
|
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].
|
|
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")
|