feat: score ordinal rows by rank, each figure against its own null - #104
Merged
Merged
Conversation
Score rows were loaded and excluded from every figure, because every metric here is rank-blind. They are now read by three rank-aware figures in a block of their own, never beside a choice figure: - mean absolute error of the expected score, in levels, against a permutation null of the arm's own answers shuffled across rows; - the ranked probability score; - a cumulative calibration error: at each threshold, the predicted probability of being at or below it against how often it was, pooled and binned as ECE is. The last two are read against a calibrated-model floor built by redrawing each row's level from its own distribution. METHODOLOGY is the design. typesafe_wire asks a real Score with the row's rubric, which the JevBench loader now keeps as the levels' descriptions, reads the vendor's expected score as given, and adds its instructions to the cache key only when they change, so existing keys stand. A score row whose options are not integer levels is refused. RunResult's choice accessors read choice rows only, which kept the site's worked example on its 105 predictions. Fixes #6. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6. This follows the approved design, with the METHODOLOGY section ("Ordinal score questions are scored by rank") written first as the spec.
Measurement (
src/plumbline/metrics/ordinal.py)The answer. A score answer is a distribution over levels 0 to K−1. Its point answer is the expected score, the vendor's own answer, never swapped for the argmax. An arm that returns only a level gets that level as its expected score, and only the first figure below.
The three figures:
The floors. The predicted distributions are held fixed and each row's gold level is redrawn from its own distribution, vectorized over the draws, so every resample is calibrated by construction. The wording reuses
_judgment, so INCONCLUSIVE reads exactly as it does everywhere else.Tests (
tests/test_ordinal.py, 10 tests):Wiring
SUPPORTED_QUESTION_TYPESincludesscore, and a newCHOICE_QUESTION_TYPESnames the rank-blind group.typesafe_wire.Scorewith the rubric in level order and reads the vendor's expected score as given.labelis the nearest level, for display only.score_instructionsjoins the cache key only when changed, so every existing key stands (tested).asked_as._armsplits into a choice block and a#### Score questionsblock, and the choice block's early return can no longer swallow the score block.RunResultaccessors.probabilities(),outcomes,accuracyand the rest are "the calibratable column", so they now read choice rows only, throughchoice_successes. The site build caught this: its worked example started deriving ECE over 111 rows, and parity refused the deploy.What changes for users
1b96dc91to420956a9.docs/example-report.mdis regenerated. README's limitation bullet and quickstart sample are updated, as aredocs/datasets.md(the score row format),datasets/public/README.md, PLAN (Score and ordinal support: rank-aware metrics for rows that currently load and are excluded #6 landed) and the site's worked-example heading.The full suite is 620 passed. mypy --strict passes, and so does the site gate (parity including the worked example, links, search, browser smoke). The prose checks pass.
🤖 Generated with Claude Code