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