Skip to content

fix(derivations): evaluate() returns the no-spread result for every bin method on a constant column - #120

Merged
cnicholas merged 1 commit into
mainfrom
fix/derivations-constant-column
Sep 11, 2026
Merged

fix(derivations): evaluate() returns the no-spread result for every bin method on a constant column#120
cnicholas merged 1 commit into
mainfrom
fix/derivations-constant-column

Conversation

@cnicholas

Copy link
Copy Markdown
Owner

Summary

  • What: the no-spread guard in _evaluate_bin now also treats non-strictly-increasing fitted edges as degenerate, so all three automatic bin methods return the documented no-spread result on a constant column.
  • Why: equal_freq already returned it (np.unique collapses the quantiles to one edge). equal_width fitted identical finite edges and sd fitted a zero-sigma edge set; both passed the guard and pd.cut raised ValueError: Bin edges must be unique. The module docstring promises evaluate never raises on a routine state, and a constant column is one. Found through the app on a single-time-point file whose TIME column was all 1.
  • Scope: one guard in derivations.py, the no-spread fitted dict shape, three tests, CHANGELOG.

Contract / invariants

  • Defaults unchanged — non-degenerate fits are byte-identical: edges, labels, and the nextafter top-edge widening are untouched. validation/e2e_bishop_report.py exits 0.
  • Residuals unaffected — derivations only.
  • Row/index alignment preserved — the no-spread result already had the input's index; unchanged.
  • Output schema compatible — the no-spread fitted dict gains n_bins: 0, edges: [], labels: [] (additive; validate() reads n_bins with .get).
  • Pinned error strings unchanged'column has no spread; cannot bin' is unchanged.

Behaviour changes

  • evaluate() returns instead of raising for equal_width / sd on a constant column.
  • The no-spread fitted dict carries a consistent shape. Explicit breaks on a constant column still bin (user cut points are strictly increasing; everything lands in one interval), covered by a test.

Methodology

  • No methodology change — implementation only.

Tests

  • test_constant_column_is_no_spread_for_every_method (parametrized over equal_freq, equal_width, sd) — no raise, no-spread message, zero fitted bins, all-NA categorical with no categories.

  • test_constant_column_with_explicit_breaks_still_bins — breaks are unaffected.

  • test_constant_column_validates_without_raisingvalidate() calls evaluate() internally and must survive too.

  • pytest tests/ — 2340 passed, 10 skipped

  • ruff check . — clean

  • mypy processbehavior/derivations.py — clean

  • Golden masters untouched

Notes

The companion app fix (the preview panel must not take the page down on empty counts) is separate and works against the 0.2.0 pin; this change makes the library side honest regardless of caller.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BKphCi1LC1hjGYteWEK9JV

…in method on a constant column

## Summary
- **What:** the no-spread guard in _evaluate_bin now also treats non-strictly-increasing
  edges as degenerate.
- **Why:** equal_width and sd on a constant column fitted identical finite edges, passed the
  guard, and pd.cut raised "Bin edges must be unique". evaluate() promises never to raise on
  a routine state. Found via the app on a T=1 file with a constant TIME column.
- **Scope:** derivations.py guard + fitted shape; tests; CHANGELOG.

## Contract / Invariants (must remain true)
- Non-degenerate fits unchanged: edges, labels, nextafter top-edge widening untouched.
- breaks on any column unchanged (user cut points are strictly increasing).
- No chart math; validation/e2e_bishop_report.py exit 0, 280/280.

## Behavior Changes (explicit)
- evaluate() returns instead of raising for equal_width / sd on a constant column.
- The no-spread fitted dict gains n_bins: 0, edges: [], labels: [].

## Tests
- test_constant_column_is_no_spread_for_every_method (parametrized over the three methods)
- test_constant_column_with_explicit_breaks_still_bins
- test_constant_column_validates_without_raising

## Manual Verification
- pytest tests/: 2340 passed, 10 skipped
- ruff check: clean; mypy processbehavior/derivations.py: clean
- validation/e2e_bishop_report.py: exit 0
@cnicholas
cnicholas merged commit daf42ee into main Sep 11, 2026
17 checks passed
@cnicholas
cnicholas deleted the fix/derivations-constant-column branch September 11, 2026 11:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant