evalaudit 0.4.0__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. evalaudit-0.6.0/.gitattributes +1 -0
  2. {evalaudit-0.4.0 → evalaudit-0.6.0}/.gitignore +20 -20
  3. {evalaudit-0.4.0 → evalaudit-0.6.0}/CHANGELOG.md +383 -14
  4. {evalaudit-0.4.0 → evalaudit-0.6.0}/LICENSE +21 -21
  5. {evalaudit-0.4.0 → evalaudit-0.6.0}/PKG-INFO +45 -23
  6. {evalaudit-0.4.0 → evalaudit-0.6.0}/README.md +372 -350
  7. evalaudit-0.6.0/analysis/README.md +108 -0
  8. evalaudit-0.6.0/analysis/mt-bench.md +277 -0
  9. evalaudit-0.6.0/analysis/output/q1_comparisons_ratings.csv +7 -0
  10. evalaudit-0.6.0/analysis/q1_check_v021.py +97 -0
  11. evalaudit-0.6.0/analysis/q1_leaderboard.py +332 -0
  12. evalaudit-0.6.0/analysis/q2_human_agreement.py +288 -0
  13. evalaudit-0.6.0/analysis/q3_alignment.py +230 -0
  14. evalaudit-0.6.0/analysis/q5_judge_against_human_baseline.py +617 -0
  15. {evalaudit-0.4.0 → evalaudit-0.6.0}/evalaudit/__init__.py +64 -64
  16. {evalaudit-0.4.0 → evalaudit-0.6.0}/evalaudit/_types.py +2174 -1763
  17. {evalaudit-0.4.0 → evalaudit-0.6.0}/evalaudit/agreement.py +605 -605
  18. {evalaudit-0.4.0 → evalaudit-0.6.0}/evalaudit/audit.py +61 -18
  19. {evalaudit-0.4.0 → evalaudit-0.6.0}/evalaudit/compare.py +25 -12
  20. {evalaudit-0.4.0 → evalaudit-0.6.0}/evalaudit/judge.py +1227 -783
  21. {evalaudit-0.4.0 → evalaudit-0.6.0}/evalaudit/pairwise.py +851 -841
  22. {evalaudit-0.4.0 → evalaudit-0.6.0}/evalaudit/power.py +528 -528
  23. {evalaudit-0.4.0 → evalaudit-0.6.0}/evalaudit/scores.py +247 -221
  24. evalaudit-0.6.0/examples/independent_bootstrap_coverage_study.py +139 -0
  25. {evalaudit-0.4.0 → evalaudit-0.6.0}/pyproject.toml +47 -47
  26. evalaudit-0.6.0/tests/data/README.md +35 -0
  27. evalaudit-0.6.0/tests/data/make_mt_bench_csv.py +77 -0
  28. evalaudit-0.6.0/tests/data/mt_bench_gpt4_pair.csv +2401 -0
  29. evalaudit-0.6.0/tests/data/mt_bench_human.csv +3356 -0
  30. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/test_agreement.py +2029 -1919
  31. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/test_audit.py +455 -11
  32. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/test_compare.py +1137 -916
  33. evalaudit-0.6.0/tests/test_judge.py +6382 -0
  34. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/test_not_shown.py +13 -5
  35. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/test_pairwise.py +2374 -2248
  36. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/test_power.py +1307 -1025
  37. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/test_scores.py +522 -453
  38. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/test_version.py +3 -3
  39. evalaudit-0.4.0/tests/test_judge.py +0 -2588
  40. {evalaudit-0.4.0 → evalaudit-0.6.0}/.github/workflows/tests.yml +0 -0
  41. {evalaudit-0.4.0 → evalaudit-0.6.0}/examples/.gitkeep +0 -0
  42. {evalaudit-0.4.0 → evalaudit-0.6.0}/examples/interval_method_study.py +0 -0
  43. {evalaudit-0.4.0 → evalaudit-0.6.0}/examples/pairwise_cluster_study.py +0 -0
  44. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/__init__.py +0 -0
  45. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/test_confidence.py +0 -0
  46. {evalaudit-0.4.0 → evalaudit-0.6.0}/tests/test_readme.py +0 -0
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -1,20 +1,20 @@
1
- CLAUDE.md
2
- __pycache__/
3
- *.py[cod]
4
- .venv/
5
- venv/
6
- .env
7
- build/
8
- dist/
9
- *.egg-info/
10
- .pytest_cache/
11
- .coverage
12
- htmlcov/
13
- .ipynb_checkpoints/
14
- .DS_Store
15
- .vscode/
16
- .idea/
17
- data/raw/
18
- outputs/
19
- *.jsonl/
20
- AUDIT.md
1
+ CLAUDE.md
2
+ __pycache__/
3
+ *.py[cod]
4
+ .venv/
5
+ venv/
6
+ .env
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .pytest_cache/
11
+ .coverage
12
+ htmlcov/
13
+ .ipynb_checkpoints/
14
+ .DS_Store
15
+ .vscode/
16
+ .idea/
17
+ data/raw/
18
+ outputs/
19
+ *.jsonl/
20
+ AUDIT.md
@@ -3,20 +3,386 @@
3
3
  Every change to evalaudit that a caller can see is recorded here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
5
  version numbers follow [semantic versioning](https://semver.org/). Before 1.0
6
- a minor version can break things, and 0.3.0 does.
6
+ a minor version can break things, and 0.3.0, 0.4.0, 0.5.0 and 0.6.0 all do.
7
7
 
8
8
  An entry marked **Breaking** changes what a call that worked on the previous
9
9
  release returns, raises or prints. The mark goes on the entry wherever it
10
10
  sits, under Changed, Fixed or Removed.
11
11
 
12
- 0.3.0 is the first release with a tag. The entries below it were
13
- reconstructed after the fact. Their dates are PyPI's upload dates, and their
14
- contents were checked against the published wheels and the commit each wheel
15
- matches.
12
+ Every release is dated by the day PyPI received its upload, in UTC. 0.3.0 is
13
+ the first release with a tag, and 0.4.0, 0.5.0 and 0.6.0 have one too. The
14
+ entries below 0.3.0 were reconstructed after the fact, and their contents
15
+ were checked against the published wheels and the commit each wheel matches.
16
+
17
+ ## [0.6.0] - 2026-10-26
18
+
19
+ Most of this release is text, and most of it changes what a summary or a
20
+ message prints. Missing and infinite scores are now refused where they used
21
+ to give NaN bounds that read as clearing zero. `audit` stops sizing the
22
+ power check on two unequal groups, stops raising when two systems never
23
+ disagree, and names the side a position-biased judge favours. The judge's
24
+ summary without a human baseline no longer reads Krippendorff's lines as a
25
+ verdict on the judge. Rates near 0% and 100% print enough digits to stay
26
+ off the end they have not reached. Anything that matches on the old
27
+ sentences will stop matching. Every entry was checked by running the same
28
+ calls against the v0.5.0 tag and against this tree. Three entries were first
29
+ written at commit 898a9cb and changed after it, and each one says how.
16
30
 
17
- ## [Unreleased]
31
+ ### Changed
32
+
33
+ - Nothing.
34
+
35
+ ### Added
36
+
37
+ - Nothing.
38
+
39
+ ### Fixed
40
+
41
+ - **Breaking.** `JudgeValidation.summary()` without a human baseline no
42
+ longer reads Krippendorff's lines as a verdict on the judge. Those lines
43
+ were set for human coders, and `AuditConfig` already says a judge's
44
+ threshold should be set from the decision the judge is used for. The
45
+ sentence "The judge is not a stand-in for the humans at this level." is
46
+ gone. Every band sentence, including the one printed when there is no
47
+ interval, is now followed by "These lines were set for human coders, and
48
+ how closely a judge has to agree depends on the decision it is used for.
49
+ Calling judge_validation with a human_baseline measures the judge against
50
+ a second human instead." Judge labels with alpha 0.405 and an interval of
51
+ 0.269 to 0.525 on 200 items printed "The whole interval sits below 0.667,
52
+ the conventional floor for drawing any conclusion from coded data. The
53
+ judge is not a stand-in for the humans at this level." They now print "The
54
+ whole interval sits below 0.667, the conventional floor for drawing any
55
+ conclusion from coded data. These lines were set for human coders, and how
56
+ closely a judge has to agree depends on the decision it is used for.
57
+ Calling judge_validation with a human_baseline measures the judge against
58
+ a second human instead." `audit` carries the judge's summary word for
59
+ word, so its judge findings carry the new sentences too. The one exception
60
+ is the finding for undefined agreement, which has no band sentence. The
61
+ action text of two judge findings, "Judge-human agreement is below the
62
+ working threshold" and "The data cannot show that the judge clears the
63
+ working threshold", was shortened, because the summary now makes the point
64
+ that a judge's threshold depends on the decision. `rater_agreement` and
65
+ the summary with a baseline are unchanged. Anything matching on the old
66
+ sentences will stop matching.
67
+ - **Breaking.** `score_ci`, `compare_paired`, `compare_independent` and
68
+ `audit` now raise `ValueError` on a NaN or infinite score. They used to
69
+ carry it into the mean and return NaN bounds. A NaN bound fails every
70
+ comparison with zero, so the margin read as clearing zero. `audit(scores=
71
+ {"A": [0.2, nan, 0.8], "B": [0.1, 0.3, 0.7]})` reported "The headline
72
+ margin clears zero" off a margin nobody measured. It now raises "scores for
73
+ A must be finite numbers, got 1 missing or infinite value, at position 1.
74
+ Drop or fill that item before computing an interval". With two or more
75
+ such values the message counts them and points at the first, as in "got 2
76
+ missing or infinite values, the first at position 0. Drop or fill those
77
+ items before computing an interval". The compare functions name the
78
+ argument, `a` or `b`, in place of the system. As first written for this
79
+ release, the message for a single value read "got 1 missing or infinite
80
+ value(s), the first at position 1. Drop or fill those items".
81
+ - **Breaking.** `audit` no longer runs the power check on two independent
82
+ groups of 0/1 scores with different sizes. The power arithmetic assumes
83
+ equal groups, and the audit passed it the total, so it split the items
84
+ evenly. Groups of 10 and 990 were read as 500 in each, and with an effect
85
+ of interest of 0.15 the report said "This eval was large enough for the
86
+ effect at issue". With the 990-item group at a 50% pass rate and the
87
+ 10-item group 15 points above it, the shipped test has about 13% power.
88
+ The check now lands in `not_run` with "The two groups have 10 and 990
89
+ items. The power arithmetic here assumes equal groups, and splitting the
90
+ total evenly would overstate what the smaller group can find. Nothing to
91
+ report rather than a figure that does not apply." Equal groups are
92
+ unchanged. Scores that are not 0/1 keep the reason 0.5.0 gave, "The power
93
+ arithmetic here is for pass rates and these scores are not 0/1. Nothing to
94
+ report rather than a figure that does not apply.", whatever the group
95
+ sizes. As first written for this release, the check for unequal groups
96
+ ran first and gave groups of continuous scores the reason about group
97
+ sizes instead.
98
+ - `audit` no longer raises when two paired binary systems agree on every
99
+ item. The measured discordance rate of zero went to `detectable_effect`,
100
+ which refuses it, so `audit(scores={"A": [1, 0] * 50, "B": [1, 0] * 50})`
101
+ raised `ValueError`. The power check now lands in `not_run` with "The two
102
+ systems never disagree on an item, so there are no discordant pairs and
103
+ paired power has nothing to run on. The eval cannot say which system is
104
+ better from these items." The other checks report as usual.
105
+ - **Breaking.** The `audit` position finding now names the side the judge
106
+ favours. Its title was always "The judge favours whichever answer it reads
107
+ first", even for a judge that picked the second answer in all 100 pairs,
108
+ whose detail already said "second". That judge now gets "The judge
109
+ favours whichever answer it reads second". A judge that favours the first
110
+ answer gets the same title as before.
111
+ - **Breaking.** `JudgeValidation.summary()` on a result with no item left
112
+ said "One item cannot carry a reliability estimate" and printed "Plain
113
+ accuracy is nan%". `judge_validation` refuses such data with "no item
114
+ carries a label from both sides", so only a `JudgeValidation` built by
115
+ hand reaches this. Built with `agreement` and `accuracy` NaN, `n_items=0`
116
+ and `n_dropped=2`, it now reads "Judge and human agreement is undefined
117
+ (nominal, 0 items). No item has a label from both sides, so there is
118
+ nothing to compare and no number to report. Label more items and run this
119
+ again. 2 items were set aside because one side had no label."
120
+ - **Breaking.** `JudgeValidation.summary()` explained an alpha left
121
+ undefined by labels that never varied with "This comes from a rubric with
122
+ one label in it." The data shows only that one label was used, and the
123
+ rubric may have had others. On `judge_validation([1] * 40, [1] * 40)` it
124
+ now reads "This comes from both sides using one label throughout." The
125
+ `audit` finding for undefined judge agreement carries the new sentence.
126
+ - **Breaking.** `PowerResult.summary()` said "about 0 discordant pairs, and
127
+ McNemar reads only those" when the expected count rounded to zero.
128
+ `detectable_effect(2, discordance_rate=0.25)` expects 0.5 discordant
129
+ pairs, which rounds to 0. Its summary now reads "At a 25% discordance rate
130
+ that is under one discordant pair on average, and McNemar reads only
131
+ discordant pairs. Items where the two systems agree carry no information
132
+ about which one is better." Counts of one or more print as before. The
133
+ `audit` power finding carries the new sentence.
134
+ - **Breaking.** `PowerResult.summary()` printed rates to one decimal place,
135
+ so a positive rate could print as 0% and a rate below 1 as 100%.
136
+ `detectable_effect(1000, alpha=0.0004, discordance_rate=0.3)` said "a 0%
137
+ significance level" and now says "a 0.04% significance level".
138
+ `detectable_effect(1000, power=0.9996, discordance_rate=0.3)` said "At
139
+ 100% power" and now says "At 99.96% power". A positive rate that rounds to
140
+ 0% now prints with two significant figures. A rate below 1 that rounds to
141
+ 100% prints its gap from 100% to two significant figures, so the float
142
+ just below 1 prints as 99.999999999999989%. Exactly 0 and 1 print as 0%
143
+ and 100%, and every other rate prints as before. The `audit` power finding
144
+ carries these summaries.
145
+ - **Breaking.** `bradley_terry` with `resample="items"` said "the first
146
+ between" when one comparison had no `item_id`, as if several did. With one
147
+ missing id it now reads "item_id is missing on 1 of 2 comparisons, the one
148
+ between 'a' and 'b'." Two or more missing ids keep "the first between".
149
+
150
+ ### Documented
151
+
152
+ - The two-sample bootstrap in `compare_independent` covers less than it
153
+ claims on skewed scores with small groups. In 5,000 simulated evals per
154
+ setting, a 95% interval on exponential scores covered 92.6% of the time
155
+ with 25 and 60 items, 93.0% with 40 and 120, and 94.5% with 100 and 300,
156
+ each with a Monte Carlo standard error between 0.3 and 0.4 points. On
157
+ normal scores with 30 and 90 items it covered 94.1%. The docstring and
158
+ README now say so, and `examples/independent_bootstrap_coverage_study.py`
159
+ reproduces the figures with its seeds, settings and recorded output.
160
+ 0.5.0's README said nothing pinned this interval's bounds. Tests now match
161
+ its bounds against a separate implementation and against Welch's interval
162
+ on a large normal sample. They also check its coverage on normal data and
163
+ guard the skewed-data shortfall from growing. As first written for this
164
+ release the figures were about 92%, about 93% and about 94.5%, from a
165
+ 5,000-trial run whose seed was not recorded. That run cannot be repeated,
166
+ so those figures have no reproducible source. That does not show they were
167
+ wrong, and the recorded run gives the same 94.5% with 100 and 300 items.
168
+ - The `bradley_terry` docstring said the prompt-level spread on MT-Bench was
169
+ not measured, and stopped there. It now also says what was measured. On
170
+ the MT-Bench human votes, with the item bootstrap clustered on
171
+ question_id, seed 0 and 2,000 resamples, the mean gap-interval width over
172
+ the fifteen pairs was 1.58 times the comparison bootstrap's mean. That is
173
+ one run of each, a ratio of means rather than a per-pair figure, and not
174
+ an estimate of the spread.
175
+ - `analysis/mt-bench.md` said that correcting for clustering "widens every
176
+ gap interval by a factor of 1.58". The 1.58 is the ratio of the mean
177
+ gap-interval widths over the fifteen pairs, and single pairs vary around
178
+ it. The section now says so and carries a dated correction. No figure
179
+ changed.
180
+
181
+ ### Removed
182
+
183
+ - Nothing.
184
+
185
+ ## [0.5.0] - 2026-09-26
186
+
187
+ `judge_validation` can now measure the judge against a second human. Most
188
+ of the rest is text. Several printed sentences change, most of them for
189
+ hyphens and plurals, and one `audit` finding changes severity. Figures
190
+ change in one case. In the both-orders design, `position_bias` now scores a
191
+ pair judged more than once in an order when its first two reversed
192
+ judgements held a tie and a later two did not. That moves the count of
193
+ pairs scored, the consistency rate and its interval, and the figures on the
194
+ flips. A pair judged once in each order scores as before. Anything that
195
+ matches on the old sentences will stop matching. Every entry was checked by
196
+ running the same calls against the v0.4.0 tag and against this tree.
197
+
198
+ ### Changed
199
+
200
+ - Nothing.
201
+
202
+ ### Added
203
+
204
+ - `judge_validation(..., item_ids=None, human_baseline=None, ties=None)`
205
+ measures the judge against a human baseline. `human_baseline` is a frame
206
+ with `item_id`, `cluster_id`, `rater_id` and `rating`, one row per human
207
+ rating, and `item_ids` names the item at each position of `human` and
208
+ `judge`. The result carries judge-human and human-human alpha on the same
209
+ items, and a percentile interval on judge-human minus human-human that
210
+ resamples whole clusters. The verdict reads that interval and nothing
211
+ else, and is called provisional when it rests on fewer than 30 clusters.
212
+ The interval is refused when fewer than 90% of resamples leave both
213
+ alphas defined.
214
+
215
+ `ties` is required with a baseline. `"category"` keeps a tie as a label of
216
+ its own and `"drop"` sets it aside as no label, and the choice applies to
217
+ every figure in the result, the headline included. Baseline items are set
218
+ aside by three rules in a fixed order, and the summary counts what each
219
+ one took. A baseline supports only `level="nominal"`. On the MT-Bench
220
+ human judgements the figures reproduce `analysis/mt-bench.md` to the third
221
+ decimal. The new arguments are refused, each with its own message, in the
222
+ combinations that cannot work, such as `ties` or `item_ids` without a
223
+ baseline. Missing values in `item_id`, `cluster_id`, `rater_id` or
224
+ `item_ids` are refused, and the message names the column, the count and
225
+ where the first one is. Every check on the baseline runs before anything
226
+ is resampled.
227
+
228
+ - `JudgeValidation.n_dropped_ties`, `JudgeValidation.ties`, eleven fields
229
+ that start with `baseline_`, and the properties `has_baseline` and
230
+ `has_baseline_interval`. The new fields are the last in the dataclass and
231
+ have defaults, so building a `JudgeValidation` by position with 0.4.0's
232
+ fields still works. A call that passes none of the three new arguments
233
+ returns the same numbers as 0.4.0.
234
+
235
+ ### Fixed
236
+
237
+ - **Breaking.** `JudgeValidation.summary()` said "No interval was computed,
238
+ so nothing here is placed against sampling error." when the resamples had
239
+ been drawn and too few of them left alpha defined. On three items, two of
240
+ them unanimous, with `n_boot=2000` and `seed=0`, it now reads "There is
241
+ no interval on that alpha. 591 of 2000 resamples left it undefined, above
242
+ the 10% this reports through. Those are the resamples where every label
243
+ was the same, so percentiles of the rest would understate the upper
244
+ bound." When one resample is left undefined it reads "That is the
245
+ resample where every label was the same", and when only one was drawn
246
+ the count reads "1 of 1 resample". With `n_boot=0` it still says no
247
+ interval was computed. The `audit` judge finding carries the new
248
+ sentence.
249
+
250
+ - **Breaking.** The `audit` finding on a margin whose interval clears zero
251
+ opened "This is the margin between new and old, and it survives the
252
+ fit." No model is fitted there. It now reads "This is the margin between
253
+ new and old, and its interval excludes zero."
254
+
255
+ - **Breaking.** A count or figure of one was printed with a plural noun, or
256
+ with a verb or pronoun that assumed more than one. Each now takes the
257
+ singular. Anything other than one prints as before.
258
+
259
+ - `ScoreCI.summary()`. "With only 1 observations" now reads "With only 1
260
+ observation", and "The interval spans 1 points." now reads "The
261
+ interval spans 1 point."
262
+ - `ComparisonResult.summary()`. "1 of 1 items changed between systems."
263
+ now reads "1 of 1 item changed between systems."
264
+ - `KappaResult.summary()`. "(1 items, 2 raters, 2 categories)" now reads
265
+ "(1 item, 2 raters, 2 categories)".
266
+ - `AgreementResult.summary()`. "1 of 1 items graded more than once" now
267
+ reads "1 of 1 item graded more than once". Where the interval is
268
+ refused, "1 of 1 resamples came back undefined" now reads "1 of 1
269
+ resample came back undefined", and after one undefined resample "Those
270
+ resamples are the unanimous ones" now reads "That resample is the
271
+ unanimous one".
272
+ - `BTResult.summary()`. With two models, "0 of 1 pairs is separable" now
273
+ reads "0 of 1 pair is separable". With one pair separable among
274
+ several, "1 of 3 pairs are separable" now reads "1 of 3 pairs is
275
+ separable", since the verb follows the separable count. Where the
276
+ interval is refused, "1 of 1 resamples came back undefined" now reads
277
+ "1 of 1 resample came back undefined".
278
+ - The `ValueError` from `bradley_terry` for a missing item id. "item_id is
279
+ missing on 1 of 1 comparisons" now reads "item_id is missing on 1 of 1
280
+ comparison".
281
+ - `PositionBias.summary()`, randomised design. "Position A won 100.0% of 1
282
+ judgements" now reads "of 1 judgement". "1 pairs were also run in the
283
+ reverse order" now reads "1 pair was also run in the reverse order".
284
+ "1 judgements were ties and are left out of the rates above." now reads
285
+ "1 judgement was a tie and is left out of the rates above."
286
+ - `PositionBias.summary()`, both orders. "1 of 1 pairs were run in both
287
+ orders" now reads "1 of 1 pair was run in both orders", and "1 of 2
288
+ pairs were run in both orders" now reads "1 of 2 pairs was run in both
289
+ orders", since the verb follows the pairs run both ways. "on 0.0% of 1
290
+ pairs" now reads "on 0.0% of 1 pair", and "Of the 1 pairs it flipped
291
+ on" now reads "Of the 1 pair it flipped on".
292
+ - `LengthBias.summary()`. "(1 pairs)" now reads "(1 pair)", and "(1
293
+ disagreements)" now reads "(1 disagreement)", with or without a fitted
294
+ model. A length step that prints as 1, "1 characters of extra length
295
+ multiplies" or "1 characters multiplies", now reads "1 character".
296
+ - `PowerResult.summary()`. "you need roughly 1 paired comparisons" now
297
+ reads "you need roughly 1 paired comparison", and "about 1 discordant
298
+ pairs, and McNemar reads only those." now reads "about 1 discordant
299
+ pair, and McNemar reads only that one." A difference that prints as 1,
300
+ "is 1 points" or "a difference of 1 points", now reads "1 point".
301
+
302
+ The `audit` compare, agreement, position, length and power findings carry
303
+ these sentences word for word.
304
+
305
+ - **Breaking.** Compound modifiers are hyphenated in printed text.
306
+
307
+ - `PowerResult.summary()`. "To detect a 5 point difference", "leaves at
308
+ most a 5 point difference" and "To find a 5 point difference" now read
309
+ "5-point", and the same holds for any figure. "the continuity
310
+ corrected and exact forms" now reads "continuity-corrected", and
311
+ "Pairing removes the item to item difficulty" now reads
312
+ "item-to-item". The `audit` power finding carries these sentences.
313
+ - The `audit` action under "Rater agreement holds up". "Keep the double
314
+ graded sample" now reads "Keep the double-graded sample".
315
+ - The `ValueError` from `detectable_effect` and `min_sample_size` when
316
+ power does not exceed alpha. "A two sided test" now reads "A two-sided
317
+ test".
318
+
319
+ - **Breaking.** `PositionBias.summary()` printed a rate, an interval and a
320
+ verdict when nothing could be scored.
321
+
322
+ - Randomised design. On three pairs judged once, each a tie, it printed
323
+ "Each pair was judged once, so this reports the position-A win rate.
324
+ Position A won nan% of 0 judgements (95% CI: nan% to nan%, exact
325
+ binomial p=nan). The interval includes 50%, so the data cannot show
326
+ that position moved the judge." and went on to the caveat about
327
+ randomised order. It now reads "Each pair was judged once, so this
328
+ would report the position-A win rate. Every judgement was a tie, so
329
+ there is no rate to report and nothing here tests position."
330
+ - Both orders. On two pairs run both ways, each with a tie on one side,
331
+ and one pair judged once, it printed "2 of 3 pairs were run in both
332
+ orders, so this reports the consistency rate. The judge named the same
333
+ output under both orderings on nan% of 0 pairs (95% CI: nan% to nan%).
334
+ It never flipped, so there is no direction to test and nothing here
335
+ points at position." It now reads "2 of 3 pairs were run in both
336
+ orders, so this would report the consistency rate. In every pair run
337
+ both ways, the judge called a tie every time in at least one of the two
338
+ orders, so no pair could be scored and there is no rate to report.
339
+ Nothing here tests position."
340
+
341
+ The randomised summary ended "3 judgements were ties and are left out of
342
+ the rates above." and the both-orders one ended the same way with 2.
343
+ There are no rates for the ties to be left out of, so that sentence is
344
+ gone from both. The `audit` position finding on this data was an info
345
+ finding titled "The data cannot show a position effect in the pairwise
346
+ judgements". It is now a warning titled "Position bias is undefined on
347
+ this data", and it carries the new summary.
348
+
349
+ - **Breaking.** In the both-orders design, `position_bias` formed each
350
+ pair's couple, its first two judgements that are reverses, before looking
351
+ at ties, and dropped the pair if either was a tie. A pair judged more than
352
+ once in an order was dropped for a tied first couple even when a later
353
+ couple had no tie, and the summary did not say so. Ties are now set aside
354
+ first, so the pair is scored on its first two decisive judgements that are
355
+ reverses. On two consistent pairs and a third judged x/y and y/x as ties
356
+ and then x/y for x and y/x for y, it printed "The judge named the same
357
+ output under both orderings on 100.0% of 2 pairs (95% CI: 34.2% to
358
+ 100.0%). It never flipped, so there is no direction to test and nothing
359
+ here points at position." It now reads "The judge named the same output
360
+ under both orderings on 66.7% of 3 pairs (95% CI: 20.8% to 93.9%). Of the
361
+ 1 pair it flipped on, 1 went to whichever output was shown first (100.0%,
362
+ 95% CI: 2.5% to 100.0%, exact binomial p=1.0000)." and goes on to say the
363
+ data cannot show that the flips have a direction. `n_pairs_scored` goes
364
+ from 2 to 3 and `n_decisive` from 0 to 1. A pair judged once in each
365
+ order scores as before, since setting its ties aside leaves a couple
366
+ exactly when neither judgement was a tie. The `audit` position finding
367
+ carries the new figures.
368
+
369
+ - The `rater_agreement` docstring said that without an interval nothing in
370
+ the result would name an outlier rater. Since 0.4.0 the summary lists
371
+ alpha with each rater left out and names no rater either way, and the
372
+ docstring now says so.
373
+
374
+ - The README said scipy computes McNemar's test. It is statsmodels. It also
375
+ said the package builds fifteen intervals, while its own lists named
376
+ seventeen. It now counts eighteen, with the interval on the human baseline
377
+ among the eight pinned against a reference.
378
+
379
+ ### Removed
380
+
381
+ - Nothing.
382
+
383
+ ## [0.4.0] - 2026-09-11
18
384
 
