plotlp 0.1.16__tar.gz → 0.1.18__tar.gz

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 (85) hide show
  1. {plotlp-0.1.16 → plotlp-0.1.18}/PKG-INFO +1 -1
  2. {plotlp-0.1.16 → plotlp-0.1.18}/pyproject.toml +1 -1
  3. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/__init__.py +1 -0
  4. plotlp-0.1.18/src/plotlp/modules/compare_distributions_LP/compare_distributions.py +326 -0
  5. plotlp-0.1.18/src/plotlp/modules/compare_distributions_LP/test_compare_distributions.py +52 -0
  6. plotlp-0.1.18/src/plotlp/modules/subplots_LP/_doctype.txt +1 -0
  7. plotlp-0.1.18/src/plotlp/modules/subplots_LP/_functions/__template__.py +19 -0
  8. plotlp-0.1.18/src/plotlp/py.typed +0 -0
  9. plotlp-0.1.18/src/plotlp/routines/__init__.py +0 -0
  10. {plotlp-0.1.16 → plotlp-0.1.18}/README.md +0 -0
  11. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/.docignore +0 -0
  12. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledAxes_LP/StyledAxes.py +0 -0
  13. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledAxes_LP/__init__.py +0 -0
  14. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledAxes_LP/_doctype.txt +0 -0
  15. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledAxes_LP/_functions/__init__.py +0 -0
  16. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledAxes_LP/_functions/__template__.py +0 -0
  17. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledAxes_LP/_functions/scalebar.py +0 -0
  18. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledAxes_LP/test_StyledAxes.py +0 -0
  19. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledFigure_LP/StyledFigure.py +0 -0
  20. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledFigure_LP/__init__.py +0 -0
  21. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledFigure_LP/_doctype.txt +0 -0
  22. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledFigure_LP/_functions/__init__.py +0 -0
  23. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledFigure_LP/_functions/__template__.py +0 -0
  24. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/StyledFigure_LP/test_StyledFigure.py +0 -0
  25. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/__init__.py +0 -0
  26. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/cmap_LP/__init__.py +0 -0
  27. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/cmap_LP/_doctype.txt +0 -0
  28. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/cmap_LP/_functions/__init__.py +0 -0
  29. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/cmap_LP/_functions/__template__.py +0 -0
  30. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/cmap_LP/cmap.py +0 -0
  31. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/cmap_LP/test_cmap.py +0 -0
  32. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/color_LP/__init__.py +0 -0
  33. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/color_LP/_doctype.txt +0 -0
  34. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/color_LP/_functions/__init__.py +0 -0
  35. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/color_LP/_functions/__template__.py +0 -0
  36. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/color_LP/color.py +0 -0
  37. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/color_LP/test_color.py +0 -0
  38. {plotlp-0.1.16/src/plotlp/modules/figure_LP → plotlp-0.1.18/src/plotlp/modules/compare_distributions_LP}/__init__.py +0 -0
  39. {plotlp-0.1.16/src/plotlp/modules/figure_LP → plotlp-0.1.18/src/plotlp/modules/compare_distributions_LP}/_doctype.txt +0 -0
  40. {plotlp-0.1.16/src/plotlp/modules/figure_LP → plotlp-0.1.18/src/plotlp/modules/compare_distributions_LP}/_functions/__init__.py +0 -0
  41. {plotlp-0.1.16/src/plotlp/modules/figure_LP → plotlp-0.1.18/src/plotlp/modules/compare_distributions_LP}/_functions/__template__.py +0 -0
  42. {plotlp-0.1.16/src/plotlp/modules/gridsubplots_LP → plotlp-0.1.18/src/plotlp/modules/figure_LP}/__init__.py +0 -0
  43. {plotlp-0.1.16/src/plotlp/modules/gridsubplots_LP → plotlp-0.1.18/src/plotlp/modules/figure_LP}/_doctype.txt +0 -0
  44. {plotlp-0.1.16/src/plotlp/modules/gridsubplots_LP → plotlp-0.1.18/src/plotlp/modules/figure_LP}/_functions/__init__.py +0 -0
  45. {plotlp-0.1.16/src/plotlp/modules/gridsubplots_LP → plotlp-0.1.18/src/plotlp/modules/figure_LP}/_functions/__template__.py +0 -0
  46. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/figure_LP/figure.py +0 -0
  47. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/figure_LP/test_figure.py +0 -0
  48. {plotlp-0.1.16/src/plotlp/modules/imgfigure_LP → plotlp-0.1.18/src/plotlp/modules/gridsubplots_LP}/__init__.py +0 -0
  49. {plotlp-0.1.16/src/plotlp/modules/imgfigure_LP → plotlp-0.1.18/src/plotlp/modules/gridsubplots_LP}/_doctype.txt +0 -0
  50. {plotlp-0.1.16/src/plotlp/modules/imgfigure_LP → plotlp-0.1.18/src/plotlp/modules/gridsubplots_LP}/_functions/__init__.py +0 -0
  51. {plotlp-0.1.16/src/plotlp/modules/imgfigure_LP → plotlp-0.1.18/src/plotlp/modules/gridsubplots_LP}/_functions/__template__.py +0 -0
  52. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/gridsubplots_LP/gridsubplots.py +0 -0
  53. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/gridsubplots_LP/test_gridsubplots.py +0 -0
  54. {plotlp-0.1.16/src/plotlp/modules/make_animation_LP → plotlp-0.1.18/src/plotlp/modules/imgfigure_LP}/__init__.py +0 -0
  55. {plotlp-0.1.16/src/plotlp/modules/make_animation_LP → plotlp-0.1.18/src/plotlp/modules/imgfigure_LP}/_doctype.txt +0 -0
  56. {plotlp-0.1.16/src/plotlp/modules/make_animation_LP → plotlp-0.1.18/src/plotlp/modules/imgfigure_LP}/_functions/__init__.py +0 -0
  57. {plotlp-0.1.16/src/plotlp/modules/make_animation_LP → plotlp-0.1.18/src/plotlp/modules/imgfigure_LP}/_functions/__template__.py +0 -0
  58. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/imgfigure_LP/imgfigure.py +0 -0
  59. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/imgfigure_LP/test_imgfigure.py +0 -0
  60. {plotlp-0.1.16/src/plotlp/modules/plt_LP → plotlp-0.1.18/src/plotlp/modules/make_animation_LP}/__init__.py +0 -0
  61. {plotlp-0.1.16/src/plotlp/modules/plt_LP → plotlp-0.1.18/src/plotlp/modules/make_animation_LP}/_doctype.txt +0 -0
  62. {plotlp-0.1.16/src/plotlp/modules/plt_LP → plotlp-0.1.18/src/plotlp/modules/make_animation_LP}/_functions/__init__.py +0 -0
  63. {plotlp-0.1.16/src/plotlp/modules/plt_LP → plotlp-0.1.18/src/plotlp/modules/make_animation_LP}/_functions/__template__.py +0 -0
  64. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/make_animation_LP/make_animation.py +0 -0
  65. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/make_animation_LP/test_make_animation.py +0 -0
  66. {plotlp-0.1.16/src/plotlp/modules/style_LP → plotlp-0.1.18/src/plotlp/modules/plt_LP}/__init__.py +0 -0
  67. {plotlp-0.1.16/src/plotlp/modules/style_LP → plotlp-0.1.18/src/plotlp/modules/plt_LP}/_doctype.txt +0 -0
  68. {plotlp-0.1.16/src/plotlp/modules/style_LP → plotlp-0.1.18/src/plotlp/modules/plt_LP}/_functions/__init__.py +0 -0
  69. {plotlp-0.1.16/src/plotlp/modules/style_LP → plotlp-0.1.18/src/plotlp/modules/plt_LP}/_functions/__template__.py +0 -0
  70. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/plt_LP/plt.py +0 -0
  71. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/plt_LP/test_plt.py +0 -0
  72. {plotlp-0.1.16/src/plotlp/modules/subplots_LP → plotlp-0.1.18/src/plotlp/modules/style_LP}/__init__.py +0 -0
  73. {plotlp-0.1.16/src/plotlp/modules/subplots_LP → plotlp-0.1.18/src/plotlp/modules/style_LP}/_doctype.txt +0 -0
  74. {plotlp-0.1.16/src/plotlp/modules/subplots_LP → plotlp-0.1.18/src/plotlp/modules/style_LP}/_functions/__init__.py +0 -0
  75. {plotlp-0.1.16/src/plotlp/modules/subplots_LP → plotlp-0.1.18/src/plotlp/modules/style_LP}/_functions/__template__.py +0 -0
  76. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/style_LP/_functions/_darkLP.py +0 -0
  77. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/style_LP/_functions/_default.py +0 -0
  78. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/style_LP/_functions/_lightLP.py +0 -0
  79. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/style_LP/_functions/_styleLP.py +0 -0
  80. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/style_LP/style.py +0 -0
  81. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/style_LP/test_style.py +0 -0
  82. {plotlp-0.1.16/src/plotlp/routines → plotlp-0.1.18/src/plotlp/modules/subplots_LP}/__init__.py +0 -0
  83. /plotlp-0.1.16/src/plotlp/py.typed → /plotlp-0.1.18/src/plotlp/modules/subplots_LP/_functions/__init__.py +0 -0
  84. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/subplots_LP/subplots.py +0 -0
  85. {plotlp-0.1.16 → plotlp-0.1.18}/src/plotlp/modules/subplots_LP/test_subplots.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: plotlp
