-
Notifications
You must be signed in to change notification settings - Fork 0
0032 fbeta substitutes tp predicted and support algebraically
Status: accepted · Date: 2026-08-14
The textbook F-beta formula is (1 + beta^2) * P * R / (beta^2 * P + R) for
precision P and recall R. The obvious implementation computes P and R
first — each already available as
Precision.Score /
Recall.Score — and
substitutes. scikit-learn's fbeta_score does not do this: it derives F-beta
from the raw tp/predicted/support counts directly. Substituting
P = tp/predicted and R = tp/support into the textbook formula and cancelling tp
leaves score = (1 + beta^2) * tp / (predicted + beta^2 * support) —
algebraically the same value when predicted and support are both nonzero,
but not the same computation.
Going through P and R first applies Prf.Divide's zero-division policy up
to three times for one F-beta value: once for P, once for R, and once more
for the combined denominator, which can itself look like zero even when the
uncombined one is not. (The inline version of this claim read "twice — once
for each of them, and once more for a denominator", counting two where it then
listed three; this sweep re-counted against Prf.FScore and corrected it to
three, the same kind of correction docs/decisions/0018 records for its
4079→4088 figure.) It also fails to reproduce scikit-learn whenever tp
is zero but predicted or support is not: the textbook route replaces an
undefined P or R with the zero-division policy's value (0, 1 or NaN)
before combining, while scikit-learn — and the direct-substitution formula —
still returns a well-defined 0 from the combined fraction, since the
numerator (1 + beta^2) * 0 is exactly 0 and the denominator is not.
Prf.FScore computes (1 + beta^2) * tp / (predicted + beta^2 * support)
directly from the three raw counts, routed through the single Prf.Divide
call the substituted formula needs — never through Precision/Recall's own
already-divided values.
-
Prf.FScore's comment carries a pointer here instead of restating the derivation and the divergence it prevents. - Verified by
PrfOracleTests.Matches_sklearn_precision_recall_fscore_support, whose corpusfbetafield spans multiplebetavalues including thetp == 0edge this derivation exists for, compared atMetricsCorpus.Tolerance(1e-9).
- 0001-target-framework
- 0002-unicode-comparison-unit
- 0003-provenance-and-licensing
- 0004-levenshtein-myers-backlog
- 0005-hamming-jellyfish-divergence
- 0006-ratcliff-autojunk
- 0007-metaphone-scope
- 0008-italian-enza-nltk-divergence
- 0009-sample-consumes-a-local-feed
- 0010-stop-word-list-provenance
- 0011-persistence-format
- 0012-per-package-versioning
- 0013-sentencepiece-parity-scope
- 0014-precompiled-normalizer
- 0015-sonar-rules-in-the-build
- 0016-metrics-package-placement
- 0017-bpe-parity-scope
- 0018-multiclass-roc-auc-parallelism-is-opt-in
- 0019-the-net-analysers-run-in-the-build-too
- 0020-normalize-is-a-projection-not-a-parameter
- 0021-multioutput-is-a-method-not-an-enum
- 0022-added-token-matching-flags
- 0023-byte-level-decode-substitutes
- 0024-weighted-median-averages-within-scikit-learns-epsilon
- 0025-quickselect-replaces-a-full-sort-for-the-median
- 0026-r2-and-explainedvariance-split-their-undefined-cases-differently
- 0027-r2-and-explainedvariance-vectorize-only-a-single-output
- 0028-log1p-is-kahans-identity-not-math-log-1-plus-x
- 0029-balanced-accuracy-adjusted-is-left-to-ieee-754-at-the-edge
- 0030-cohen-kappa-keeps-scikit-learns-expected-matrix-orientation
- 0031-nosamplecorrect-mirrors-numpys-float64-upcast
- 0032-fbeta-substitutes-tp-predicted-and-support-algebraically
- 0033-compensated-sum-is-neumaiers-variant
- 0034-dropout-is-refused-for-want-of-a-user
- 0035-a-null-pre-split-is-removed-with-invert-not-isolated
- 0036-a-member-may-ship-without-an-oracle-if-it-says-so
- 0037-the-guards-run-before-the-commit
- 0038-the-gate-confronts-an-exception-tag-with-the-page-that-documents-it
- 0039-mutual-information-returns-zero-on-an-empty-input
- 0040-a-curve-is-a-sealed-class-per-curve
- 0041-one-sample-file-per-public-class
- 0042-phonetic-encoders-refuse-a-null-word
- 0043-the-equality-table-is-sized-to-the-pattern
- 0044-compression-belongs-to-the-caller
- 0045-a-console-call-carries-its-reason-on-the-line
- 0046-check-adr-immutable-runs-in-ci-only
- 0047-one-gate-per-kernel-not-one-per-alphabet
- 0048-the-gate-depends-on-the-kernel-and-the-alphabet
- 0049-two-gates-per-kernel-tested-where-the-width-is-known
- 0050-the-sentencepiece-bpe-lineage-stays-a-bpe-model
- 0051-the-save-paths-cost-is-the-buffer-not-the-encoding
- benchmark_latest
- decisions
- equivalence
- matplotlib
- migration
- nightly_run
- numpy
- pandas
- performance
- pytorch
- seaborn
- sklearn
- statsmodels