19
- Everything here is headed for 0.4.0, and nearly all of it changes what a
385
+ Nearly everything in 0.4.0 changes what a
20
386
  summary or a report prints. One rule is behind it. A result that does not
21
387
  clear its threshold has failed to show something, and it has not shown the
22
388
  opposite. Summaries that described such a result as if the opposite were
@@ -24,7 +390,8 @@ shown, and verdicts decided on a point estimate where an interval existed,
24
390
  now say what the data did and did not show. Several `audit` findings change
25
391
  severity on data you have already run. Each breaking entry says what it
26
392
  was, what it is now and what to do about it. Every entry was checked by
27
- running the same calls against 0.3.0 from PyPI and against this tree.
393
+ running the same calls against 0.3.0 from PyPI and against the code
394
+ released as 0.4.0.
28
395
 
29
396
  ### Changed
30
397
 
@@ -54,7 +421,7 @@ running the same calls against 0.3.0 from PyPI and against this tree.
54
421
  eval's reach", because without a stated effect the audit does not know
55
422
  what size of difference matters. Its lead says "This reports what the
56
423
  design could find and makes no claim about the margin it measured." On 400
57
- paired items with a 5.0 point margin and an interval of 1.1% to 9.0%,
424
+ paired items with a 5.0-point margin and an interval of 1.1% to 9.0%,
58
425
  0.3.0 opened the report with a `critical` power finding and the verdict
