fix(metrics): ignore NaN target/prediction pairs in validation metrics (SIENTIAPDE-2215)
This commit is contained in:
@@ -405,6 +405,23 @@ class DataManagerRepository(SientiaMonitoring):
|
||||
y_true_val = y_true_val.loc[common_index]
|
||||
y_pred_val = y_pred_val.loc[common_index]
|
||||
|
||||
# NaN target, or NaN prediction for rows with NaN features, carries no error signal
|
||||
# (SIENTIAPDE-2215).
|
||||
valid = y_true_val.notna() & y_pred_val.notna()
|
||||
if not valid.any():
|
||||
raise ValueError(
|
||||
'No validation rows with both target and prediction: all '
|
||||
f'{len(valid)} overlapping rows have NaN'
|
||||
)
|
||||
if not valid.all():
|
||||
self.info(
|
||||
f'Ignoring {int((~valid).sum())} validation row(s) with NaN target or prediction '
|
||||
'in the regression metrics',
|
||||
metadata,
|
||||
)
|
||||
y_true_val = y_true_val[valid]
|
||||
y_pred_val = y_pred_val[valid]
|
||||
|
||||
# Metrics helpers already round to 2 decimals.
|
||||
tmr.mse_val = mse(y_true_val, y_pred_val)
|
||||
tmr.mae_val = mae(y_true_val, y_pred_val)
|
||||
|
||||
@@ -309,6 +309,41 @@ def test_compute_regression_metrics_no_overlap():
|
||||
repo.compute_regression_metrics(tmr, MagicMock())
|
||||
|
||||
|
||||
def test_compute_regression_metrics_ignores_nan_pairs():
|
||||
"""NaN target or NaN prediction rows are left out of mse/mae/r2 (SIENTIAPDE-2215)."""
|
||||
repo = dmr.DataManagerRepository(MagicMock())
|
||||
p = TrainModelParams.from_dict(_minimal_dict_for_prepare())
|
||||
idx = pd.Index([0, 1, 2, 3])
|
||||
tmr = TrainModelResult(
|
||||
params=p,
|
||||
train_data=pd.DataFrame({'t': [1.0, 2.0, 3.0, 4.0]}, index=idx),
|
||||
val_data=pd.DataFrame({'t': [1.0, np.nan, 3.0, 4.0]}, index=idx),
|
||||
y_pred=pd.DataFrame({'p': [1.0, 2.0, np.nan, 4.0]}, index=idx),
|
||||
)
|
||||
|
||||
wrapper = MagicMock()
|
||||
wrapper.model = object() # no sklearn .regr -> equation extraction is skipped
|
||||
out = repo.compute_regression_metrics(tmr, wrapper)
|
||||
|
||||
assert out.mse_val == 0.0
|
||||
assert out.mae_val == 0.0
|
||||
|
||||
|
||||
def test_compute_regression_metrics_all_nan_pairs_raises():
|
||||
repo = dmr.DataManagerRepository(MagicMock())
|
||||
p = TrainModelParams.from_dict(_minimal_dict_for_prepare())
|
||||
idx = pd.Index([0, 1])
|
||||
tmr = TrainModelResult(
|
||||
params=p,
|
||||
train_data=pd.DataFrame({'t': [1.0, 2.0]}, index=idx),
|
||||
val_data=pd.DataFrame({'t': [1.0, 2.0]}, index=idx),
|
||||
y_pred=pd.DataFrame({'p': [np.nan, np.nan]}, index=idx),
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match='No validation rows with both target and prediction'):
|
||||
repo.compute_regression_metrics(tmr, MagicMock())
|
||||
|
||||
|
||||
def test_compute_regression_metrics_linear_equation():
|
||||
repo = dmr.DataManagerRepository(MagicMock())
|
||||
p = TrainModelParams.from_dict(_minimal_dict_for_prepare())
|
||||
|
||||
Reference in New Issue
Block a user