From 42c629aa1de96ca8c6321027817e71c45c63a1bd Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Wed, 16 Apr 2025 19:40:37 +0200 Subject: [PATCH 1/5] add ensemble parameters plot --- pypesto/visualize/__init__.py | 2 +- pypesto/visualize/ensemble.py | 51 ++++++++++++++++++++++++++++++++ test/visualize/test_visualize.py | 15 ++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/pypesto/visualize/__init__.py b/pypesto/visualize/__init__.py index 5a1b12bef..17f624071 100644 --- a/pypesto/visualize/__init__.py +++ b/pypesto/visualize/__init__.py @@ -17,7 +17,7 @@ projection_scatter_umap, projection_scatter_umap_original, ) -from .ensemble import ensemble_identifiability +from .ensemble import ensemble_identifiability, ensemble_parameters_plot from .misc import process_offset_y, process_result_list, process_y_limits from .observable_mapping import ( plot_linear_observable_mappings_from_pypesto_result, diff --git a/pypesto/visualize/ensemble.py b/pypesto/visualize/ensemble.py index 413341223..402c95f70 100644 --- a/pypesto/visualize/ensemble.py +++ b/pypesto/visualize/ensemble.py @@ -413,3 +413,54 @@ def _create_patches( ) return patches_both_hit, patches_lb_hit, patches_ub_hit, patches_none_hit + + +def ensemble_parameters_plot( + ensemble: Ensemble, + ax: Optional[plt.Axes] = None, + size: Optional[tuple[float]] = (12, 6) +): + """ + Visualize parameter ensemble. + + Parameters + ---------- + ensemble: + ensemble of parameter vectors (from pypesto.ensemble) + ax: + Axes object to use. + size: + Figure size (width, height) in inches. Is only applied when no ax + object is specified. + + Returns + ------- + ax: matplotlib.Axes + The plot axes. + """ + import seaborn as sns + + if ax is None: + fig, ax = plt.subplots(figsize=size) + + x = -0.4 + w = 0.8 # rectangle width + rectangles = [] + colors = [c + (0.8,) for c in sns.color_palette("husl", n_colors=ensemble.n_x)] + + sns.stripplot(np.transpose(ensemble.x_vectors), color='dimgrey') + + for i, par_values in enumerate(ensemble.x_vectors): + h = np.max(par_values) - np.min(par_values) # rectangle hight + rectangles.append( + Rectangle((x, np.min(par_values)), w, h)) + x += w + 0.2 + ax.add_collection(PatchCollection(rectangles, facecolors=colors, edgecolors='dimgrey')) + + ax.plot(np.arange(ensemble.n_x), ensemble.lower_bound, '--', color='grey') + ax.plot(np.arange(ensemble.n_x), ensemble.upper_bound, '--', color='grey') + ax.set_ylim(np.min(ensemble.lower_bound) * 1.1, np.max(ensemble.upper_bound) * 1.1) + plt.xticks(np.arange(ensemble.n_x), ensemble.x_names, rotation='vertical') + plt.tight_layout() + + return ax diff --git a/test/visualize/test_visualize.py b/test/visualize/test_visualize.py index 7512b344c..574ea441e 100644 --- a/test/visualize/test_visualize.py +++ b/test/visualize/test_visualize.py @@ -636,6 +636,21 @@ def test_ensemble_identifiability(): # test plotting from a collection object visualize.ensemble_identifiability(my_ensemble) +@close_fig +def test_ensemble_parameters_plot(): + # creates a test problem + problem = create_problem(n_parameters=100) + + my_ensemble = [ + (1 + np.cos(ix) ** 2) * np.random.rand(500) - 1.0 + np.sin(ix) + for ix in range(100) + ] + my_ensemble = ensemble.Ensemble( + np.array(my_ensemble), lower_bound=problem.lb, upper_bound=problem.ub + ) + + visualize.ensemble_parameters_plot(my_ensemble) + @close_fig def test_profiles(): From 61ace8b0ab2ca4b5863deb1672d53ed2985861e7 Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Tue, 6 May 2025 14:03:42 +0200 Subject: [PATCH 2/5] change colors --- pypesto/visualize/ensemble.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pypesto/visualize/ensemble.py b/pypesto/visualize/ensemble.py index 402c95f70..7f5ce3ad7 100644 --- a/pypesto/visualize/ensemble.py +++ b/pypesto/visualize/ensemble.py @@ -3,6 +3,7 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd +from matplotlib import colormaps from matplotlib.collections import PatchCollection from matplotlib.patches import Rectangle @@ -438,7 +439,6 @@ def ensemble_parameters_plot( ax: matplotlib.Axes The plot axes. """ - import seaborn as sns if ax is None: fig, ax = plt.subplots(figsize=size) @@ -446,16 +446,22 @@ def ensemble_parameters_plot( x = -0.4 w = 0.8 # rectangle width rectangles = [] - colors = [c + (0.8,) for c in sns.color_palette("husl", n_colors=ensemble.n_x)] - - sns.stripplot(np.transpose(ensemble.x_vectors), color='dimgrey') + cmap = colormaps['Greys'] + colors = np.flip(cmap(np.linspace(0.3, 0.8, (ensemble.n_vectors-1))), axis=0) + colors = np.insert(colors, 0, [1. , 0. , 0. , 1. ], axis=0) for i, par_values in enumerate(ensemble.x_vectors): - h = np.max(par_values) - np.min(par_values) # rectangle hight + h = np.max(par_values) - np.min(par_values) # rectangle height rectangles.append( Rectangle((x, np.min(par_values)), w, h)) x += w + 0.2 - ax.add_collection(PatchCollection(rectangles, facecolors=colors, edgecolors='dimgrey')) + ax.add_collection(PatchCollection(rectangles, facecolors=[1, 1, 1, 1], edgecolors='dimgrey')) + + for i, v in enumerate(ensemble.x_vectors): + ax.scatter(x=[i]*ensemble.n_vectors, y=v, s=40, color=colors, alpha=0.6) + # plot the best parameter values + ax.scatter(np.arange(ensemble.n_x), ensemble.x_vectors[:, 0], s=40, + color=[1. , 0. , 0. , 1. ]) ax.plot(np.arange(ensemble.n_x), ensemble.lower_bound, '--', color='grey') ax.plot(np.arange(ensemble.n_x), ensemble.upper_bound, '--', color='grey') From da99c3b432d19cf153afb45d39946991a0d2bd1e Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Tue, 6 May 2025 14:53:21 +0200 Subject: [PATCH 3/5] add possibility to plot a subset of parameters --- pypesto/visualize/ensemble.py | 25 ++++++++++++++++++------- test/visualize/test_visualize.py | 1 + 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/pypesto/visualize/ensemble.py b/pypesto/visualize/ensemble.py index 7f5ce3ad7..0fb2315ed 100644 --- a/pypesto/visualize/ensemble.py +++ b/pypesto/visualize/ensemble.py @@ -419,6 +419,7 @@ def _create_patches( def ensemble_parameters_plot( ensemble: Ensemble, ax: Optional[plt.Axes] = None, + parameter_ids: Optional[list[int]] = None, size: Optional[tuple[float]] = (12, 6) ): """ @@ -427,9 +428,11 @@ def ensemble_parameters_plot( Parameters ---------- ensemble: - ensemble of parameter vectors (from pypesto.ensemble) + ensemble of parameter vectors (from pypesto.ensemble). ax: Axes object to use. + parameter_ids: + Indices of parameters to plot. size: Figure size (width, height) in inches. Is only applied when no ax object is specified. @@ -443,6 +446,14 @@ def ensemble_parameters_plot( if ax is None: fig, ax = plt.subplots(figsize=size) + if parameter_ids: + x_vectors = ensemble.x_vectors[parameter_ids] + n_x = len(parameter_ids) + else: + parameter_ids = np.arange(ensemble.n_x) + x_vectors = ensemble.x_vectors + n_x = ensemble.n_x + x = -0.4 w = 0.8 # rectangle width rectangles = [] @@ -450,23 +461,23 @@ def ensemble_parameters_plot( colors = np.flip(cmap(np.linspace(0.3, 0.8, (ensemble.n_vectors-1))), axis=0) colors = np.insert(colors, 0, [1. , 0. , 0. , 1. ], axis=0) - for i, par_values in enumerate(ensemble.x_vectors): + for i, par_values in enumerate(x_vectors): h = np.max(par_values) - np.min(par_values) # rectangle height rectangles.append( Rectangle((x, np.min(par_values)), w, h)) x += w + 0.2 ax.add_collection(PatchCollection(rectangles, facecolors=[1, 1, 1, 1], edgecolors='dimgrey')) - for i, v in enumerate(ensemble.x_vectors): + for i, v in enumerate(x_vectors): ax.scatter(x=[i]*ensemble.n_vectors, y=v, s=40, color=colors, alpha=0.6) # plot the best parameter values - ax.scatter(np.arange(ensemble.n_x), ensemble.x_vectors[:, 0], s=40, + ax.scatter(np.arange(n_x), x_vectors[:, 0], s=40, color=[1. , 0. , 0. , 1. ]) - ax.plot(np.arange(ensemble.n_x), ensemble.lower_bound, '--', color='grey') - ax.plot(np.arange(ensemble.n_x), ensemble.upper_bound, '--', color='grey') + ax.plot(np.arange(n_x), ensemble.lower_bound[parameter_ids], '--', color='grey') + ax.plot(np.arange(n_x), ensemble.upper_bound[parameter_ids], '--', color='grey') ax.set_ylim(np.min(ensemble.lower_bound) * 1.1, np.max(ensemble.upper_bound) * 1.1) - plt.xticks(np.arange(ensemble.n_x), ensemble.x_names, rotation='vertical') + plt.xticks(np.arange(n_x), np.asarray(ensemble.x_names)[parameter_ids], rotation='vertical') plt.tight_layout() return ax diff --git a/test/visualize/test_visualize.py b/test/visualize/test_visualize.py index 574ea441e..800e8c25c 100644 --- a/test/visualize/test_visualize.py +++ b/test/visualize/test_visualize.py @@ -650,6 +650,7 @@ def test_ensemble_parameters_plot(): ) visualize.ensemble_parameters_plot(my_ensemble) + visualize.ensemble_parameters_plot(my_ensemble, parameter_ids=[0,5,8,13,17,33,45,76,82,88,90]) @close_fig From a4d9dd99dddb3dcac7b68d8fb70f33f0eb400816 Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Tue, 6 May 2025 16:55:23 +0200 Subject: [PATCH 4/5] change to vertical --- pypesto/visualize/ensemble.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pypesto/visualize/ensemble.py b/pypesto/visualize/ensemble.py index 0fb2315ed..b0e67e753 100644 --- a/pypesto/visualize/ensemble.py +++ b/pypesto/visualize/ensemble.py @@ -420,7 +420,7 @@ def ensemble_parameters_plot( ensemble: Ensemble, ax: Optional[plt.Axes] = None, parameter_ids: Optional[list[int]] = None, - size: Optional[tuple[float]] = (12, 6) + size: Optional[tuple[float]] = (6, 12) ): """ Visualize parameter ensemble. @@ -454,30 +454,30 @@ def ensemble_parameters_plot( x_vectors = ensemble.x_vectors n_x = ensemble.n_x - x = -0.4 - w = 0.8 # rectangle width + y_rect = -0.4 + h_rect = 0.8 # rectangle height rectangles = [] cmap = colormaps['Greys'] colors = np.flip(cmap(np.linspace(0.3, 0.8, (ensemble.n_vectors-1))), axis=0) colors = np.insert(colors, 0, [1. , 0. , 0. , 1. ], axis=0) for i, par_values in enumerate(x_vectors): - h = np.max(par_values) - np.min(par_values) # rectangle height + w_rect = np.max(par_values) - np.min(par_values) # rectangle width rectangles.append( - Rectangle((x, np.min(par_values)), w, h)) - x += w + 0.2 + Rectangle((np.min(par_values), y_rect), w_rect, h_rect)) + y_rect += h_rect + 0.2 ax.add_collection(PatchCollection(rectangles, facecolors=[1, 1, 1, 1], edgecolors='dimgrey')) for i, v in enumerate(x_vectors): - ax.scatter(x=[i]*ensemble.n_vectors, y=v, s=40, color=colors, alpha=0.6) + ax.scatter(x=v, y=[i]*ensemble.n_vectors, s=40, color=colors, alpha=0.6) # plot the best parameter values - ax.scatter(np.arange(n_x), x_vectors[:, 0], s=40, + ax.scatter(x_vectors[:, 0], np.arange(n_x), s=40, color=[1. , 0. , 0. , 1. ]) - ax.plot(np.arange(n_x), ensemble.lower_bound[parameter_ids], '--', color='grey') - ax.plot(np.arange(n_x), ensemble.upper_bound[parameter_ids], '--', color='grey') - ax.set_ylim(np.min(ensemble.lower_bound) * 1.1, np.max(ensemble.upper_bound) * 1.1) - plt.xticks(np.arange(n_x), np.asarray(ensemble.x_names)[parameter_ids], rotation='vertical') + ax.plot(ensemble.lower_bound[parameter_ids], np.arange(n_x), '--', color='grey') + ax.plot(ensemble.upper_bound[parameter_ids], np.arange(n_x), '--', color='grey') + ax.set_xlim(np.min(ensemble.lower_bound) * 1.1, np.max(ensemble.upper_bound) * 1.1) + plt.yticks(np.arange(n_x), np.asarray(ensemble.x_names)[parameter_ids]) plt.tight_layout() return ax From f5691c974bfec37ada645b53b455ffe01c71b38f Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Tue, 6 May 2025 16:59:09 +0200 Subject: [PATCH 5/5] fix white spaces --- pypesto/visualize/ensemble.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pypesto/visualize/ensemble.py b/pypesto/visualize/ensemble.py index b0e67e753..fb2922dfe 100644 --- a/pypesto/visualize/ensemble.py +++ b/pypesto/visualize/ensemble.py @@ -459,20 +459,20 @@ def ensemble_parameters_plot( rectangles = [] cmap = colormaps['Greys'] colors = np.flip(cmap(np.linspace(0.3, 0.8, (ensemble.n_vectors-1))), axis=0) - colors = np.insert(colors, 0, [1. , 0. , 0. , 1. ], axis=0) + colors = np.insert(colors, 0, [1., 0., 0., 1.], axis=0) for i, par_values in enumerate(x_vectors): w_rect = np.max(par_values) - np.min(par_values) # rectangle width rectangles.append( Rectangle((np.min(par_values), y_rect), w_rect, h_rect)) y_rect += h_rect + 0.2 - ax.add_collection(PatchCollection(rectangles, facecolors=[1, 1, 1, 1], edgecolors='dimgrey')) + ax.add_collection(PatchCollection(rectangles, facecolors=[1., 1., 1., 1.], edgecolors='dimgrey')) for i, v in enumerate(x_vectors): ax.scatter(x=v, y=[i]*ensemble.n_vectors, s=40, color=colors, alpha=0.6) # plot the best parameter values ax.scatter(x_vectors[:, 0], np.arange(n_x), s=40, - color=[1. , 0. , 0. , 1. ]) + color=[1., 0., 0., 1.]) ax.plot(ensemble.lower_bound[parameter_ids], np.arange(n_x), '--', color='grey') ax.plot(ensemble.upper_bound[parameter_ids], np.arange(n_x), '--', color='grey')