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
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
# Timestamp: "2025-05-02 22:21:41 (ywatanabe)"
|
|
4
|
-
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/plt/ax/_plot/_plot_heatmap.py
|
|
5
|
-
# ----------------------------------------
|
|
6
|
-
import os
|
|
7
|
-
|
|
8
|
-
__FILE__ = "./src/scitex/plt/ax/_plot/_plot_heatmap.py"
|
|
9
|
-
__DIR__ = os.path.dirname(__FILE__)
|
|
10
|
-
# ----------------------------------------
|
|
11
|
-
|
|
12
|
-
import matplotlib
|
|
13
|
-
import matplotlib.pyplot as plt
|
|
14
|
-
import numpy as np
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def plot_heatmap(
|
|
18
|
-
ax,
|
|
19
|
-
data,
|
|
20
|
-
x_labels=None,
|
|
21
|
-
y_labels=None,
|
|
22
|
-
cmap="viridis",
|
|
23
|
-
cbar_label="ColorBar Label",
|
|
24
|
-
annot_format="{x:.1f}",
|
|
25
|
-
show_annot=True,
|
|
26
|
-
annot_color_lighter="black",
|
|
27
|
-
annot_color_darker="white",
|
|
28
|
-
**kwargs,
|
|
29
|
-
):
|
|
30
|
-
"""
|
|
31
|
-
Plot a heatmap on the given axes.
|
|
32
|
-
|
|
33
|
-
Parameters
|
|
34
|
-
----------
|
|
35
|
-
ax : matplotlib.axes.Axes
|
|
36
|
-
The axes to plot on
|
|
37
|
-
data : array-like
|
|
38
|
-
The 2D data to display as heatmap
|
|
39
|
-
x_labels : list, optional
|
|
40
|
-
Labels for the x-axis
|
|
41
|
-
y_labels : list, optional
|
|
42
|
-
Labels for the y-axis
|
|
43
|
-
cmap : str or matplotlib colormap, optional
|
|
44
|
-
Colormap to use, default "viridis"
|
|
45
|
-
cbar_label : str, optional
|
|
46
|
-
Label for the colorbar, default "ColorBar Label"
|
|
47
|
-
show_annot : bool, optional
|
|
48
|
-
Whether to annotate the heatmap with values, default True
|
|
49
|
-
annot_format : str, optional
|
|
50
|
-
Format string for annotations, default "{x:.1f}"
|
|
51
|
-
annot_color_lighter : str, optional
|
|
52
|
-
Color for annotations on lighter background, default "black"
|
|
53
|
-
annot_color_darker : str, optional
|
|
54
|
-
Color for annotations on darker background, default "white"
|
|
55
|
-
**kwargs
|
|
56
|
-
Additional keyword arguments passed to matplotlib.axes.Axes.imshow()
|
|
57
|
-
|
|
58
|
-
Returns
|
|
59
|
-
-------
|
|
60
|
-
im : matplotlib.image.AxesImage
|
|
61
|
-
The image object created by imshow
|
|
62
|
-
cbar : matplotlib.colorbar.Colorbar
|
|
63
|
-
The colorbar object
|
|
64
|
-
"""
|
|
65
|
-
|
|
66
|
-
im, cbar = _mpl_heatmap(
|
|
67
|
-
data,
|
|
68
|
-
x_labels,
|
|
69
|
-
y_labels,
|
|
70
|
-
ax=ax,
|
|
71
|
-
cmap=cmap,
|
|
72
|
-
cbarlabel=cbar_label,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
if show_annot:
|
|
76
|
-
textcolors = _switch_annot_colors(cmap, annot_color_lighter, annot_color_darker)
|
|
77
|
-
texts = _mpl_annotate_heatmap(
|
|
78
|
-
im,
|
|
79
|
-
valfmt=annot_format,
|
|
80
|
-
textcolors=textcolors,
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
return ax, im, cbar
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def _switch_annot_colors(cmap, annot_color_lighter, annot_color_darker):
|
|
87
|
-
# Get colormap
|
|
88
|
-
cmap_obj = plt.cm.get_cmap(cmap)
|
|
89
|
-
|
|
90
|
-
# Sample the colormap at its extremes
|
|
91
|
-
dark_color = cmap_obj(0.1) # Not using 0.0 to avoid edge effects
|
|
92
|
-
light_color = cmap_obj(0.9) # Not using 1.0 to avoid edge effects
|
|
93
|
-
|
|
94
|
-
# Calculate perceived brightness
|
|
95
|
-
dark_brightness = (
|
|
96
|
-
0.2126 * dark_color[0] + 0.7152 * dark_color[1] + 0.0722 * dark_color[2]
|
|
97
|
-
)
|
|
98
|
-
light_brightness = (
|
|
99
|
-
0.2126 * light_color[0] + 0.7152 * light_color[1] + 0.0722 * light_color[2]
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
# Choose text colors based on background brightness
|
|
103
|
-
if dark_brightness < 0.5:
|
|
104
|
-
return (annot_color_lighter, annot_color_darker)
|
|
105
|
-
else:
|
|
106
|
-
return (annot_color_darker, annot_color_lighter)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def _mpl_heatmap(
|
|
110
|
-
data, row_labels, col_labels, ax=None, cbar_kw=None, cbarlabel="", **kwargs
|
|
111
|
-
):
|
|
112
|
-
"""
|
|
113
|
-
A function to annotate a heatmap.
|
|
114
|
-
|
|
115
|
-
Parameters
|
|
116
|
-
----------
|
|
117
|
-
im : matplotlib.image.AxesImage
|
|
118
|
-
The image to be annotated
|
|
119
|
-
data : array-like, optional
|
|
120
|
-
Data used to annotate. If None, the image's array is used
|
|
121
|
-
valfmt : str or matplotlib.ticker.Formatter, optional
|
|
122
|
-
Format of the annotations, default "{x:.2f}"
|
|
123
|
-
textcolors : tuple of str, optional
|
|
124
|
-
Colors for the annotations. The first is used for values below
|
|
125
|
-
threshold, the second for those above, default ("lightgray", "black")
|
|
126
|
-
threshold : float, optional
|
|
127
|
-
Value in normalized colormap space (0 to 1) above which the
|
|
128
|
-
second color is used. If None, 0.7*max(data) is used
|
|
129
|
-
**textkw
|
|
130
|
-
Additional keyword arguments passed to matplotlib.axes.Axes.text()
|
|
131
|
-
|
|
132
|
-
Returns
|
|
133
|
-
-------
|
|
134
|
-
texts : list of matplotlib.text.Text
|
|
135
|
-
The annotation text objects
|
|
136
|
-
"""
|
|
137
|
-
|
|
138
|
-
if ax is None:
|
|
139
|
-
ax = plt.gca()
|
|
140
|
-
|
|
141
|
-
if cbar_kw is None:
|
|
142
|
-
cbar_kw = {}
|
|
143
|
-
|
|
144
|
-
# Plot the heatmap
|
|
145
|
-
im = ax.imshow(data, **kwargs)
|
|
146
|
-
|
|
147
|
-
# Create colorbar with proper formatting
|
|
148
|
-
cbar = ax.figure.colorbar(im, ax=ax, **cbar_kw)
|
|
149
|
-
cbar.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom")
|
|
150
|
-
|
|
151
|
-
# Set colorbar border width to match axes spines
|
|
152
|
-
cbar.outline.set_linewidth(0.2 * 2.83465) # 0.2mm in points
|
|
153
|
-
|
|
154
|
-
# Format colorbar ticks
|
|
155
|
-
from matplotlib.ticker import MaxNLocator
|
|
156
|
-
cbar.ax.yaxis.set_major_locator(MaxNLocator(nbins=4, min_n_ticks=3))
|
|
157
|
-
cbar.ax.tick_params(width=0.2 * 2.83465, length=0.8 * 2.83465) # Match tick styling
|
|
158
|
-
|
|
159
|
-
# Show all ticks and label them with the respective list entries.
|
|
160
|
-
ax.set_xticks(
|
|
161
|
-
range(data.shape[1]),
|
|
162
|
-
labels=col_labels,
|
|
163
|
-
# rotation=45,
|
|
164
|
-
# ha="right",
|
|
165
|
-
# rotation_mode="anchor",
|
|
166
|
-
)
|
|
167
|
-
ax.set_yticks(range(data.shape[0]), labels=row_labels)
|
|
168
|
-
|
|
169
|
-
# Let the horizontal axes labeling appear on top.
|
|
170
|
-
ax.tick_params(top=False, bottom=True, labeltop=False, labelbottom=True)
|
|
171
|
-
|
|
172
|
-
# Show all 4 spines for heatmap
|
|
173
|
-
ax.spines[:].set_visible(True)
|
|
174
|
-
|
|
175
|
-
# Set aspect ratio to 'equal' for square cells (1:1)
|
|
176
|
-
ax.set_aspect('equal', adjustable='box')
|
|
177
|
-
|
|
178
|
-
ax.set_xticks(np.arange(data.shape[1] + 1) - 0.5, minor=True)
|
|
179
|
-
ax.set_yticks(np.arange(data.shape[0] + 1) - 0.5, minor=True)
|
|
180
|
-
ax.tick_params(which="minor", bottom=False, left=False)
|
|
181
|
-
|
|
182
|
-
return im, cbar
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
def _mpl_annotate_heatmap(
|
|
186
|
-
im,
|
|
187
|
-
data=None,
|
|
188
|
-
valfmt="{x:.2f}",
|
|
189
|
-
textcolors=("lightgray", "black"),
|
|
190
|
-
threshold=None,
|
|
191
|
-
**textkw,
|
|
192
|
-
):
|
|
193
|
-
"""
|
|
194
|
-
A function to annotate a heatmap.
|
|
195
|
-
|
|
196
|
-
Parameters
|
|
197
|
-
----------
|
|
198
|
-
im : matplotlib.image.AxesImage
|
|
199
|
-
The image to be annotated
|
|
200
|
-
data : array-like, optional
|
|
201
|
-
Data used to annotate. If None, the image's array is used
|
|
202
|
-
valfmt : str or matplotlib.ticker.Formatter, optional
|
|
203
|
-
Format of the annotations, default "{x:.2f}"
|
|
204
|
-
textcolors : tuple of str, optional
|
|
205
|
-
Colors for the annotations. The first is used for values below
|
|
206
|
-
threshold, the second for those above, default ("lightgray", "black")
|
|
207
|
-
threshold : float, optional
|
|
208
|
-
Value in normalized colormap space (0 to 1) above which the
|
|
209
|
-
second color is used. If None, 0.7*max(data) is used
|
|
210
|
-
**textkw
|
|
211
|
-
Additional keyword arguments passed to matplotlib.axes.Axes.text()
|
|
212
|
-
|
|
213
|
-
Returns
|
|
214
|
-
-------
|
|
215
|
-
texts : list of matplotlib.text.Text
|
|
216
|
-
The annotation text objects
|
|
217
|
-
"""
|
|
218
|
-
|
|
219
|
-
if not isinstance(data, (list, np.ndarray)):
|
|
220
|
-
data = im.get_array()
|
|
221
|
-
|
|
222
|
-
# Normalize the threshold to the images color range.
|
|
223
|
-
if threshold is not None:
|
|
224
|
-
threshold = im.norm(threshold)
|
|
225
|
-
else:
|
|
226
|
-
# Use 0.7 instead of 0.5 for better visibility with most colormaps
|
|
227
|
-
threshold = im.norm(data.max()) * 0.7
|
|
228
|
-
|
|
229
|
-
# Set default alignment to center, but allow it to be
|
|
230
|
-
# overwritten by textkw.
|
|
231
|
-
kw = dict(horizontalalignment="center", verticalalignment="center")
|
|
232
|
-
kw.update(textkw)
|
|
233
|
-
|
|
234
|
-
# Get the formatter in case a string is supplied
|
|
235
|
-
if isinstance(valfmt, str):
|
|
236
|
-
valfmt = matplotlib.ticker.StrMethodFormatter(valfmt)
|
|
237
|
-
|
|
238
|
-
# Loop over the data and create a `Text` for each "pixel".
|
|
239
|
-
# Change the text's color depending on the data.
|
|
240
|
-
texts = []
|
|
241
|
-
for ii in range(data.shape[0]):
|
|
242
|
-
for jj in range(data.shape[1]):
|
|
243
|
-
kw.update(color=textcolors[int(im.norm(data[ii, jj]) > threshold)])
|
|
244
|
-
text = im.axes.text(jj, ii, valfmt(data[ii, jj], None), **kw)
|
|
245
|
-
texts.append(text)
|
|
246
|
-
|
|
247
|
-
return texts
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if __name__ == "__main__":
|
|
251
|
-
import matplotlib
|
|
252
|
-
import matplotlib as mpl
|
|
253
|
-
import matplotlib.pyplot as plt
|
|
254
|
-
import numpy as np
|
|
255
|
-
|
|
256
|
-
data = np.random.rand(5, 10)
|
|
257
|
-
x_labels = [f"X{ii+1}" for ii in range(5)]
|
|
258
|
-
y_labels = [f"Y{ii+1}" for ii in range(10)]
|
|
259
|
-
|
|
260
|
-
fig, ax = plt.subplots()
|
|
261
|
-
|
|
262
|
-
im, cbar = plot_heatmap(
|
|
263
|
-
ax,
|
|
264
|
-
data,
|
|
265
|
-
x_labels=x_labels,
|
|
266
|
-
y_labels=y_labels,
|
|
267
|
-
show_annot=True,
|
|
268
|
-
annot_color_lighter="white",
|
|
269
|
-
annot_color_darker="black",
|
|
270
|
-
cmap="Blues",
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
fig.tight_layout()
|
|
274
|
-
plt.show()
|
|
275
|
-
# EOF
|
|
276
|
-
|
|
277
|
-
# EOF
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
# Timestamp: "2025-05-02 09:03:23 (ywatanabe)"
|
|
4
|
-
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/plt/ax/_plot/_plot_joyplot.py
|
|
5
|
-
# ----------------------------------------
|
|
6
|
-
import os
|
|
7
|
-
|
|
8
|
-
__FILE__ = "./src/scitex/plt/ax/_plot/_plot_joyplot.py"
|
|
9
|
-
__DIR__ = os.path.dirname(__FILE__)
|
|
10
|
-
# ----------------------------------------
|
|
11
|
-
|
|
12
|
-
import warnings
|
|
13
|
-
|
|
14
|
-
from .._style._set_xyt import set_xyt as scitex_plt_set_xyt
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def plot_joyplot(ax, data, orientation="vertical", **kwargs):
|
|
18
|
-
"""
|
|
19
|
-
Create a joyplot (ridgeline plot) with proper orientation handling.
|
|
20
|
-
|
|
21
|
-
Parameters
|
|
22
|
-
----------
|
|
23
|
-
ax : matplotlib.axes.Axes
|
|
24
|
-
The axes to plot on
|
|
25
|
-
data : pandas.DataFrame or array-like
|
|
26
|
-
The data to plot
|
|
27
|
-
orientation : str, default "vertical"
|
|
28
|
-
Plot orientation. Either "vertical" or "horizontal"
|
|
29
|
-
**kwargs
|
|
30
|
-
Additional keyword arguments passed to joypy.joyplot()
|
|
31
|
-
|
|
32
|
-
Returns
|
|
33
|
-
-------
|
|
34
|
-
matplotlib.axes.Axes
|
|
35
|
-
The axes with the joyplot
|
|
36
|
-
|
|
37
|
-
Raises
|
|
38
|
-
------
|
|
39
|
-
ValueError
|
|
40
|
-
If orientation is not "vertical" or "horizontal"
|
|
41
|
-
"""
|
|
42
|
-
# Lazy import to avoid scipy circular import on startup
|
|
43
|
-
import joypy
|
|
44
|
-
|
|
45
|
-
if orientation not in ["vertical", "horizontal"]:
|
|
46
|
-
raise ValueError("orientation must be either 'vertical' or 'horizontal'")
|
|
47
|
-
|
|
48
|
-
# Handle orientation by setting appropriate joypy parameters
|
|
49
|
-
if orientation == "horizontal":
|
|
50
|
-
# For horizontal orientation, we need to transpose the data display
|
|
51
|
-
# joypy doesn't have direct horizontal support, so we work with the result
|
|
52
|
-
kwargs.setdefault("kind", "kde") # Ensure we're using KDE plots
|
|
53
|
-
|
|
54
|
-
fig, axes = joypy.joyplot(
|
|
55
|
-
data=data,
|
|
56
|
-
**kwargs,
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
# Set appropriate labels based on orientation
|
|
60
|
-
if orientation == "vertical":
|
|
61
|
-
ax = scitex_plt_set_xyt(ax, None, "Density", "Joyplot")
|
|
62
|
-
elif orientation == "horizontal":
|
|
63
|
-
ax = scitex_plt_set_xyt(ax, "Density", None, "Joyplot")
|
|
64
|
-
# For horizontal plots, we might need additional transformations
|
|
65
|
-
# This is a limitation of joypy which primarily supports vertical plots
|
|
66
|
-
|
|
67
|
-
return ax
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# def plot_vertical_joyplot(ax, data, **kwargs):
|
|
71
|
-
# return _plot_joyplot(ax, data, "vertical", **kwargs)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# def plot_horizontal_joyplot(ax, data, **kwargs):
|
|
75
|
-
# return _plot_joyplot(ax, data, "horizontal", **kwargs)
|
|
76
|
-
|
|
77
|
-
# EOF
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
# Timestamp: "2025-05-02 23:28:32 (ywatanabe)"
|
|
4
|
-
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/plt/ax/_plot/_plot_shaded_line.py
|
|
5
|
-
# ----------------------------------------
|
|
6
|
-
import os
|
|
7
|
-
|
|
8
|
-
__FILE__ = "./src/scitex/plt/ax/_plot/_plot_shaded_line.py"
|
|
9
|
-
__DIR__ = os.path.dirname(__FILE__)
|
|
10
|
-
# ----------------------------------------
|
|
11
|
-
|
|
12
|
-
from typing import List, Optional, Tuple, Union
|
|
13
|
-
|
|
14
|
-
import matplotlib
|
|
15
|
-
import numpy as np
|
|
16
|
-
import pandas as pd
|
|
17
|
-
from matplotlib.axes._axes import Axes
|
|
18
|
-
|
|
19
|
-
from scitex.types import ColorLike
|
|
20
|
-
from ....plt.utils import assert_valid_axis
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _plot_single_shaded_line(
|
|
24
|
-
axis: Union[Axes, 'AxisWrapper'],
|
|
25
|
-
xx: np.ndarray,
|
|
26
|
-
y_lower: np.ndarray,
|
|
27
|
-
y_middle: np.ndarray,
|
|
28
|
-
y_upper: np.ndarray,
|
|
29
|
-
color: Optional[ColorLike] = None,
|
|
30
|
-
alpha: float = 0.3,
|
|
31
|
-
**kwargs
|
|
32
|
-
) -> Tuple[Union[Axes, 'AxisWrapper'], pd.DataFrame]:
|
|
33
|
-
"""Plot a line with shaded area between y_lower and y_upper bounds."""
|
|
34
|
-
assert_valid_axis(axis, "First argument must be a matplotlib axis or scitex axis wrapper")
|
|
35
|
-
assert (
|
|
36
|
-
len(xx) == len(y_middle) == len(y_lower) == len(y_upper)
|
|
37
|
-
), "All arrays must have the same length"
|
|
38
|
-
|
|
39
|
-
label = kwargs.get("label")
|
|
40
|
-
if kwargs.get("label"):
|
|
41
|
-
del kwargs["label"]
|
|
42
|
-
axis.plot(xx, y_middle, color=color, alpha=alpha, label=label, **kwargs)
|
|
43
|
-
kwargs["linewidth"] = 0
|
|
44
|
-
kwargs["edgecolor"] = "none" # Remove edge line
|
|
45
|
-
axis.fill_between(xx, y_lower, y_upper, alpha=alpha, color=color, **kwargs)
|
|
46
|
-
|
|
47
|
-
return axis, pd.DataFrame(
|
|
48
|
-
{"x": xx, "y_lower": y_lower, "y_middle": y_middle, "y_upper": y_upper}
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def _plot_shaded_line(
|
|
53
|
-
axis: Union[Axes, 'AxisWrapper'],
|
|
54
|
-
xs: List[np.ndarray],
|
|
55
|
-
ys_lower: List[np.ndarray],
|
|
56
|
-
ys_middle: List[np.ndarray],
|
|
57
|
-
ys_upper: List[np.ndarray],
|
|
58
|
-
color: Optional[Union[List[ColorLike], ColorLike]] = None,
|
|
59
|
-
**kwargs
|
|
60
|
-
) -> Tuple[Union[Axes, 'AxisWrapper'], List[pd.DataFrame]]:
|
|
61
|
-
"""Plot multiple lines with shaded areas between ys_lower and ys_upper bounds."""
|
|
62
|
-
assert_valid_axis(axis, "First argument must be a matplotlib axis or scitex axis wrapper")
|
|
63
|
-
assert (
|
|
64
|
-
len(xs) == len(ys_lower) == len(ys_middle) == len(ys_upper)
|
|
65
|
-
), "All input lists must have the same length"
|
|
66
|
-
|
|
67
|
-
results = []
|
|
68
|
-
colors = color
|
|
69
|
-
color_list = colors
|
|
70
|
-
|
|
71
|
-
if colors is not None:
|
|
72
|
-
if not isinstance(colors, list):
|
|
73
|
-
color_list = [colors] * len(xs)
|
|
74
|
-
else:
|
|
75
|
-
assert len(colors) == len(xs), "Number of colors must match number of lines"
|
|
76
|
-
color_list = colors
|
|
77
|
-
|
|
78
|
-
for idx, (xx, y_lower, y_middle, y_upper) in enumerate(
|
|
79
|
-
zip(xs, ys_lower, ys_middle, ys_upper)
|
|
80
|
-
):
|
|
81
|
-
this_kwargs = kwargs.copy()
|
|
82
|
-
this_kwargs["color"] = color_list[idx]
|
|
83
|
-
_, result_df = _plot_single_shaded_line(
|
|
84
|
-
axis, xx, y_lower, y_middle, y_upper, **this_kwargs
|
|
85
|
-
)
|
|
86
|
-
results.append(result_df)
|
|
87
|
-
else:
|
|
88
|
-
for xx, y_lower, y_middle, y_upper in zip(xs, ys_lower, ys_middle, ys_upper):
|
|
89
|
-
_, result_df = _plot_single_shaded_line(
|
|
90
|
-
axis, xx, y_lower, y_middle, y_upper, **kwargs
|
|
91
|
-
)
|
|
92
|
-
results.append(result_df)
|
|
93
|
-
|
|
94
|
-
return axis, results
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def plot_shaded_line(
|
|
98
|
-
axis: Union[Axes, 'AxisWrapper'],
|
|
99
|
-
xs: Union[np.ndarray, List[np.ndarray]],
|
|
100
|
-
ys_lower: Union[np.ndarray, List[np.ndarray]],
|
|
101
|
-
ys_middle: Union[np.ndarray, List[np.ndarray]],
|
|
102
|
-
ys_upper: Union[np.ndarray, List[np.ndarray]],
|
|
103
|
-
color: Optional[Union[ColorLike, List[ColorLike]]] = None,
|
|
104
|
-
**kwargs
|
|
105
|
-
) -> Tuple[Union[Axes, 'AxisWrapper'], Union[pd.DataFrame, List[pd.DataFrame]]]:
|
|
106
|
-
"""
|
|
107
|
-
Plot a line with shaded area, automatically switching between single and multiple line versions.
|
|
108
|
-
|
|
109
|
-
Args:
|
|
110
|
-
axis: matplotlib axis or scitex axis wrapper
|
|
111
|
-
xs: x values (single array or list of arrays)
|
|
112
|
-
ys_lower: lower bound y values (single array or list of arrays)
|
|
113
|
-
ys_middle: middle y values (single array or list of arrays)
|
|
114
|
-
ys_upper: upper bound y values (single array or list of arrays)
|
|
115
|
-
color: color or list of colors for the lines
|
|
116
|
-
**kwargs: additional plotting parameters
|
|
117
|
-
|
|
118
|
-
Returns:
|
|
119
|
-
tuple: (axis, DataFrame or list of DataFrames with plot data)
|
|
120
|
-
"""
|
|
121
|
-
is_single = not (
|
|
122
|
-
isinstance(xs, list)
|
|
123
|
-
and isinstance(ys_lower, list)
|
|
124
|
-
and isinstance(ys_middle, list)
|
|
125
|
-
and isinstance(ys_upper, list)
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
if is_single:
|
|
129
|
-
assert (
|
|
130
|
-
len(xs) == len(ys_lower) == len(ys_middle) == len(ys_upper)
|
|
131
|
-
), "All arrays must have the same length for single line plot"
|
|
132
|
-
|
|
133
|
-
return _plot_single_shaded_line(
|
|
134
|
-
axis, xs, ys_lower, ys_middle, ys_upper, color=color, **kwargs
|
|
135
|
-
)
|
|
136
|
-
else:
|
|
137
|
-
return _plot_shaded_line(
|
|
138
|
-
axis, xs, ys_lower, ys_middle, ys_upper, color=color, **kwargs
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
# EOF
|