Thanks to visit codestin.com
Credit goes to github.com

Skip to content

DOC Fix det_curve fnr docstring threshold direction - #34390

Merged
jeremiedbb merged 2 commits into
scikit-learn:mainfrom
sha-sta:doc-det-curve-fnr-threshold-direction
Jul 9, 2026
Merged

DOC Fix det_curve fnr docstring threshold direction#34390
jeremiedbb merged 2 commits into
scikit-learn:mainfrom
sha-sta:doc-det-curve-fnr-threshold-direction

Conversation

@sha-sta

@sha-sta sha-sta commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Reference Issues/PRs

Issue #34310 and PR #34325 concern the adjacent thresholds increasing/decreasing wording; this PR fixes the separate fnr threshold-direction sentence.

What does this implement/fix? Explain your changes.

The Returns section of sklearn.metrics.det_curve describes fnr as "the false negative rate of predictions with score >= thresholds[i]." That text is copied from the fpr entry and is wrong for the false negative rate: a false negative is a positive sample predicted negative, i.e. one scoring below the threshold, so FNR at element i is the rate of positives with score < thresholds[i]. The function's own example confirms this: with thresholds=[0.35, 0.4, 0.8] and fnr=[0., 0.5, 0.5], the positive scoring 0.35 becomes a false negative at threshold 0.4 (0.35 < 0.4). This is a one-token docstring fix (>= to <) on the fnr line only; the fpr line is left unchanged (its >= is correct), there is no behavior change, and the existing doctest still passes.

First time contributor introduction

Hi, I'm Christian, and I'm a rising sophomore studying CS at Johns Hopkins. I used scikit-learn for binary classification for a project where a false negative (a missed positive) can potentially be dangerous for the user/client. I ran into this while using det_curve to choose an operating threshold on the miss-rate vs false-alarm tradeoff, and the inverted fnr threshold direction in the docs pointed me the wrong way until the worked example set me straight.

AI usage disclosure

I used AI assistance for:

  • Documentation (including examples)
  • Research and understanding

Any other comments?

The correctness proof is already encoded in the function's own doctest (the fnr=[0., 0.5, 0.5] example), so no new test is needed.

The Returns entry for `fnr` described the false negative rate over
predictions with `score >= thresholds[i]`, copied from the `fpr` entry.
A false negative is a positive sample predicted negative, i.e. one
scoring below the threshold, so FNR at element i is the rate of positives
with `score < thresholds[i]`. The function's own example confirms FNR
uses score < threshold (thresholds=[0.35, 0.4, 0.8], fnr=[0., 0.5, 0.5]).
Distinct from scikit-learn#34325, which fixes the separate thresholds-direction wording.
@github-actions

Copy link
Copy Markdown

Thank you for opening your first pull request to scikit-learn! 🎉

To help get your contribution reviewed, please make sure that:

  • You have filled out the pull request template.
  • The pull request addresses an existing issue that is ready for
    contribution (e.g. not tagged as "Needs Triage", "Needs Decision", ...).
    If you are proposing a new feature, please open an issue to discuss it first.
  • There are no other open pull requests already targeting the same issue.
  • You have followed the pull request checklist.
    In particular, linting and tests should pass.

@jeremiedbb

Copy link
Copy Markdown
Member

Thanks for the PR @sha-sta. Please also include the fix for the threshold direction in this PR as the author of #34325 has not followed-up.

@eeshsaxena eeshsaxena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks correct to me. A false negative is a positive sample that scores below the threshold (so it's predicted negative), so fnr[i] should indeed be described in terms of score < thresholds[i], not >=.

I checked it against the actual output to be sure:

import numpy as np
from sklearn.metrics import det_curve

y_true  = np.array([0, 0, 1, 1, 1, 0])
y_score = np.array([0.1, 0.4, 0.35, 0.8, 0.6, 0.55])
fpr, fnr, thr = det_curve(y_true, y_score)
pos = y_score[y_true == 1]
# fnr matches "fraction of positives with score < threshold"
assert np.allclose(fnr, [np.mean(pos < t) for t in thr])

The fnr values (0, 0.333, 0.333, 0.333) line up with score < thresholds[i] and clearly do not match score >= thresholds[i] (1, 0.667, 0.667, 0.667), which confirms the sentence was copied from the fpr entry above (whose >= wording is correct for the false positive rate). Nice, focused fix.

The `thresholds` return of det_curve is documented as "Decreasing" but the
function returns them in increasing order, as its own example shows. Correct
the wording and assert monotonic non-decreasing thresholds in
test_det_curve_toydata. Incorporates scikit-learn#34325 at the maintainer's request;
combined with the fnr threshold-direction fix already in this PR.
@sha-sta

sha-sta commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Done @jeremiedbb, added the threshold-direction fix too. So this PR now covers both the fnr sentence and the thresholds direction.

@sha-sta
sha-sta requested a review from eeshsaxena July 9, 2026 06:52

@eeshsaxena eeshsaxena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the ping. I went through the change and it looks correct to me.

I reproduced the docstring example to double-check both edits:

y_true = np.array([0, 0, 1, 1]); y_score = np.array([0.1, 0.4, 0.35, 0.8])
fpr, fnr, thr = det_curve(y_true, y_score)
# fpr [0.5 0.5 0. ]  fnr [0. 0.5 0.5]  thr [0.35 0.4 0.8]
  • fnr -> score < thresholds[i]: a false negative is a positive sample predicted negative, i.e. scoring below the threshold, so this is the right direction. The thr[0] = 0.35 point is the one that disambiguates it: the two positives score 0.35 and 0.8, none are < 0.35, giving fnr = 0, which matches. The old >= thresholds[i] wording would give 1.0 there, so the previous text was indeed copied from fpr and wrong.
  • thresholds -> Increasing: the returned thr is [0.35, 0.4, 0.8], consistent with the 1.7 change (arbitrary threshold at +inf appended for the fpr=0/fnr=1 case), so Increasing matches the current behaviour.

Nice that the fpr entry is left as >= thresholds[i] - I checked and that one is still correct (fpr counts negatives at/above the threshold), so keeping the fpr/fnr boundary asymmetric is intentional and right, not an oversight.

The added assert np.all(np.diff(thresholds) >= 0) in test_det_curve_toydata is a nice guard for the increasing-order claim across the parametrized cases.

LGTM from me - deferring to the maintainers for the final call.

@jeremiedbb jeremiedbb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks @sha-sta

@jeremiedbb
jeremiedbb merged commit 54915bb into scikit-learn:main Jul 9, 2026
42 of 43 checks passed
prady0t pushed a commit to prady0t/scikit-learn that referenced this pull request Sep 2, 2026
@jeremiedbb jeremiedbb mentioned this pull request Sep 8, 2026
14 tasks
jeremiedbb pushed a commit to jeremiedbb/scikit-learn that referenced this pull request Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants