plotlp 0.1.17__tar.gz → 0.1.19__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.
- {plotlp-0.1.17 → plotlp-0.1.19}/PKG-INFO +1 -1
- {plotlp-0.1.17 → plotlp-0.1.19}/pyproject.toml +1 -1
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/__init__.py +1 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledAxes_LP/StyledAxes.py +24 -1
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledFigure_LP/StyledFigure.py +32 -0
- plotlp-0.1.19/src/plotlp/modules/compare_distributions_LP/compare_distributions.py +326 -0
- plotlp-0.1.19/src/plotlp/modules/compare_distributions_LP/test_compare_distributions.py +52 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/figure_LP/test_figure.py +6 -4
- plotlp-0.1.19/src/plotlp/modules/subplots_LP/_doctype.txt +1 -0
- plotlp-0.1.19/src/plotlp/modules/subplots_LP/_functions/__template__.py +19 -0
- plotlp-0.1.19/src/plotlp/py.typed +0 -0
- plotlp-0.1.19/src/plotlp/routines/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/README.md +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/.docignore +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledAxes_LP/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledAxes_LP/_doctype.txt +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledAxes_LP/_functions/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledAxes_LP/_functions/__template__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledAxes_LP/_functions/scalebar.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledAxes_LP/test_StyledAxes.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledFigure_LP/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledFigure_LP/_doctype.txt +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledFigure_LP/_functions/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledFigure_LP/_functions/__template__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledFigure_LP/test_StyledFigure.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/cmap_LP/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/cmap_LP/_doctype.txt +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/cmap_LP/_functions/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/cmap_LP/_functions/__template__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/cmap_LP/cmap.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/cmap_LP/test_cmap.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/color_LP/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/color_LP/_doctype.txt +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/color_LP/_functions/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/color_LP/_functions/__template__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/color_LP/color.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/color_LP/test_color.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/figure_LP → plotlp-0.1.19/src/plotlp/modules/compare_distributions_LP}/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/figure_LP → plotlp-0.1.19/src/plotlp/modules/compare_distributions_LP}/_doctype.txt +0 -0
- {plotlp-0.1.17/src/plotlp/modules/figure_LP → plotlp-0.1.19/src/plotlp/modules/compare_distributions_LP}/_functions/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/figure_LP → plotlp-0.1.19/src/plotlp/modules/compare_distributions_LP}/_functions/__template__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/gridsubplots_LP → plotlp-0.1.19/src/plotlp/modules/figure_LP}/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/gridsubplots_LP → plotlp-0.1.19/src/plotlp/modules/figure_LP}/_doctype.txt +0 -0
- {plotlp-0.1.17/src/plotlp/modules/gridsubplots_LP → plotlp-0.1.19/src/plotlp/modules/figure_LP}/_functions/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/gridsubplots_LP → plotlp-0.1.19/src/plotlp/modules/figure_LP}/_functions/__template__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/figure_LP/figure.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/imgfigure_LP → plotlp-0.1.19/src/plotlp/modules/gridsubplots_LP}/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/imgfigure_LP → plotlp-0.1.19/src/plotlp/modules/gridsubplots_LP}/_doctype.txt +0 -0
- {plotlp-0.1.17/src/plotlp/modules/imgfigure_LP → plotlp-0.1.19/src/plotlp/modules/gridsubplots_LP}/_functions/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/imgfigure_LP → plotlp-0.1.19/src/plotlp/modules/gridsubplots_LP}/_functions/__template__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/gridsubplots_LP/gridsubplots.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/gridsubplots_LP/test_gridsubplots.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/make_animation_LP → plotlp-0.1.19/src/plotlp/modules/imgfigure_LP}/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/make_animation_LP → plotlp-0.1.19/src/plotlp/modules/imgfigure_LP}/_doctype.txt +0 -0
- {plotlp-0.1.17/src/plotlp/modules/make_animation_LP → plotlp-0.1.19/src/plotlp/modules/imgfigure_LP}/_functions/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/make_animation_LP → plotlp-0.1.19/src/plotlp/modules/imgfigure_LP}/_functions/__template__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/imgfigure_LP/imgfigure.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/imgfigure_LP/test_imgfigure.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/plt_LP → plotlp-0.1.19/src/plotlp/modules/make_animation_LP}/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/plt_LP → plotlp-0.1.19/src/plotlp/modules/make_animation_LP}/_doctype.txt +0 -0
- {plotlp-0.1.17/src/plotlp/modules/plt_LP → plotlp-0.1.19/src/plotlp/modules/make_animation_LP}/_functions/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/plt_LP → plotlp-0.1.19/src/plotlp/modules/make_animation_LP}/_functions/__template__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/make_animation_LP/make_animation.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/make_animation_LP/test_make_animation.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/style_LP → plotlp-0.1.19/src/plotlp/modules/plt_LP}/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/style_LP → plotlp-0.1.19/src/plotlp/modules/plt_LP}/_doctype.txt +0 -0
- {plotlp-0.1.17/src/plotlp/modules/style_LP → plotlp-0.1.19/src/plotlp/modules/plt_LP}/_functions/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/style_LP → plotlp-0.1.19/src/plotlp/modules/plt_LP}/_functions/__template__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/plt_LP/plt.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/plt_LP/test_plt.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/subplots_LP → plotlp-0.1.19/src/plotlp/modules/style_LP}/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/subplots_LP → plotlp-0.1.19/src/plotlp/modules/style_LP}/_doctype.txt +0 -0
- {plotlp-0.1.17/src/plotlp/modules/subplots_LP → plotlp-0.1.19/src/plotlp/modules/style_LP}/_functions/__init__.py +0 -0
- {plotlp-0.1.17/src/plotlp/modules/subplots_LP → plotlp-0.1.19/src/plotlp/modules/style_LP}/_functions/__template__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/style_LP/_functions/_darkLP.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/style_LP/_functions/_default.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/style_LP/_functions/_lightLP.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/style_LP/_functions/_styleLP.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/style_LP/style.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/style_LP/test_style.py +0 -0
- {plotlp-0.1.17/src/plotlp/routines → plotlp-0.1.19/src/plotlp/modules/subplots_LP}/__init__.py +0 -0
- /plotlp-0.1.17/src/plotlp/py.typed → /plotlp-0.1.19/src/plotlp/modules/subplots_LP/_functions/__init__.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/subplots_LP/subplots.py +0 -0
- {plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/subplots_LP/test_subplots.py +0 -0
|
@@ -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',
|
|
@@ -133,6 +133,7 @@ class StyledAxes(Axes) :
|
|
|
133
133
|
if self.polish_imscale : self.imscale()
|
|
134
134
|
if self.polish_noborders : self.noborders()
|
|
135
135
|
if self.polish_equiscale : self.equiscale()
|
|
136
|
+
if self.polish_figscale : self.figscale()
|
|
136
137
|
|
|
137
138
|
# grids
|
|
138
139
|
@prop()
|
|
@@ -179,6 +180,28 @@ class StyledAxes(Axes) :
|
|
|
179
180
|
with plt.style.context(self.style) :
|
|
180
181
|
self.set_aspect(aspect='equal', adjustable='box')
|
|
181
182
|
|
|
183
|
+
# figscale
|
|
184
|
+
@prop()
|
|
185
|
+
def polish_figscale(self) :
|
|
186
|
+
return self.figure.naxes == 1 and not self.polish_imscale and self.polish_equiscale
|
|
187
|
+
def figscale(self) :
|
|
188
|
+
with plt.style.context(self.style) :
|
|
189
|
+
ratio = self.figure.figsize_ratio
|
|
190
|
+
xmin, xmax = self.get_xlim()
|
|
191
|
+
ymin, ymax = self.get_ylim()
|
|
192
|
+
dx, dy = xmax - xmin, ymax - ymin
|
|
193
|
+
x, y = (xmax + xmin) / 2, (ymax + ymin) / 2
|
|
194
|
+
if dx / dy != ratio :
|
|
195
|
+
_dx, _dy = ratio * dy, dx / ratio
|
|
196
|
+
dx = _dx if _dx > dx else dx
|
|
197
|
+
dy = _dy if _dy > dy else dy
|
|
198
|
+
xmin, xmax = x - dx/2, x + dx/2
|
|
199
|
+
ymin, ymax = y - dy/2, y + dy/2
|
|
200
|
+
if self.get_autoscalex_on() :
|
|
201
|
+
self.set_xlim(xmin, xmax)
|
|
202
|
+
if self.get_autoscaley_on() :
|
|
203
|
+
self.set_ylim(ymin, ymax)
|
|
204
|
+
|
|
182
205
|
|
|
183
206
|
|
|
184
207
|
### --- Regenerate parent class methods in the given style ---
|
|
@@ -189,7 +212,7 @@ def is_plottable(method_name, method_obj):
|
|
|
189
212
|
"""
|
|
190
213
|
Returns True if this method should be wrapped automatically.
|
|
191
214
|
"""
|
|
192
|
-
if
|
|
215
|
+
if method_name in StyledAxes.__dict__ : # Already overriden
|
|
193
216
|
return False
|
|
194
217
|
if method_name.startswith("_"): # private
|
|
195
218
|
return False # handled separately
|
|
@@ -156,10 +156,15 @@ class StyledFigure(Figure) :
|
|
|
156
156
|
|
|
157
157
|
|
|
158
158
|
|
|
159
|
+
# Merging and splitting
|
|
159
160
|
@property
|
|
160
161
|
def merge(self):
|
|
161
162
|
return _MergeIndexer(self)
|
|
162
163
|
|
|
164
|
+
@property
|
|
165
|
+
def split(self):
|
|
166
|
+
return _SplitIndexer(self)
|
|
167
|
+
|
|
163
168
|
def _normalize_indices(self, idx, size):
|
|
164
169
|
if isinstance(idx, slice):
|
|
165
170
|
return list(range(*idx.indices(size)))
|
|
@@ -198,6 +203,17 @@ class StyledFigure(Figure) :
|
|
|
198
203
|
|
|
199
204
|
return new_ax
|
|
200
205
|
|
|
206
|
+
def _split_axis(self, row, col, height, width):
|
|
207
|
+
|
|
208
|
+
if not hasattr(self, "_axes_grid"):
|
|
209
|
+
raise RuntimeError("merge only works with fig.subplots()")
|
|
210
|
+
|
|
211
|
+
grid = self._axes_grid
|
|
212
|
+
axes_to_merge = [grid[i, j] for i in row for j in col]
|
|
213
|
+
|
|
214
|
+
# TODO
|
|
215
|
+
|
|
216
|
+
return None
|
|
201
217
|
|
|
202
218
|
|
|
203
219
|
# Paper index
|
|
@@ -351,6 +367,22 @@ class _MergeIndexer:
|
|
|
351
367
|
|
|
352
368
|
|
|
353
369
|
|
|
370
|
+
class _SplitIndexer:
|
|
371
|
+
|
|
372
|
+
def __init__(self, fig):
|
|
373
|
+
self.fig = fig
|
|
374
|
+
|
|
375
|
+
def __getitem__(self, key):
|
|
376
|
+
|
|
377
|
+
if not isinstance(key, tuple):
|
|
378
|
+
raise KeyError(f'Splitting figure axis should only be done with two coordinates, not {key}')
|
|
379
|
+
|
|
380
|
+
r, c, h, w = key
|
|
381
|
+
|
|
382
|
+
return self.fig._split_axis(r, c, h, w)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
|
|
354
386
|
# %% Test function run
|
|
355
387
|
if __name__ == "__main__":
|
|
356
388
|
from corelp import test
|
|
@@ -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__)
|
|
@@ -31,12 +31,14 @@ def test_plot() :
|
|
|
31
31
|
y = x**2
|
|
32
32
|
fig = figure(figsize_fact=(0.5,0.5), darkmode=True)
|
|
33
33
|
fig.axis.plot(x, -y)
|
|
34
|
+
fig.axis.scatter(x, -y)
|
|
34
35
|
plt.plot(x, y)
|
|
36
|
+
plt.scatter(x, y)
|
|
35
37
|
plt.xlabel('x [ua]')
|
|
36
38
|
fig.axis.set_ylabel('y [ua]')
|
|
37
39
|
fig.title = 'test title'
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
fig.savefig(path_png = debug_folder / 'plot_png', close=False)
|
|
41
|
+
fig.savefig(path_pdf = debug_folder / 'plot_pdf', close=False)
|
|
40
42
|
plt.savefig(debug_folder / 'plot_all')
|
|
41
43
|
|
|
42
44
|
def test_imshow() :
|
|
@@ -50,8 +52,8 @@ def test_imshow() :
|
|
|
50
52
|
fig = figure(figsize_ratio=4/3)
|
|
51
53
|
fig.axis.imshow(img, barname='intensity')
|
|
52
54
|
fig.title = 'test title'
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
fig.savefig(path_png = debug_folder / 'imshow_png', close=False)
|
|
56
|
+
fig.savefig(path_pdf = debug_folder / 'imshow_pdf', close=False)
|
|
55
57
|
plt.savefig(debug_folder / 'imshow_all')
|
|
56
58
|
|
|
57
59
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
default
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plotlp-0.1.17 → plotlp-0.1.19}/src/plotlp/modules/StyledFigure_LP/_functions/__template__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plotlp-0.1.17/src/plotlp/modules/style_LP → plotlp-0.1.19/src/plotlp/modules/plt_LP}/__init__.py
RENAMED
|
File without changes
|
{plotlp-0.1.17/src/plotlp/modules/style_LP → plotlp-0.1.19/src/plotlp/modules/plt_LP}/_doctype.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plotlp-0.1.17/src/plotlp/routines → plotlp-0.1.19/src/plotlp/modules/subplots_LP}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|