views-frames 1.2.0__tar.gz → 1.3.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 (33) hide show
  1. {views_frames-1.2.0 → views_frames-1.3.0}/PKG-INFO +14 -9
  2. {views_frames-1.2.0 → views_frames-1.3.0}/README.md +13 -8
  3. {views_frames-1.2.0 → views_frames-1.3.0}/pyproject.toml +1 -1
  4. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/config.py +6 -3
  5. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/tower.py +22 -11
  6. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/tower_point.py +6 -5
  7. {views_frames-1.2.0 → views_frames-1.3.0}/.gitignore +0 -0
  8. {views_frames-1.2.0 → views_frames-1.3.0}/LICENSE +0 -0
  9. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/__init__.py +0 -0
  10. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/_typing.py +0 -0
  11. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/_validation.py +0 -0
  12. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/conformance/__init__.py +0 -0
  13. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/feature_frame.py +0 -0
  14. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/index.py +0 -0
  15. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/io/__init__.py +0 -0
  16. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/io/arrow.py +0 -0
  17. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/io/npz.py +0 -0
  18. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/metadata.py +0 -0
  19. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/prediction_frame.py +0 -0
  20. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/protocols.py +0 -0
  21. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/py.typed +0 -0
  22. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/spatial_level.py +0 -0
  23. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames/target_frame.py +0 -0
  24. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/__init__.py +0 -0
  25. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/_common.py +0 -0
  26. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/aggregate.py +0 -0
  27. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/bimodality.py +0 -0
  28. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/collapse.py +0 -0
  29. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/conformance.py +0 -0
  30. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/interval.py +0 -0
  31. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/point.py +0 -0
  32. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/py.typed +0 -0
  33. {views_frames-1.2.0 → views_frames-1.3.0}/src/views_frames_summarize/summarize_tower.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: views-frames
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: The VIEWS platform data-contract layer: immutable array+identifier frames (numpy only, root of the dependency DAG).
5
5
  Project-URL: Homepage, https://github.com/views-platform/views-frames
6
6
  Project-URL: Repository, https://github.com/views-platform/views-frames
@@ -30,9 +30,10 @@ Description-Content-Type: text/markdown
30
30
  > containers (`FeatureFrame`, `PredictionFrame`, and their anticipated siblings)
31
31
  > that every other repo depends on and that depends on nothing internal.
32
32
  >
33
- > **Status:** **v1.2.0 — frozen API** (frozen since v1.0.0, ADR-018; the v1.1 surface is
34
- > purely additive — the coherent posterior summary, ADR-019; v1.2.0 rebuilds the tower
35
- > `outside-in` and makes it config-driven, register C-44). This README is the design
33
+ > **Status:** **v1.3.0 — frozen API** (frozen since v1.0.0, ADR-018; the v1.1 surface is
34
+ > purely additive — the coherent posterior summary, ADR-019; v1.2.0 rebuilt the tower
35
+ > `outside-in`, C-44; v1.3.0 makes the tower summary distribution-agnostic no magnitude
36
+ > zeroing by default, register C-45). This README is the design
36
37
  > bible; the contract it specifies is realised in `src/views_frames/` (index, frames,
37
38
  > io, conformance suite) plus the `src/views_frames_summarize/` sibling package