3
- Version: 0.1.16
3
+ Version: 0.1.18
4
4
  Summary: A library wrapper around matplotlib for custom plots.
5
5
  Requires-Dist: corelp
6
6
  Requires-Dist: cycler
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plotlp"
3
- version = "0.1.16"
3
+ version = "0.1.18"
4
4
  description = "A library wrapper around matplotlib for custom plots."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -17,6 +17,7 @@ sources = {
17
17
  'StyledFigure': 'plotlp.modules.StyledFigure_LP.StyledFigure',
18
18
  'cmap': 'plotlp.modules.cmap_LP.cmap',
19
19
  'color': 'plotlp.modules.color_LP.color',
20
+ 'compare_distributions': 'plotlp.modules.compare_distributions_LP.compare_distributions',
20
21
  'figure': 'plotlp.modules.figure_LP.figure',
21
22
  'gridsubplots': 'plotlp.modules.gridsubplots_LP.gridsubplots',
22
23
  'imgfigure': 'plotlp.modules.imgfigure_LP.imgfigure',
@@ -0,0 +1,326 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2026-05-28
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : plotLP
7
+ # Module : compare_distributions
8
+
9
+ """
10
+ This function create a figure which compares distributions via histograms and scatterplots.
11
+ """
12
+
13
+
14
+
15
+ # %% Libraries
16
+ import numpy as np
17
+ from plotlp import subplots
18
+
19
+
20
+
21
+ # %% Function
22
+ def compare_distributions(
23
+ *vectors,
24
+ labels=None,
25
+ hue=None,
26
+ npoints=1000,
27
+ bins=64,
28
+ percentile=99,
29
+ bissectrice=False,
30
+ title=None,
31
+ ):
32
+ """Compare distributions with pairwise scatterplots and diagonal histograms."""
33
+ if len(vectors) < 2:
34
+ raise ValueError("compare_distributions requires at least two vectors")
35
+
36
+ vectors = [np.asarray(vector, dtype=np.float32).ravel() for vector in vectors]
37
+ n = len(vectors)
38
+
39
+ if labels is None:
40
+ labels = [f"Vector {index + 1}" for index in range(n)]
41
+ labels = list(labels)
42
+ if len(labels) != n:
43
+ raise ValueError("labels length must match the number of vectors")
44
+
45
+ if hue is not None:
46
+ hue = np.asarray(hue).ravel()
47
+ expected_size = vectors[0].size
48
+ if any(vector.size != expected_size for vector in vectors):
49
+ raise ValueError("all vectors must have the same size when hue is provided")
50
+ if hue.size != expected_size:
51
+ raise ValueError("hue length must match vector length")
52
+ categories = list(dict.fromkeys(hue.tolist()))
53
+ else:
54
+ categories = [None]
55
+
56
+ npoints = int(npoints)
57
+ if npoints < 1:
58
+ raise ValueError("npoints must be >= 1")
59
+
60
+ percentile = float(percentile)
61
+ if percentile <= 0 or percentile > 100:
62
+ raise ValueError("percentile must be in (0, 100]")
63
+ q_low = (100 - percentile) / 2
64
+ q_high = 100 - q_low
65
+
66
+ global_scatter_limits = None
67
+ if bissectrice:
68
+ finite_all = [values[np.isfinite(values)] for values in vectors]
69
+ finite_all = [values for values in finite_all if values.size]
70
+ if finite_all:
71
+ limits_values = np.concatenate(finite_all)
72
+ low = float(np.nanpercentile(limits_values, q_low))
73
+ high = float(np.nanpercentile(limits_values, q_high))
74
+ if high <= low:
75
+ high = low + 1.0
76
+ global_scatter_limits = (low, high)
77
+
78
+ color_map = {category: f"C{index % 10}" for index, category in enumerate(categories)}
79
+
80
+ if n == 2:
81
+ figure, axes = subplots(
82
+ 2,
83
+ 2,
84
+ figsize_ratio=1,
85
+ dpi=100,
86
+ sharex="col",
87
+ gridspec_kw={"width_ratios": [3, 1], "height_ratios": [1, 3]},
88
+ )
89
+ ax_hist_x = axes[0, 0]
90
+ ax_scatter = axes[1, 0]
91
+ ax_hist_y = axes[1, 1]
92
+ axes[0, 1].axis("off")
93
+
94
+ x_values, y_values = vectors
95
+ pair_mask = np.isfinite(x_values) & np.isfinite(y_values)
96
+ if hue is None:
97
+ category_masks = [(None, pair_mask)]
98
+ else:
99
+ category_masks = [
100
+ (category, pair_mask & (hue == category)) for category in categories
101
+ ]
102
+
103
+ x_finite = x_values[np.isfinite(x_values)]
104
+ y_finite = y_values[np.isfinite(y_values)]
105
+ if x_finite.size:
106
+ if bissectrice and global_scatter_limits is not None:
107
+ _low, _high = global_scatter_limits
108
+ x_bins = np.linspace(_low, _high, int(bins) + 1, dtype=np.float32)
109
+ else:
110
+ x_bins = np.histogram_bin_edges(x_finite, bins=bins)
111
+ for category, mask in category_masks:
112
+ x_mask = mask if hue is None else np.isfinite(x_values) & (hue == category)
113
+ x_cat = x_values[x_mask]
114
+ x_cat = x_cat[np.isfinite(x_cat)]
115
+ if x_cat.size:
116
+ ax_hist_x.hist(
117
+ x_cat,
118
+ bins=x_bins,
119
+ alpha=0.45,
120
+ edgecolor=color_map[category],
121
+ linewidth=0.8,
122
+ color=color_map[category],
123
+ )
124
+
125
+ if y_finite.size:
126
+ if bissectrice and global_scatter_limits is not None:
127
+ _low, _high = global_scatter_limits
128
+ y_bins = np.linspace(_low, _high, int(bins) + 1, dtype=np.float32)
129
+ else:
130
+ y_bins = np.histogram_bin_edges(y_finite, bins=bins)
131
+ for category, mask in category_masks:
132
+ y_mask = mask if hue is None else np.isfinite(y_values) & (hue == category)
133
+ y_cat = y_values[y_mask]
134
+ y_cat = y_cat[np.isfinite(y_cat)]
135
+ if y_cat.size:
136
+ ax_hist_y.hist(
137
+ y_cat,
138
+ bins=y_bins,
139
+ alpha=0.45,
140
+ edgecolor=color_map[category],
141
+ linewidth=0.8,
142
+ color=color_map[category],
143
+ orientation="horizontal",
144
+ )
145
+
146
+ rng = np.random.default_rng(0)
147
+ points_drawn = 0
148
+ for category, mask in category_masks:
149
+ indices = np.flatnonzero(mask)
150
+ if indices.size == 0:
151
+ continue
152
+ sample_size = min(npoints, indices.size)
153
+ sample_indices = rng.choice(indices, size=sample_size, replace=False)
154
+ points_drawn += sample_size
155
+ ax_scatter.scatter(
156
+ x_values[sample_indices],
157
+ y_values[sample_indices],
158
+ s=6,
159
+ alpha=0.35,
160
+ color=color_map[category],
161
+ )
162
+
163
+ if points_drawn == 0:
164
+ ax_scatter.text(0.5, 0.5, "No finite paired values", ha="center", va="center")
165
+
166
+ ax_scatter.set_xlabel(labels[0])
167
+ ax_scatter.set_ylabel(labels[1])
168
+ ax_scatter.polish_equiscale = True
169
+ ax_hist_x.set_ylabel("Counts")
170
+ ax_hist_y.set_xlabel("Counts")
171
+ ax_hist_x.tick_params(axis="x", labelbottom=False)
172
+ ax_hist_y.tick_params(axis="y", labelleft=False)
173
+ ax_hist_x.polish_grids = True
174
+ ax_hist_y.polish_grids = True
175
+
176
+ if bissectrice and np.any(pair_mask):
177
+ low, high = global_scatter_limits
178
+ ax_scatter.plot([low, high], [low, high], "k--", linewidth=1.2)
179
+ ax_scatter.set_xlim(low, high)
180
+ ax_scatter.set_ylim(low, high)
181
+ ax_hist_x.set_xlim(low, high)
182
+ ax_hist_y.set_ylim(low, high)
183
+ elif np.any(pair_mask):
184
+ x_pair = x_values[pair_mask]
185
+ y_pair = y_values[pair_mask]
186
+ x_low = float(np.nanpercentile(x_pair, q_low))
187
+ x_high = float(np.nanpercentile(x_pair, q_high))
188
+ y_low = float(np.nanpercentile(y_pair, q_low))
189
+ y_high = float(np.nanpercentile(y_pair, q_high))
190
+ if x_high <= x_low:
191
+ x_high = x_low + 1.0
192
+ if y_high <= y_low:
193
+ y_high = y_low + 1.0
194
+ ax_scatter.set_xlim(x_low, x_high)
195
+ ax_scatter.set_ylim(y_low, y_high)
196
+
197
+ if title is not None:
198
+ figure.title = title
199
+ return figure
200
+
201
+ figure, axes = subplots(
202
+ n,
203
+ n,
204
+ figsize_fact=(n / 2, n / 2),
205
+ figsize_ratio=1,
206
+ dpi=100,
207
+ sharex="col",
208
+ )
209
+ rng = np.random.default_rng(0)
210
+
211
+ for i in range(n):
212
+ for j in range(n):
213
+ axis = axes[i, j]
214
+ if i < j:
215
+ axis.axis("off")
216
+ continue
217
+
218
+ if i == j:
219
+ values = vectors[i]
220
+ finite = values[np.isfinite(values)]
221
+ axis.set_box_aspect(1)
222
+ if finite.size == 0:
223
+ axis.text(0.5, 0.5, "No finite values", ha="center", va="center")
224
+ else:
225
+ if bissectrice and global_scatter_limits is not None:
226
+ _low, _high = global_scatter_limits
227
+ edges = np.linspace(_low, _high, int(bins) + 1, dtype=np.float32)
228
+ else:
229
+ edges = np.histogram_bin_edges(finite, bins=bins)
230
+ for category in categories:
231
+ if hue is None:
232
+ category_values = finite
233
+ else:
234
+ category_mask = np.isfinite(values) & (hue == category)
235
+ category_values = values[category_mask]
236
+ if category_values.size:
237
+ axis.hist(
238
+ category_values,
239
+ bins=edges,
240
+ alpha=0.45,
241
+ edgecolor=color_map[category],
242
+ linewidth=0.8,
243
+ color=color_map[category],
244
+ )
245
+ axis.set_ylabel("Counts")
246
+ axis.polish_grids = True
247
+ if bissectrice and global_scatter_limits is not None:
248
+ _low, _high = global_scatter_limits
249
+ axis.set_xlim(_low, _high)
250
+ if i == n - 1:
251
+ axis.set_xlabel(labels[j])
252
+ else:
253
+ axis.set_xlabel("")
254
+ axis.tick_params(axis="x", labelbottom=False)
255
+ continue
256
+
257
+ x_values = vectors[j]
258
+ y_values = vectors[i]
259
+ pair_mask = np.isfinite(x_values) & np.isfinite(y_values)
260
+ if hue is None:
261
+ category_masks = [(None, pair_mask)]
262
+ else:
263
+ category_masks = [
264
+ (category, pair_mask & (hue == category)) for category in categories
265
+ ]
266
+
267
+ points_drawn = 0
268
+ for category, mask in category_masks:
269
+ indices = np.flatnonzero(mask)
270
+ if indices.size == 0:
271
+ continue
272
+ sample_size = min(npoints, indices.size)
273
+ sample_indices = rng.choice(indices, size=sample_size, replace=False)
274
+ points_drawn += sample_size
275
+ axis.scatter(
276
+ x_values[sample_indices],
277
+ y_values[sample_indices],
278
+ s=5,
279
+ alpha=0.35,
280
+ color=color_map[category],
281
+ )
282
+
283
+ if points_drawn == 0:
284
+ axis.text(0.5, 0.5, "No finite paired values", ha="center", va="center")
285
+
286
+ axis.polish_equiscale = True
287
+ if i == n - 1:
288
+ axis.set_xlabel(labels[j])
289
+ else:
290
+ axis.set_xlabel("")
291
+ axis.tick_params(axis="x", labelbottom=False)
292
+ if j == 0:
293
+ axis.set_ylabel(labels[i])
294
+ else:
295
+ axis.set_ylabel("")
296
+ axis.tick_params(axis="y", labelleft=False)
297
+
298
+ if bissectrice and np.any(pair_mask):
299
+ low, high = global_scatter_limits
300
+ axis.plot([low, high], [low, high], "k--", linewidth=1.0)
301
+ axis.set_xlim(low, high)
302
+ axis.set_ylim(low, high)
303
+ elif np.any(pair_mask):
304
+ x_pair = x_values[pair_mask]
305
+ y_pair = y_values[pair_mask]
306
+ x_low = float(np.nanpercentile(x_pair, q_low))
307
+ x_high = float(np.nanpercentile(x_pair, q_high))
308
+ y_low = float(np.nanpercentile(y_pair, q_low))
309
+ y_high = float(np.nanpercentile(y_pair, q_high))
310
+ if x_high <= x_low:
311
+ x_high = x_low + 1.0
312
+ if y_high <= y_low:
313
+ y_high = y_low + 1.0
314
+ axis.set_xlim(x_low, x_high)
315
+ axis.set_ylim(y_low, y_high)
316
+
317
+ if title is not None:
318
+ figure.title = title
319
+ return figure
320
+
321
+
322
+
323
+ # %% Test function run
324
+ if __name__ == "__main__":
325
+ from corelp import test
326
+ test(__file__)
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Date : 2026-05-28
4
+ # Author : Lancelot PINCET
5
+ # GitHub : https://github.com/LancelotPincet
6
+ # Library : plotLP
7
+ # Module : compare_distributions
8
+
9
+ """
10
+ This file allows to test compare_distributions
11
+
12
+ compare_distributions : This function create a figure which compares distributions via histograms and scatterplots.
13
+ """
14
+
15
+
16
+
17
+ # %% Libraries
18
+ import numpy as np
19
+ import pytest
20
+ from plotlp import compare_distributions
21
+
22
+
23
+
24
+ # %% Function test
25
+ def test_two_vectors_returns_figure():
26
+ """Two vectors should return a figure."""
27
+ x = np.arange(20, dtype=np.float32)
28
+ y = x + 1
29
+ figure = compare_distributions(x, y)
30
+ assert figure is not None
31
+
32
+
33
+ def test_multiple_vectors_returns_figure():
34
+ """Three vectors should return a pairwise-plot figure."""
35
+ x = np.arange(30, dtype=np.float32)
36
+ y = x * 2
37
+ z = x * 3
38
+ figure = compare_distributions(x, y, z, bissectrice=True)
39
+ assert figure is not None
40
+
41
+
42
+ def test_requires_at_least_two_vectors():
43
+ """One vector should raise an error."""
44
+ with pytest.raises(ValueError, match="at least two vectors"):
45
+ compare_distributions(np.arange(10))
46
+
47
+
48
+
49
+ # %% Test function run
50
+ if __name__ == "__main__":
51
+ from corelp import test
52
+ test(__file__)
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Author : Lancelot PINCET
4
+ # GitHub : https://github.com/LancelotPincet
5
+
6
+
7
+
8
+ # %% Libraries
9
+
10
+
11
+
12
+
13
+ # %% Function
14
+ def temp(**kwargs) :
15
+ '''
16
+ TODO
17
+ '''
18
+
19
+ print('Hello world!')
File without changes
File without changes
File without changes
File without changes