NSE and R2 return 1.0 when model values are NaN
Maintainers usually reply within 1 day
Nobody has claimed this yet.
- #721 by @syahra712 — closed without merging
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 72/100
Research direction
Start in src/modelskill/metrics.py, in nash_sutcliffe_efficiency, where np.sum is applied to the inputs; r2 builds on the same path. Reproduce with the pandas Series example from the issue, comparing against the NumPy-array call, which already returns NaN. Done when both input types give NaN for NaN model values and the existing metric tests still pass.
Written by the indexing model from the issue text.
Description
nse and r2 return a perfect 1.0 when model values are missing. rmse, bias, mae, kge and cc return NaN for the same input.
import numpy as np, pandas as pd, modelskill as ms
t = pd.date_range("2026-01-01", periods=4, freq="h")
df = pd.DataFrame({"obs": [1.0, 2, 3, 4], "mod": [1.0, 2, np.nan, np.nan]}, index=t)
cmp = ms.from_matched(df, obs_item="obs")
print(cmp.skill(metrics=["nse", "rmse"]).to_dataframe())
# n nse rmse
# observation
# obs 4 1.0 NaN
df["mod"] = np.nan
print(ms.from_matched(df, obs_item="obs").score(metric="nse"))
# {'mod': 1.0}
The same happens calling the metric directly on pandas Series. With NumPy arrays it returns NaN:
obs = pd.Series([1.0, 2, 3, 4])
mod = pd.Series([1.0, 2, np.nan, np.nan])
ms.metrics.nse(obs, mod) # 1.0
ms.metrics.nse(obs.values, mod.values) # nan
Cause
nash_sutcliffe_efficiency uses np.sum on its inputs, which for a pandas Series skips NaN. The numerator then sums only the points where the model has a value, while the denominator sums every observation. The result is not the NSE of the non-missing points either; it overstates the fit, and an all-NaN model scores 1.0. r2 inherits it.
Expected
NaN, as the other metrics return. A model run that failed part-way, and so returns NaN after the failure, should not score as a perfect fit.
Converting the inputs with np.asarray before summing would make it consistent with the other metrics.
modelskill 1.4.0, numpy 2.4.6, pandas 2.3.3. The code is unchanged on main.
- Dominant language
- Python
- Stars
- 58
- Forks
- 10
- Avg merge
- 28m
- Merged PRs (30d)
- 1
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from DHI/modelskill
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
DHI/modelskill#732 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
DHI/modelskill#731 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 69/100
DHI/modelskill#728 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
DHI/modelskill#628 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
DHI/modelskill#734 ·
Maintainers usually reply within 1 day
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
AstrBotDevs/AstrBot#10720 ·
Maintainers usually reply within 1 day
-
fix(xiaomi): curated picker list still offers six ids that return 400 (plan-limited vs retired)Opencause/upstream comp/cli P2 provider/other tool/file type/bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
NousResearch/hermes-agent#143900 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 1-3 hours Newbie friendliness 85/100
WLAN-Pi/wlanpi-profiler#341 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
Maintainers usually reply within 1 day