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
@@ -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