From 70b958be90ca812be9ad65517112f3894890487f Mon Sep 17 00:00:00 2001 From: Teddy Tennant Date: Wed, 8 Jul 2026 12:29:16 -0400 Subject: [PATCH 1/2] fix(metrics): correct explained_variance residual-variance term The residual-variance numerator in explained_variance subtracted the raw residual mean from a sum of squared residuals, mixing units of y with units of y^2. Explained variance is 1 - Var(y_true - y_pred) / Var(y_true), so the numerator must be the sum of squared deviations of the residuals from their mean. For y_true=[0,0.1,0.2,0.3,0.4], y_pred=[0.1,0.3,0.2,0.5,0.7] the function returned 0.8; the correct explained variance is 0.48, matching sklearn.metrics.explained_variance_score. Update the test accordingly. --- src/metrics_regression.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/metrics_regression.rs b/src/metrics_regression.rs index c15c8db08..6af225f32 100644 --- a/src/metrics_regression.rs +++ b/src/metrics_regression.rs @@ -137,7 +137,9 @@ pub trait SingleTargetRegression>: let mean_error = diff.mean().ok_or(Error::NotEnoughSamples)?; Ok(F::one() - - (diff.mapv_into(|x| x * x).sum() - mean_error) + - diff + .mapv_into(|x| (x - mean_error) * (x - mean_error)) + .sum() / (single_target_compare_to .mapv(|x| (x - mean) * (x - mean)) .sum() @@ -474,7 +476,10 @@ mod tests { let abs_err_from_arr1 = prediction.explained_variance(st_dataset.targets()).unwrap(); let prediction: DatasetBase<_, _> = (records.view(), prediction).into(); let abs_err_from_ds = prediction.explained_variance(&st_dataset).unwrap(); - assert_abs_diff_eq!(abs_err_from_arr1, 0.8, epsilon = 1e-5); + // Explained variance is 1 - Var(y_true - y_pred) / Var(y_true), matching + // sklearn.metrics.explained_variance_score([0.0,0.1,0.2,0.3,0.4], + // [0.1,0.3,0.2,0.5,0.7]) == 0.48 + assert_abs_diff_eq!(abs_err_from_arr1, 0.48, epsilon = 1e-5); assert_abs_diff_eq!(abs_err_from_arr1, abs_err_from_ds); } From ff0ed3e857a9cca03e6baf56ced9c6977a81f62a Mon Sep 17 00:00:00 2001 From: Teddy Tennant Date: Mon, 24 Aug 2026 12:10:56 -0400 Subject: [PATCH 2/2] Link the sklearn reference in the explained_variance test --- src/metrics_regression.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/metrics_regression.rs b/src/metrics_regression.rs index 6af225f32..c6bed21c2 100644 --- a/src/metrics_regression.rs +++ b/src/metrics_regression.rs @@ -479,6 +479,8 @@ mod tests { // Explained variance is 1 - Var(y_true - y_pred) / Var(y_true), matching // sklearn.metrics.explained_variance_score([0.0,0.1,0.2,0.3,0.4], // [0.1,0.3,0.2,0.5,0.7]) == 0.48 + // https://scikit-learn.org/stable/modules/generated/sklearn.metrics.explained_variance_score.html + // sklearn averages where this sums, and the 1/n cancels between the two terms. assert_abs_diff_eq!(abs_err_from_arr1, 0.48, epsilon = 1e-5); assert_abs_diff_eq!(abs_err_from_arr1, abs_err_from_ds); }