59
426
  said the conclusion did not hold. Two systems with a margin of exactly
60
427
  zero, which 0.3.0 skipped, get the same warning.
@@ -328,8 +695,8 @@ running the same calls against 0.3.0 from PyPI and against this tree.
328
695
  ### Known limitations
329
696
 
330
697
  This rule is unchanged since 0.3.0 and is not fixed in this release. The
331
- figures come from simulations run against 0.3.0 and against this tree, which
332
- give the same results.
698
+ figures come from simulations run against 0.3.0 and against the code released
699
+ as 0.4.0, which give the same results.
333
700
 
334
701
  - `JudgeValidation` names a slice, and `audit` raises a critical finding on
335
702
  it, only when the slice's own interval sits wholly below the interval on
@@ -386,7 +753,7 @@ breaking entry says what it was, what it is now and what to do about it.
386
753
  simulated data shaped like MT-Bench's human judgements, the rating
387
754
  intervals 0.2.1 reported covered 88% of the time at a nominal 95% once
388
755
  prompts shifted model strength by a spread of 0.5 on the log-odds scale,
389
- and 79% at a spread of 1.0. `examples/pairwise_cluster_study.py` has the
756
+ and 80% at a spread of 1.0. `examples/pairwise_cluster_study.py` has the
390
757
  table for 0.3.0.
