Coverage for uqmodels / visualization / aux_visualization.py: 72%
258 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-09 08:15 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-09 08:15 +0000
1import matplotlib.pyplot as plt
2import numpy as np
3from uqmodels.utils import compute_born, propagate, _merge_config, _merge_nested
5import matplotlib.dates as mdates
6from matplotlib.colors import LinearSegmentedColormap
9def aux_adjust_axes(ax, x, y_list, ylim=None, x_lim=None, margin=0.05, x_margin=0.5):
10 """
11 Adjust x/y axis limits based on data and optional explicit limits.
13 Parameters
14 ----------
15 ax : Axes
16 x : array-like
17 y_list : array-like or list of array-like
18 One or several y-series to consider for limits.
19 ylim : tuple or None
20 If not None, force (ymin, ymax).
21 x_lim : tuple or None
22 If not None, force (xmin, xmax).
23 margin : float
24 Relative margin applied to inferred y-limits (ignored if ylim is set).
25 x_margin : float
26 Margin added around min/max of x (ignored if x_lim is set).
27 """
28 x = np.asarray(x)
30 if not isinstance(y_list, (list, tuple)):
31 y_list = [y_list]
33 y_min = min(np.asarray(y).min() for y in y_list)
34 y_max = max(np.asarray(y).max() for y in y_list)
36 # Y limits
37 if ylim is None:
38 y_low = y_min - abs(y_min * margin)
39 y_high = y_max + abs(y_max * margin)
40 else:
41 y_low, y_high = ylim
43 ax.set_ylim(y_low, y_high)
45 # X limits
46 if x_lim is None:
47 x_low = x.min() - x_margin
48 x_high = x.max() + x_margin
49 else:
50 x_low, x_high = x_lim
52 ax.set_xlim(x_low, x_high)
55DEFAULT_PLOT_PRED_CONFIG = {
56 "truth_line": { # line for y (true / observed curve)
57 "ls": "dotted",
58 "color": "black",
59 "linewidth": 0.9,
60 "alpha": 1.0,
61 },
62 "pred_line": { # line for pred
63 "linestyle": "-", # alias to show it works even with different key
64 "color": "darkgreen",
65 "alpha": 1.0,
66 "linewidth": 0.7,
67 "zorder": -4,
68 "label": "Prediction",
69 },
70 "obs_scatter": { # scatter for observations as points
71 "c": "black",
72 "s": 10,
73 "marker": "x",
74 "linewidth": 1,
75 "label": "Observation",
76 },
77}
79DEFAULT_LINE_CONFIG = {
80 "color": "black",
81 "linestyle": "-",
82 "linewidth": 1.0,
83 "marker": None,
84 "markersize": None,
85 "label": None,
86 "zorder": None,
87}
90def aux_plot_line(ax, x, y, config=None):
91 """
92 Plot a line (or markers only) on an Axes with configurable style.
94 Parameters
95 ----------
96 ax : Axes
97 x, y : array-like
98 config : dict, optional
99 Keys: color, linestyle, linewidth, marker, markersize, label, zorder.
100 """
101 x = np.asarray(x)
102 y = np.asarray(y)
104 cfg = DEFAULT_LINE_CONFIG.copy()
105 if config is not None:
106 cfg.update({k: v for k, v in config.items() if v is not None})
108 # Filtrer les None pour ne pas polluer ax.plot
109 kwargs = {k: v for k, v in cfg.items() if v is not None}
110 return ax.plot(x, y, **kwargs)
113DEFAULT_PLOT_PRED_CONFIG = {
114 "truth_line": { # line for y (true / observed curve)
115 "color": "black",
116 "linestyle": "dotted",
117 "linewidth": 0.9,
118 "alpha": 1.0,
119 "zorder": -5,
120 "label": None,
121 },
122 "pred_line": { # line for pred
123 "color": "darkgreen",
124 "linestyle": "-",
125 "linewidth": 0.7,
126 "alpha": 1.0,
127 "zorder": -4,
128 "label": "Prediction",
129 },
130 "obs_scatter": { # observations as points
131 "color": "black",
132 "marker": "x",
133 "markersize": 4,
134 "linestyle": "none",
135 "linewidth": 1.0,
136 "alpha": 1.0,
137 "zorder": 10,
138 "label": "Observation",
139 },
140}
143def aux_plot_pred(ax, x, y, pred, config=None):
144 """
145 Plot observations and predictions on a given Axes with optional style config.
147 Parameters
148 ----------
149 ax : Axes
150 x, y, pred : array-like
151 Coordinates, observations, and predictions.
152 config : dict, optional
153 Style overrides for truth line, prediction line, and observation scatter.
154 Keys: "truth_line", "pred_line", "obs_scatter".
155 """
156 x = np.asarray(x)
157 y = np.asarray(y)
158 pred = np.asarray(pred)
160 cfg = _merge_config(DEFAULT_PLOT_PRED_CONFIG, config)
162 # Courbe "vérité terrain"
163 aux_plot_line(ax, x, y, config=cfg["truth_line"])
165 # Courbe de prédiction
166 aux_plot_line(ax, x, pred, config=cfg["pred_line"])
168 # Points d'observation
169 aux_plot_line(ax, x, y, config=cfg["obs_scatter"])
172DEFAULT_PLOT_ANOM_CONFIG = {
173 "anom_scatter": {
174 "linewidth": 1,
175 "marker": "x",
176 "c": "magenta",
177 "s": 25,
178 "label": '"Abnormal" real demand',
179 }
180}
182DEFAULT_PLOT_ANOM_CONFIG = {
183 "anom_scatter": {
184 "color": "magenta",
185 "marker": "x",
186 "markersize": 5,
187 "linestyle": "none",
188 "linewidth": 1.0,
189 "alpha": 1.0,
190 "zorder": 15,
191 "label": '"Abnormal" observation',
192 }
193}
196def aux_plot_anom(ax, x, y, config=None):
197 """
198 Plot anomalous observations on an Axes using aux_plot_line.
200 Parameters
201 ----------
202 ax : Axes
203 x, y : array-like
204 Coordinates and anomalous values.
205 config : dict, optional
206 Style overrides for anomalous points.
207 """
208 x = np.asarray(x)
209 y = np.asarray(y)
211 cfg = _merge_config(DEFAULT_PLOT_ANOM_CONFIG, config)
212 aux_plot_line(ax, x, y, config=cfg["anom_scatter"])
215DEFAULT_FILL_AREA_CONFIG = {
216 "color": None,
217 "alpha": 0.2,
218 "label": None,
219}
221DEFAULT_FILL_BETWEEN_CONFIG = {
222 "color": None,
223 "facecolor": None,
224 "alpha": 0.2,
225 "label": None,
226 "interpolate": False,
227 "zorder": None,
228}
231def aux_fill_between(ax, x, y1, y2, where=None, config=None):
232 """
233 Wrapper around ax.fill_between with configurable style.
235 Parameters
236 ----------
237 ax : Axes
238 x, y1, y2 : array-like
239 where : array-like or None
240 Boolean mask for conditional fill.
241 config : dict, optional
242 Style overrides (color, facecolor, alpha, label, interpolate, zorder).
243 """
244 x = np.asarray(x)
245 y1 = np.asarray(y1)
246 y2 = np.asarray(y2)
248 kwargs = DEFAULT_FILL_BETWEEN_CONFIG.copy()
249 if config is not None:
250 kwargs.update({k: v for k, v in config.items() if v is not None})
252 return ax.fill_between(
253 x,
254 y1,
255 y2,
256 where=where,
257 **kwargs,
258 )
261def aux_fill_area(ax, x, env_bot, env_top, config=None):
262 """
263 Fill an envelope between two curves on an Axes.
265 Parameters
266 ----------
267 ax : Axes
268 x, env_bot, env_top : array-like
269 Coordinates and envelope bounds.
270 config : dict, optional
271 Style overrides (color, alpha, label).
272 """
273 x = np.asarray(x)
274 env_bot = np.asarray(env_bot)
275 env_top = np.asarray(env_top)
277 final_cfg = DEFAULT_FILL_AREA_CONFIG.copy()
278 if config is not None:
279 final_cfg.update({k: v for k, v in config.items() if v is not None})
281 return ax.fill_between(
282 x,
283 env_bot,
284 env_top,
285 color=final_cfg["color"],
286 alpha=final_cfg["alpha"],
287 label=final_cfg["label"],
288 )
291DEFAULT_PI_PLOT_CONFIG = {
292 "line": { # style des lignes de bornes
293 "ls": "dotted",
294 "lw": 1.2,
295 "color": None,
296 },
297 "fill": { # style du remplissage
298 "color": None,
299 "alpha": 0.2,
300 },
301}
304def aux_plot_PIs(
305 ax,
306 x,
307 list_PIs,
308 list_alpha_PIs,
309 list_colors_PIs=["lightblue", "lightgreen"],
310 list_alpha_fig_PIs=[0.3, 0.15],
311 list_label_PIs=None,
312 config=None,
313):
314 """
315 Plot multiple prediction interval envelopes on an Axes.
317 Parameters
318 ----------
319 ax : Axes
320 x : array-like
321 list_PIs : list of array-like
322 [low_1, ..., low_k, high_k, ..., high_1].
323 list_alpha_PIs : list of float
324 Quantile levels for each bound.
325 list_colors_PIs : list, optional
326 Per-interval colors overriding config.
327 list_alpha_fig_PIs : list, optional
328 Per-interval alphas overriding config.
329 list_label_PIs : list, optional
330 Per-interval labels.
331 config : dict, optional
332 Global style config: {"line": {...}, "fill": {...}}.
333 """
334 n_bounds = len(list_PIs)
335 n_couple = n_bounds // 2
337 # Merge global config with defaults
338 line_cfg = DEFAULT_PI_PLOT_CONFIG["line"].copy()
339 fill_cfg = DEFAULT_PI_PLOT_CONFIG["fill"].copy()
340 if config is not None:
341 if "line" in config:
342 line_cfg.update(config["line"])
343 if "fill" in config:
344 fill_cfg.update(config["fill"])
346 # Defaults for per-interval overrides
347 if list_colors_PIs is None:
348 list_colors_PIs = [None] * n_couple
349 if list_alpha_fig_PIs is None:
350 list_alpha_fig_PIs = [fill_cfg.get("alpha", 0.2)] * n_couple
352 for i in range(n_couple):
353 low = list_PIs[i]
354 high = list_PIs[-(i + 1)]
356 color = list_colors_PIs[i] if list_colors_PIs[i] is not None else fill_cfg.get("color", None)
357 alpha = list_alpha_fig_PIs[i]
359 if list_label_PIs is None:
360 coverage = (list_alpha_PIs[-(i + 1)] - list_alpha_PIs[i]) * 100
361 label = f"Predictive interval: {coverage:.0f}%"
362 else:
363 label = list_label_PIs[i]
365 # Lignes de bornes
366 line_kwargs = line_cfg.copy()
367 if color is not None:
368 line_kwargs["color"] = color
369 ax.plot(x, low, **line_kwargs)
370 ax.plot(x, high, **line_kwargs)
372 # Remplissage via le helper
373 fill_kwargs = fill_cfg.copy()
374 if color is not None:
375 fill_kwargs["color"] = color
376 fill_kwargs["alpha"] = alpha
377 fill_kwargs["label"] = label
379 aux_fill_area(ax, x, low, high, config=fill_kwargs)
382DEFAULT_CONF_SCORE_CONFIG = {
383 "marker": "D",
384 "s": 14,
385 "edgecolors": "black",
386 "linewidth": 0.2,
387 "cmap": "RdYlGn_r",
388 "zorder_base": 10,
389}
392def aux_plot_conf_score(ax, x, pred, confidence_lvl, label, mode_res=False, config=None):
393 """
394 Plot confidence scores as colored markers on an Axes.
396 Parameters
397 ----------
398 ax : Axes
399 x, pred : array-like
400 Coordinates and predictions.
401 confidence_lvl : array-like
402 Discrete confidence levels (int).
403 label : list of str
404 Legend labels per confidence level.
405 mode_res : bool, default False
406 If True, plot residual scores around zero.
407 config : dict, optional
408 Style overrides (marker, s, edgecolors, linewidth, cmap, zorder_base).
409 """
410 x = np.asarray(x)
411 pred = np.asarray(pred)
412 confidence_lvl = np.asarray(confidence_lvl)
414 if mode_res:
415 pred = pred - pred
417 cfg = DEFAULT_CONF_SCORE_CONFIG.copy()
418 if config is not None:
419 cfg.update({k: v for k, v in config.items() if v is not None})
421 max_conf = int(confidence_lvl.max())
422 cmap = plt.get_cmap(cfg["cmap"], len(label) + 1)
424 for i in range(0, max_conf + 1):
425 mask = (confidence_lvl == i)
426 if not mask.any():
427 continue
429 ax.scatter(
430 x[mask],
431 pred[mask],
432 c=confidence_lvl[mask],
433 marker=cfg["marker"],
434 s=cfg["s"],
435 edgecolors=cfg["edgecolors"],
436 linewidth=cfg["linewidth"],
437 cmap=cmap,
438 vmin=0,
439 vmax=max_conf,
440 label=label[i],
441 zorder=cfg["zorder_base"] + i,
442 )
445DEFAULT_CONFIDENCE_PLOT_CONFIG = {
446 "pred": None, # config -> aux_plot_pred
447 "anom": None, # config -> aux_plot_anom
448 "pis_born": None, # config -> aux_plot_PIs (born)
449 "pis_aleatoric": None, # config -> aux_plot_PIs (aleatoric PI)
450 "pis_total": None, # config -> aux_plot_PIs (total PI)
451 "pis_born_bis": None, # config -> aux_plot_PIs (born_bis)
452 "anom_fill_upper": { # config -> aux_fill_between (born upper)
453 "facecolor": "red",
454 "alpha": 0.8,
455 "label": "Anomaly",
456 "interpolate": True,
457 "zorder": -10,
458 },
459 "anom_fill_lower": { # config -> aux_fill_between (born lower)
460 "facecolor": "red",
461 "alpha": 0.8,
462 "interpolate": True,
463 "zorder": -10,
464 },
465 "axes": { # config -> aux_adjust_axes
466 "margin": 0.05,
467 "x_margin": 0.5,
468 },
469}
472def aux_plot_confiance(
473 ax,
474 y,
475 pred,
476 var_A,
477 var_E,
478 born=None,
479 born_bis=None,
480 ylim=None,
481 split_values=-1,
482 x=None,
483 mode_res=False,
484 min_A=0.08,
485 min_E=0.02,
486 env=[0.95, 0.68],
487 config=None,
488 **kwarg,
489):
490 """
491 Plot prediction, uncertainty intervals and anomaly regions on an Axes.
492 """
493 y = np.asarray(y)
494 pred = np.asarray(pred)
495 var_A = np.asarray(var_A)
496 var_E = np.asarray(var_E)
498 if x is None:
499 x = np.arange(len(y))
500 else:
501 x = np.asarray(x)
503 # Config globale fusionnée
504 cfg = _merge_nested(DEFAULT_CONFIDENCE_PLOT_CONFIG, config or {})
506 # Mode résidus
507 if mode_res:
508 y = y - pred
509 if born is not None:
510 born = (born[0] - pred, born[1] - pred)
511 pred = pred * 0.0
513 # Indicateurs tronqués
514 ind_A = np.sqrt(var_A)
515 ind_E = np.sqrt(var_E)
516 ind_E[ind_E < min_E] = min_E
517 ind_A[ind_A < min_A] = min_A
519 # PIs (aleatoric et total)
520 y_lower, y_upper = compute_born(pred, np.sqrt(var_A + var_E * 0.0), 0.045)
521 y_lower_N, y_upper_N = compute_born(pred, np.sqrt(var_A + var_E), 0.045)
523 # Anomalies
524 if born is not None:
525 anom_mask = (y < born[0]) | (y > born[1])
526 else:
527 anom_score = np.abs(y - pred) / (2.0 * np.sqrt(ind_E**2 + ind_A**2))
528 anom_mask = anom_score > 1.0
530 # Prédiction + anomalies
531 aux_plot_pred(ax, x, y, pred, config=cfg["pred"])
532 aux_plot_anom(ax, x[anom_mask], y[anom_mask], config=cfg["anom"])
534 # Cas avec bornes explicites
535 if born is not None:
536 aux_plot_PIs(
537 ax,
538 x,
539 [born[0], born[1]],
540 list_alpha_PIs=[0.025, 0.975],
541 list_colors_PIs=["green"],
542 list_label_PIs=["Normal_limit"],
543 config=cfg["pis_born"],
544 )
546 aux_fill_between(
547 ax,
548 x,
549 born[1],
550 y,
551 where=propagate(y > born[1], 0, sym=True),
552 config=cfg["anom_fill_upper"],
553 )
555 aux_fill_between(
556 ax,
557 x,
558 born[0],
559 y,
560 where=propagate(y < born[0], 0),
561 config=cfg["anom_fill_lower"],
562 )
564 # Cas sans bornes explicites : PIs théoriques
565 else:
566 aux_plot_PIs(
567 ax,
568 x,
569 [y_lower, y_upper],
570 list_alpha_PIs=[0.025, 0.975],
571 list_colors_PIs=["green"],
572 list_label_PIs=["2σAleatoric PIs (95%)"],
573 list_alpha_fig_PIs=[0.2],
574 config=cfg["pis_aleatoric"],
575 )
577 aux_plot_PIs(
578 ax,
579 x,
580 [y_lower_N, y_upper_N],
581 list_alpha_PIs=[0.16, 0.84],
582 list_colors_PIs=["darkblue"],
583 list_alpha_fig_PIs=[0.1],
584 list_label_PIs=["2σTotal PIs(95%)"],
585 config=cfg["pis_total"],
586 )
588 # Bornes supplémentaires
589 if born_bis is not None:
590 aux_plot_PIs(
591 ax,
592 x,
593 [born_bis[0], born_bis[1]],
594 list_alpha_PIs=[0.025, 0.975],
595 list_colors_PIs=["teal"],
596 list_alpha_fig_PIs=[0.1],
597 list_label_PIs=["Normal_limit"],
598 config=cfg["pis_born_bis"],
599 )
601 # Ajustement axes
602 axes_cfg = cfg["axes"]
603 aux_adjust_axes(ax, x, [y, y_lower], ylim=ylim, margin=axes_cfg["margin"], x_margin=axes_cfg["x_margin"])
606# Auxiliar function related to matplot
608def aux_norm_score_inputs(score, f_obs=None, cmap=None):
609 """
610 Normalise score en liste, infère len_score, dim_score, n_score et f_obs.
611 """
612 if isinstance(score, list):
613 len_score = len(score[0])
614 dim_score = [score_.shape[-1] for score_ in score]
615 n_score = len(score)
616 score_list = score
617 else:
618 score_list = [score]
619 len_score = len(score)
620 dim_score = [score_list[0].shape[-1]]
621 n_score = 1
623 if f_obs is None:
624 f_obs = np.arange(len_score)
625 else:
626 f_obs = np.asarray(f_obs)
628 if cmap is None:
629 cmap = provide_cmap("bluetored")
631 return score_list, len_score, dim_score, n_score, f_obs, cmap
634def aux_prepare_x_extent(x, f_obs, dim_score):
635 """
636 Prépare x (échelle) et la liste d'extent pour imshow.
637 Retourne x, x_flag (datetime ou non), list_extent.
638 """
639 if x is None:
640 x_flag = False
641 x = np.arange(len(f_obs))
642 list_extent = [None for _ in dim_score]
643 else:
644 x_flag = True
645 x = np.asarray(x)
646 d0 = mdates.date2num(x[f_obs][0])
647 d1 = mdates.date2num(x[f_obs][-1])
648 list_extent = [[d0, d1, 0, dim] for dim in dim_score]
650 return x, x_flag, list_extent
653def aux_compute_layout_params(n_score, dim_score, true_label, score2, data, grid_spec=None):
654 """
655 Calcule n_fig, sharey, grid_spec.
656 """
657 n_fig = 3 + n_score
658 if true_label is None:
659 n_fig -= 1
660 if score2 is None:
661 n_fig -= 1
662 if data is None:
663 n_fig -= 1
665 sharey = False
666 if (
667 (n_score == 1)
668 and (true_label is not None)
669 and (dim_score[0] == true_label.shape)
670 ):
671 # Même condition que ton code initial (même si peu naturelle)
672 sharey = True
674 if grid_spec is None:
675 grid_spec = np.ones(n_fig)
677 return n_fig, sharey, grid_spec
680def aux_create_score_figure(n_fig, sharey, grid_spec, figsize):
681 """
682 Crée la figure et les axes pour la matrice d'anomalies.
683 """
684 fig, ax = plt.subplots(
685 n_fig,
686 1,
687 sharex=True,
688 sharey=sharey,
689 gridspec_kw={"height_ratios": grid_spec},
690 figsize=figsize,
691 )
692 return fig, ax
695def aux_plot_score_matrix(ax, score_mat, f_obs, extent, vmin, vmax, cmap, title="score"):
696 """
697 Affiche une matrice de score via imshow.
698 """
699 ax.set_title(title)
700 ax.imshow(
701 score_mat[f_obs].T[::-1],
702 cmap=cmap,
703 vmin=vmin,
704 vmax=vmax,
705 aspect="auto",
706 extent=extent,
707 interpolation=None,
708 )
711def aux_overlay_setup_grid(ax, setup, n_points):
712 """
713 Superpose la grille channels/sensors sur une matrice de score.
714 setup = (n_chan, n_sensor)
715 """
716 if setup is None:
717 return
718 n_chan, n_sensor = setup
719 for i in range(n_chan * n_sensor):
720 ax.hlines(i, 0, n_points, color="grey", lw=0.5)
721 for i in range(n_sensor):
722 ax.hlines(i * n_chan, 0, n_points, color="black", lw=1)
725def aux_plot_true_label_matrix(ax, true_label, f_obs, extent):
726 """
727 Affiche la matrice de labels vrais.
728 """
729 ax.set_title("True_anom")
730 ax.imshow(
731 true_label[f_obs].T[::-1],
732 cmap="Reds",
733 aspect="auto",
734 extent=extent,
735 interpolation=None,
736 )
739def aux_build_data_colors(data, list_anom_ind=None):
740 """
741 Construit la palette de couleurs par canal, en surlignant éventuellement
742 certains indices anormaux.
743 """
744 n_chan = data.shape[1]
745 base_cmap = plt.get_cmap("Greens", n_chan)
746 colors = [base_cmap(i) for i in range(n_chan)]
748 if list_anom_ind is not None:
749 red_cmap = plt.get_cmap("Reds", len(list_anom_ind) + 4)
750 for n, anom_ind in enumerate(list_anom_ind):
751 colors[anom_ind] = red_cmap(n + 4)
753 return colors
756def aux_plot_data_timeseries(ax, x, data, f_obs, dim, colors, lw=0.9):
757 """
758 Trace les séries temporelles multicanal.
759 """
760 for i in range(dim):
761 ax.plot(x[f_obs], data[f_obs, i], color=colors[i], lw=lw)
764def aux_overlay_score_anoms_on_data(ax, x, data, score, f_obs, dim, threshold=1.0):
765 """
766 Superpose les points où |score| > threshold sur les séries de données.
767 """
768 for i in range(dim):
769 mask = np.abs(score)[f_obs, i] > threshold
770 ax.scatter(
771 x[f_obs][mask],
772 data[f_obs, i][mask],
773 color="red",
774 marker="x",
775 s=1,
776 zorder=10,
777 )
780def aux_overlay_true_label_on_data(ax, x, data, true_label, f_obs, color="purple"):
781 """
782 Superpose les labels vrais sur les séries temporelles.
783 """
784 for i in range(data.shape[1]):
785 mask = true_label[f_obs, i] > 0
786 ax.scatter(x[f_obs][mask], data[f_obs, i][mask], color=color)
789def aux_format_time_axis(ax, x_flag, x_date):
790 """
791 Configure l'axe des x comme temporel et éventuellement applique un formatter.
792 """
793 if x_flag:
794 ax.xaxis_date()
795 if x_date:
796 ax.xaxis.set_major_formatter(mdates.DateFormatter("%d/%m %H:%M"))
799def aux_finalize_figure(fig, show_plot=True):
800 """
801 Finalise la figure (tight_layout + show optionnel).
802 """
803 fig.tight_layout()
804 if show_plot:
805 plt.show()
808def provide_cmap(mode="bluetored"):
809 """Generate a bluetored or a cyantopurple cutsom cmap
811 Args:
812 mode (str, optional):Values: bluetored' or 'cyantopurple '
814 return:
815 Colormap matplotlib
816 """
817 if mode == "bluetored":
818 bluetored = [
819 [0.0, (0, 0, 90)],
820 [0.05, (5, 5, 120)],
821 [0.1, (20, 20, 150)],
822 [0.15, (20, 20, 190)],
823 [0.2, (40, 40, 220)],
824 [0.25, (70, 70, 255)],
825 [0.33, (100, 100, 255)],
826 [0.36, (180, 180, 255)],
827 [0.4, (218, 218, 255)],
828 [0.45, (245, 245, 255)],
829 [0.5, (255, 253, 253)],
830 [0.55, (255, 245, 245)],
831 [0.6, (255, 218, 218)],
832 [0.63, (255, 200, 200)],
833 [0.66, (255, 160, 160)],
834 [0.7, (255, 110, 110)],
835 [0.75, (255, 70, 70)],
836 [0.8, (230, 40, 40)],
837 [0.85, (200, 20, 20)],
838 [0.9, (180, 10, 10)],
839 [0.95, (150, 5, 5)],
840 [1.0, (130, 0, 0)],
841 ]
842 bluetored_cmap = LinearSegmentedColormap.from_list(
843 "bluetored", [np.array(i[1]) / 255 for i in bluetored], N=255
844 )
845 return bluetored_cmap
846 elif mode == "cyantopurple":
847 cyantopurple = [
848 [0.0, (25, 255, 255)],
849 [0.05, (20, 250, 250)],
850 [0.1, (20, 230, 230)],
851 [0.15, (20, 220, 220)],
852 [0.2, (15, 200, 200)],
853 [0.25, (10, 170, 170)],
854 [0.3, (10, 140, 140)],
855 [0.36, (5, 80, 80)],
856 [0.4, (5, 50, 50)],
857 [0.45, (0, 30, 30)],
858 [0.5, (0, 0, 0)],
859 [0.55, (30, 0, 30)],
860 [0.6, (59, 0, 50)],
861 [0.64, (80, 0, 80)],
862 [0.7, (140, 0, 140)],
863 [0.75, (170, 0, 170)],
864 [0.8, (200, 40, 200)],
865 [0.85, (220, 20, 220)],
866 [0.9, (240, 10, 240)],
867 [0.95, (250, 5, 250)],
868 [1.0, (255, 0, 255)],
869 ]
870 else:
871 raise NameError
873 cyantopurple_cmap = LinearSegmentedColormap.from_list(
874 "cyantopurple", [np.array(i[1]) / 255 for i in cyantopurple], N=255
875 )
876 return cyantopurple_cmap
879def _get_panel_ax(axs, n_dim, n_ctx, idx_dim, idx_ctx):
880 """Sélectionne l'Axes correct dans la grille axs (len(dim) x n_ctx)."""
881 if n_dim == 1 and n_ctx == 1:
882 return axs
883 if n_dim == 1:
884 return axs[idx_ctx]
885 if n_ctx == 1:
886 return axs[idx_dim]
887 return axs[idx_dim, idx_ctx]