38
39
  > (sample-axis summarization — `collapse`/MAP/HDI/quantiles, the coherent-tower estimators
@@ -96,16 +97,20 @@ Both are numpy-only. For the subtler cm↔pgm surface — a time-varying
96
97
  `aggregate_distributions` (`HDI(sum) ≠ sum(HDI)`) — see
97
98
  [`examples/cross_level.py`](examples/cross_level.py).
98
99
 
99
- **Which estimator? (two coherent paths, v1.2.0).** Each frozen estimator has a
100
+ **Which estimator? (two coherent paths, v1.3.0).** Each frozen estimator has a
100
101
  coherent-tower sibling (ADR-019): `map_estimate` ↔ `tower_point` (an unbinned
101
102
  median-of-the-`tip_mass`-floor point — the "shorth", free of `map_estimate`'s histogram
102
103
  tie-break bias **and** robust to minority duplicated draws, register C-44), and
103
104
  `hdi`/`quantiles` ↔ `hdi_tower(masses=…)` (HDIs built **outside-in**, nested **by
104
105
  construction** and reproducible — a mass's interval is identical regardless of which others
105
- you request), with `summarize_tower` returning all three in one pass. The tower's tunables
106
- live in `views_frames_summarize.config` (fail-loud, no silent defaults). Use the **frozen**
107
- estimators for parity with existing pipelines; use the **tower** path when you need coherent,
108
- reproducible bands plus a matching, duplicate-robust point.
106
+ you request), with `summarize_tower` returning all three in one pass. The tower path is
107
+ **distribution-agnostic** it works for counts, continuous, normal, and `[0,1]`
108
+ (rate/probability) targets, with **no magnitude zeroing by default** (zero-inflation is read
109
+ off the floor's density, register C-45); a count consumer that wants "sub-1 ⇒ 0" opts in via
110
+ `config['zero_cutoff']`. The tower's tunables live in `views_frames_summarize.config`
111
+ (fail-loud, no silent defaults). Use the **frozen** estimators for parity with existing
112
+ pipelines; use the **tower** path when you need coherent, reproducible bands plus a matching,
113
+ duplicate-robust point.
109
114
 
110
115
  **Reading the `bimodality` flag.** It is a *deliberately conservative* heuristic, **not** a
111
116
  formal multimodality test — tuned for **zero false positives** at the cost of recall on
@@ -4,9 +4,10 @@
4
4
  > containers (`FeatureFrame`, `PredictionFrame`, and their anticipated siblings)
5
5
  > that every other repo depends on and that depends on nothing internal.
6
6
  >
7
- > **Status:** **v1.2.0 — frozen API** (frozen since v1.0.0, ADR-018; the v1.1 surface is
8
- > purely additive — the coherent posterior summary, ADR-019; v1.2.0 rebuilds the tower
9
- > `outside-in` and makes it config-driven, register C-44). This README is the design
7
+ > **Status:** **v1.3.0 — frozen API** (frozen since v1.0.0, ADR-018; the v1.1 surface is
8
+ > purely additive — the coherent posterior summary, ADR-019; v1.2.0 rebuilt the tower
9
+ > `outside-in`, C-44; v1.3.0 makes the tower summary distribution-agnostic no magnitude
10
+ > zeroing by default, register C-45). This README is the design
10
11
  > bible; the contract it specifies is realised in `src/views_frames/` (index, frames,
11
12
  > io, conformance suite) plus the `src/views_frames_summarize/` sibling package
12
13
  > (sample-axis summarization — `collapse`/MAP/HDI/quantiles, the coherent-tower estimators
@@ -70,16 +71,20 @@ Both are numpy-only. For the subtler cm↔pgm surface — a time-varying
70
71
  `aggregate_distributions` (`HDI(sum) ≠ sum(HDI)`) — see
71
72
  [`examples/cross_level.py`](examples/cross_level.py).
72
73
 
73
- **Which estimator? (two coherent paths, v1.2.0).** Each frozen estimator has a
74
+ **Which estimator? (two coherent paths, v1.3.0).** Each frozen estimator has a
74
75
  coherent-tower sibling (ADR-019): `map_estimate` ↔ `tower_point` (an unbinned
75
76
  median-of-the-`tip_mass`-floor point — the "shorth", free of `map_estimate`'s histogram
76
77
  tie-break bias **and** robust to minority duplicated draws, register C-44), and
77
78
  `hdi`/`quantiles` ↔ `hdi_tower(masses=…)` (HDIs built **outside-in**, nested **by
78
79
  construction** and reproducible — a mass's interval is identical regardless of which others
79
- you request), with `summarize_tower` returning all three in one pass. The tower's tunables
80
- live in `views_frames_summarize.config` (fail-loud, no silent defaults). Use the **frozen**
81
- estimators for parity with existing pipelines; use the **tower** path when you need coherent,
82
- reproducible bands plus a matching, duplicate-robust point.
80
+ you request), with `summarize_tower` returning all three in one pass. The tower path is
81
+ **distribution-agnostic** it works for counts, continuous, normal, and `[0,1]`
82
+ (rate/probability) targets, with **no magnitude zeroing by default** (zero-inflation is read
83
+ off the floor's density, register C-45); a count consumer that wants "sub-1 ⇒ 0" opts in via
84
+ `config['zero_cutoff']`. The tower's tunables live in `views_frames_summarize.config`
85
+ (fail-loud, no silent defaults). Use the **frozen** estimators for parity with existing
86
+ pipelines; use the **tower** path when you need coherent, reproducible bands plus a matching,
87
+ duplicate-robust point.
83
88
 
84
89
  **Reading the `bimodality` flag.** It is a *deliberately conservative* heuristic, **not** a
85
90
  formal multimodality test — tuned for **zero false positives** at the cost of recall on
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "views-frames"
7
- version = "1.2.0"
7
+ version = "1.3.0"
8
8
  description = "The VIEWS platform data-contract layer: immutable array+identifier frames (numpy only, root of the dependency DAG)."
9
9
  authors = [
10
10
  { name = "Simon Polichinel von der Maase", email = "simmaa@prio.org" },
@@ -31,9 +31,12 @@ TOWER_CONFIG: Final[dict[str, Any]] = {
31
31
  # The tower-tip reads the median of the floor at this mass — the "shorth". 0.5 is
32
32
  # the maximally-robust choice: a duplicate would need ~half the draws to hijack it.
33
33
  "tip_mass": 0.5,
34
- # Raw-count zero rule: a row whose every draw is <= this collapses to 0 (the quiet
35
- # cell). A *count* rule, distinct from ``map_estimate``'s zero-mass-fraction rule.
36
- "zero_cutoff": 1.0,
34
+ # Optional magnitude zero rule, OFF by default (``None``). When set to a float, a
35
+ # row whose every draw is <= it collapses to 0 — a *count* opt-in (sub-1 ⇒ 0).
36
+ # Left ``None`` for any non-count target: zero-inflation is otherwise handled by the
37
+ # density of the ``tip_mass`` floor, not by magnitude (register C-45). The leaf
38
+ # imposes no magnitude assumption by default; a consumer that wants it sets this.
39
+ "zero_cutoff": None,
37
40
  # Bimodality detector (coarse histogram + light smoothing; see bimodality.py).
38
41
  "bimodality_bins": 16,
39
42
  "bimodality_prominence": 0.40,
@@ -13,9 +13,10 @@ nearest floor, never inserted — "the 50% HDI" is identical regardless of which
13
13
  masses a caller asks for (reproducibility).
14
14
 
15
15
  The construction is vectorized over the sample axis and runs in row-blocks (register
16
- C-22/C-25): one sort per block, never a whole-grid sorted copy. A raw-count zero
17
- short-circuit (`max(row) <= zero_cutoff` the whole summary collapses to 0) kills the
18
- quiet cells cheaply.
16
+ C-22/C-25): one sort per block, never a whole-grid sorted copy. Zero-inflation is read
17
+ off the *density* of the `tip_mass` floor (a zero-majority row reads 0); an
18
+ **optional, off-by-default** magnitude cutoff (`config['zero_cutoff']`, a count-domain
19
+ opt-in) can additionally force `max(row) <= cutoff` rows to 0 (C-45).
19
20
 
20
21
  All tunables (the grid, zero cutoff, row-block size) come from `config` with **no silent
21
22
  defaults** (ADR-009). The private engine (`_dense_tower`, `_median_in`, `_pin`,
@@ -34,11 +35,10 @@ from numpy.typing import NDArray
34
35
  from views_frames_summarize import config
35
36
  from views_frames_summarize._common import AnyFrame, block_apply
36
37
 
37
- # The fixed canonical mass grid + the zero cutoff, sourced from the single config (no
38
- # silent defaults — ADR-009). Kept as module names so the engine and its siblings share
39
- # one source of truth.
38
+ # The fixed canonical mass grid, sourced from the single config (no silent defaults —
39
+ # ADR-009). The grid is reproducibility-fixed by design (ADR-019), so it is snapshotted
40
+ # here; the *tunable* knobs (``zero_cutoff``, ``tip_mass``) are read live at call time.
40
41
  _CANONICAL_FLOORS: Final[NDArray[np.float64]] = config.canonical_floors()
41
- _ZERO_CUTOFF: Final[float] = float(config.get("zero_cutoff"))
42
42
 
43
43
 
44
44
  def _ks(sample_count: int) -> NDArray[np.intp]:
@@ -65,8 +65,18 @@ def _pin(masses: Sequence[float]) -> NDArray[np.intp]:
65
65
 
66
66
 
67
67
  def _zero_mask(block: NDArray[np.float32]) -> NDArray[np.bool_]:
68
- """Rows whose maximum draw is at/below the zero cutoff the quiet cells."""
69
- return np.asarray(block.max(axis=-1) <= _ZERO_CUTOFF, dtype=np.bool_)
68
+ """Rows the optional magnitude cutoff zeroes``max(draws) <= zero_cutoff``.
69
+
70
+ **Off by default** (``zero_cutoff is None`` ⇒ no row is masked). A consumer that
71
+ wants magnitude zeroing (a count target where a sub-1 posterior should read 0) sets
72
+ ``zero_cutoff`` to a float in ``config`` — read **live** here, so the knob takes
73
+ effect at runtime. By default the leaf imposes **no** magnitude assumption; zero-
74
+ inflation is handled by the density of the ``tip_mass`` floor instead (C-45).
75
+ """
76
+ cutoff = config.get("zero_cutoff")
77
+ if cutoff is None:
78
+ return np.zeros(block.shape[0], dtype=np.bool_)
79
+ return np.asarray(block.max(axis=-1) <= cutoff, dtype=np.bool_)
70
80
 
71
81
 
72
82
  def _in_range_span(
@@ -203,8 +213,9 @@ def hdi_tower(
203
213
  Each requested mass is pinned to the nearest fixed canonical floor; the interval is
204
214
  read out of the full canonical tower (built once per block, outside-in). Nested by
205
215
  construction, robust to minority duplicates, and reproducible (a mass's interval is
206
- independent of the other requested masses). Quiet rows (``max <= zero_cutoff``)
207
- collapse to ``(0, 0)``. Aligned to ``frame.index``. Tunables come from ``config``.
216
+ independent of the other requested masses). If the optional ``zero_cutoff`` is set,
217
+ ``max <= cutoff`` rows collapse to ``(0, 0)`` (off by default C-45). Aligned to
218
+ ``frame.index``. Tunables come from ``config``.
208
219
  """
209
220
  values = frame.values
210
221
  lead = values.shape[:-1]
@@ -2,8 +2,8 @@
2
2
 
3
3
  `tower_point` is the "most likely single value" we report to a consumer: the median of
4
4
  the draws inside the **`tip_mass` floor** of the nested tower (config-driven, default
5
- 0.5 — the "shorth"), with the same raw-count zero short-circuit as the tower
6
- (`max(row) <= zero_cutoff` 0).
5
+ 0.5 — the "shorth"). Zero-inflation is handled by that floor's density (a zero-majority
6
+ row reads 0); the optional, off-by-default magnitude cutoff applies too if set (C-45).
7
7
 
8
8
  It is a *new* estimator, deliberately distinct from the frozen `map_estimate` (ADR-018):
9
9
  `map_estimate` is a binned histogram mode with a zero-*mass*-fraction rule and a
@@ -34,9 +34,10 @@ from views_frames_summarize.tower import (
34
34
  def tower_point(frame: AnyFrame) -> AnyFrame:
35
35
  """Per-row tower-tip point estimate over the sample axis → a `(N, …, 1)` frame.
36
36
 
37
- The median of the ``tip_mass`` floor's samples; ``0.0`` for quiet rows (every draw
38
- ``<= zero_cutoff``). Returns a frame of the same type as ``frame``. Tunables (the
39
- ``tip_mass``, the grid, the zero cutoff, the row-block) come from ``config``.
37
+ The median of the ``tip_mass`` floor's samples (``0`` when the zero atom dominates,
38
+ by density); if the optional ``zero_cutoff`` is set, ``max <= cutoff`` rows are
39
+ forced to ``0`` (off by default C-45). Returns the same frame type as ``frame``.
40
+ Tunables (``tip_mass``, grid, ``zero_cutoff``, row-block) come from ``config``.
40
41
  """
41
42
  values = frame.values
42
43
  lead = values.shape[:-1]
File without changes
File without changes