From 39a487f26a4bdec31ab09b3e62eee9810b5d8daa Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Wed, 4 Feb 2026 21:07:43 +0100 Subject: [PATCH 1/5] fix mean of residuals in plot_goodness_of_fit --- petab/v1/visualize/plot_residuals.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/petab/v1/visualize/plot_residuals.py b/petab/v1/visualize/plot_residuals.py index 46e83fb9..a15a46b3 100644 --- a/petab/v1/visualize/plot_residuals.py +++ b/petab/v1/visualize/plot_residuals.py @@ -199,7 +199,8 @@ def plot_goodness_of_fit( ax.plot(x, x, linestyle="--", color="gray") ax.plot(x, intercept + slope * x, "r", label="fitted line") - mse = np.mean(np.abs(residual_df["residual"])) + # assumes that residuals are normalized by default + msnr = np.mean(np.power(residual_df["residual"], 2)) ax.text( 0.1, 0.70, @@ -207,7 +208,7 @@ def plot_goodness_of_fit( f"slope: {slope:.2f}\n" f"intercept: {intercept:.2f}\n" f"p-value: {p_value:.2e}\n" - f"mean squared error: {mse:.2e}\n", + f"mean of squared normalized residuals: {msnr:.2e}\n", transform=ax.transAxes, ) From 7757a1dc1c633d7bf209952eb1ada8eb714a7297 Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Wed, 4 Feb 2026 21:19:23 +0100 Subject: [PATCH 2/5] add line break --- petab/v1/visualize/plot_residuals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/petab/v1/visualize/plot_residuals.py b/petab/v1/visualize/plot_residuals.py index a15a46b3..63c21ddd 100644 --- a/petab/v1/visualize/plot_residuals.py +++ b/petab/v1/visualize/plot_residuals.py @@ -208,7 +208,7 @@ def plot_goodness_of_fit( f"slope: {slope:.2f}\n" f"intercept: {intercept:.2f}\n" f"p-value: {p_value:.2e}\n" - f"mean of squared normalized residuals: {msnr:.2e}\n", + f"mean of squared\nnormalized residuals: {msnr:.2e}\n", transform=ax.transAxes, ) From 80b83c2a503228375a73917fa92458766b7949c1 Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Wed, 18 Mar 2026 14:22:14 +0100 Subject: [PATCH 3/5] ensure that normalized residuals are used --- petab/v1/visualize/plot_residuals.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/petab/v1/visualize/plot_residuals.py b/petab/v1/visualize/plot_residuals.py index 63c21ddd..4c29531d 100644 --- a/petab/v1/visualize/plot_residuals.py +++ b/petab/v1/visualize/plot_residuals.py @@ -173,7 +173,11 @@ def plot_goodness_of_fit( simulation_dfs=simulations_df, observable_dfs=petab_problem.observable_df, parameter_dfs=petab_problem.parameter_df, + normalize=True )[0] + # compute mean of squared normalized residuals + msnr = np.mean(np.power(residual_df["residual"], 2)) + slope, intercept, r_value, p_value, std_err = stats.linregress( simulations_df["simulation"], petab_problem.measurement_df["measurement"], @@ -199,8 +203,6 @@ def plot_goodness_of_fit( ax.plot(x, x, linestyle="--", color="gray") ax.plot(x, intercept + slope * x, "r", label="fitted line") - # assumes that residuals are normalized by default - msnr = np.mean(np.power(residual_df["residual"], 2)) ax.text( 0.1, 0.70, From 5513cd40ae62f62139229c92be142db98d021bf0 Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Wed, 18 Mar 2026 15:20:10 +0100 Subject: [PATCH 4/5] add possibility to choose between normalized and unnormalized errors --- petab/v1/visualize/plot_residuals.py | 34 ++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/petab/v1/visualize/plot_residuals.py b/petab/v1/visualize/plot_residuals.py index 4c29531d..2a296249 100644 --- a/petab/v1/visualize/plot_residuals.py +++ b/petab/v1/visualize/plot_residuals.py @@ -136,6 +136,7 @@ def plot_goodness_of_fit( size: tuple = (10, 7), color=None, ax: plt.Axes | None = None, + normalized_error: bool = True, ) -> matplotlib.axes.Axes: """ Plot goodness of fit. @@ -154,6 +155,9 @@ def plot_goodness_of_fit( `matplotlib.pyplot.scatter`. ax: Axis object. + normalized_error: + Type of error to display. If True, mean of squared normalized residuals is shown, + otherwise mean of squared residuals. Returns ------- @@ -168,15 +172,25 @@ def plot_goodness_of_fit( "are needed for goodness_of_fit" ) - residual_df = calculate_residuals( - measurement_dfs=petab_problem.measurement_df, - simulation_dfs=simulations_df, - observable_dfs=petab_problem.observable_df, - parameter_dfs=petab_problem.parameter_df, - normalize=True - )[0] - # compute mean of squared normalized residuals - msnr = np.mean(np.power(residual_df["residual"], 2)) + if normalized_error: + residual_df = calculate_residuals( + measurement_dfs=petab_problem.measurement_df, + simulation_dfs=simulations_df, + observable_dfs=petab_problem.observable_df, + parameter_dfs=petab_problem.parameter_df, + normalize=True, + )[0] + error_name = "mean of squared\nnormalized residuals" + else: + residual_df = calculate_residuals( + measurement_dfs=petab_problem.measurement_df, + simulation_dfs=simulations_df, + observable_dfs=petab_problem.observable_df, + parameter_dfs=petab_problem.parameter_df, + normalize=False, + )[0] + error_name = "mean of squared residuals" + error = np.mean(np.power(residual_df["residual"], 2)) slope, intercept, r_value, p_value, std_err = stats.linregress( simulations_df["simulation"], @@ -210,7 +224,7 @@ def plot_goodness_of_fit( f"slope: {slope:.2f}\n" f"intercept: {intercept:.2f}\n" f"p-value: {p_value:.2e}\n" - f"mean of squared\nnormalized residuals: {msnr:.2e}\n", + f"{error_name}: {error:.2e}\n", transform=ax.transAxes, ) From c71f7426313a1186b1af0d3e1db7573ae6bfc051 Mon Sep 17 00:00:00 2001 From: Polina Lakrisenko Date: Wed, 18 Mar 2026 15:21:37 +0100 Subject: [PATCH 5/5] ruff --- petab/v1/visualize/plot_residuals.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/petab/v1/visualize/plot_residuals.py b/petab/v1/visualize/plot_residuals.py index 2a296249..a1f2ec9b 100644 --- a/petab/v1/visualize/plot_residuals.py +++ b/petab/v1/visualize/plot_residuals.py @@ -156,7 +156,8 @@ def plot_goodness_of_fit( ax: Axis object. normalized_error: - Type of error to display. If True, mean of squared normalized residuals is shown, + Type of error to display. + If True, mean of squared normalized residuals is shown, otherwise mean of squared residuals. Returns