391
758
 
392
759
  To get the ratings and rating intervals an earlier version gave on the
@@ -494,7 +861,7 @@ breaking entry says what it was, what it is now and what to do about it.
494
861
  - `to_elo` stretches the gap intervals with the gaps and carries `pairs` and
495
862
  `resample` across.
496
863
  - `examples/pairwise_cluster_study.py`, in the source distribution, which
497
- measures how narrow the comparison bootstrap runs on MT-Bench shaped data.
864
+ measures how narrow the comparison bootstrap runs on MT-Bench-shaped data.
498
865
 
499
866
  ### Fixed
500
867
 
@@ -629,7 +996,9 @@ hour after the upload. It reported `__version__` as 0.1.0.
629
996
 
630
997
  - Nothing.
631
998
 
632
- [Unreleased]: https://github.com/mokayahezron/evalaudit/compare/v0.3.0...main
999
+ [0.6.0]: https://github.com/mokayahezron/evalaudit/compare/v0.5.0...v0.6.0
1000
+ [0.5.0]: https://github.com/mokayahezron/evalaudit/compare/v0.4.0...v0.5.0
1001
+ [0.4.0]: https://github.com/mokayahezron/evalaudit/compare/v0.3.0...v0.4.0
633
1002
  [0.3.0]: https://github.com/mokayahezron/evalaudit/compare/809a650...v0.3.0
634
1003
  [0.2.1]: https://github.com/mokayahezron/evalaudit/compare/242e354...809a650
635
1004
  [0.2.0]: https://github.com/mokayahezron/evalaudit/compare/c1230f1...242e354
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Hezron Mokaya
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hezron Mokaya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.