views-frames 1.11.0__tar.gz → 2.0.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.
- {views_frames-1.11.0 → views_frames-2.0.0}/PKG-INFO +15 -10
- {views_frames-1.11.0 → views_frames-2.0.0}/README.md +14 -9
- {views_frames-1.11.0 → views_frames-2.0.0}/pyproject.toml +1 -1
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/conformance/__init__.py +7 -2
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/feature_frame.py +11 -1
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/index.py +15 -1
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/prediction_frame.py +11 -1
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/target_frame.py +11 -1
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/conformance.py +18 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/point.py +8 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/.gitignore +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/LICENSE +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/__init__.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/_typing.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/_validation.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/io/__init__.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/io/arrow.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/io/npz.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/metadata.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/protocols.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/py.typed +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames/spatial_level.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_reconcile/__init__.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_reconcile/conformance.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_reconcile/frames.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_reconcile/grouping.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_reconcile/module.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_reconcile/proportional.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_reconcile/py.typed +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_reconcile/result.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_reconcile/validation.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/__init__.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/_common.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/aggregate.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/bimodality.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/collapse.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/config.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/exceedance.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/expected_shortfall.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/interval.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/py.typed +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/summarize_tower.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/tower.py +0 -0
- {views_frames-1.11.0 → views_frames-2.0.0}/src/views_frames_summarize/tower_point.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: views-frames
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.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
|
|
@@ -36,7 +36,11 @@ Description-Content-Type: text/markdown
|
|
|
36
36
|
> containers (`FeatureFrame`, `PredictionFrame`, and their anticipated siblings)
|
|
37
37
|
> that every other repo depends on and that depends on nothing internal.
|
|
38
38
|
>
|
|
39
|
-
> **Status:** **
|
|
39
|
+
> **Status:** **v2.0.0 — the version in this tree.** For what is actually published, see
|
|
40
|
+
> [PyPI](https://pypi.org/project/views-frames/): between a release commit and its publish
|
|
41
|
+
> job this number is deliberately ahead, so this banner tracks the source and never claims
|
|
42
|
+
> a publication it cannot verify. API frozen at v1.0.0 (ADR-018) and broken once in 2.0.0
|
|
43
|
+
> (ADR-028; the
|
|
40
44
|
> v1.1 surface is
|
|
41
45
|
> purely additive — the coherent posterior summary, ADR-019; v1.2.0 rebuilt the tower
|
|
42
46
|
> `outside-in`, C-44; v1.3.0 makes the tower summary distribution-agnostic — no magnitude
|
|
@@ -53,7 +57,7 @@ Description-Content-Type: text/markdown
|
|
|
53
57
|
> `research/figures/` tower-figure toolkit; v1.10.0 adds the **dense-grid fill**
|
|
54
58
|
> primitive — `reindex_fill(other, *, fill_value)` on all three frames +
|
|
55
59
|
> `SpatioTemporalIndex.cartesian` + the published `assert_reindex_fill_law`, ADR-026,
|
|
56
|
-
> unblocking pandas-free FAO ingestion; v1.10.1 makes `io.arrow.load` fail loudly on a parquet whose row order breaks the wire contract, #199; v1.10.2 changes no behaviour — it arms the documentation check in CI, turns eleven falsification tests from README-wording checks into behaviour checks, and records ADR-027 declining the construction shortcut; v1.11.0 corrects the published MAP-containment law, which was wrong on tied draws and so failed ~6% of rows on integer count posteriors — register C-88 — and makes the two IO codecs agree on a non-JSON metadata value, C-90; the rest of that release makes the governance documents match the code and adds the CI checks that keep them matching). This
|
|
60
|
+
> unblocking pandas-free FAO ingestion; v1.10.1 makes `io.arrow.load` fail loudly on a parquet whose row order breaks the wire contract, #199; v1.10.2 changes no behaviour — it arms the documentation check in CI, turns eleven falsification tests from README-wording checks into behaviour checks, and records ADR-027 declining the construction shortcut; v1.11.0 corrects the published MAP-containment law, which was wrong on tied draws and so failed ~6% of rows on integer count posteriors — register C-88 — and makes the two IO codecs agree on a non-JSON metadata value, C-90; the rest of that release makes the governance documents match the code and adds the CI checks that keep them matching; **v2.0.0 is the first MAJOR** — a falsification audit found that a frame's `index` was never type-checked, so a frame (or any object with an `n_rows`) was accepted silently and the published `assert_summarizer_contract` then certified the result, and it now raises `TypeError`; riding it, `frame.values` becomes read-only (C-66) and `map_estimate` rejects non-finite draws (C-57), and `CONFORMANCE_FLOOR` moves to `2.0.0` — a correct consumer changes its version constraint and re-locks, nothing else, ADR-028). This
|
|
57
61
|
> README is the design
|
|
58
62
|
> bible; the contract it specifies is realised in `src/views_frames/` (index, frames,
|
|
59
63
|
> io, conformance suite) plus the `src/views_frames_summarize/` sibling package
|
|
@@ -265,13 +269,14 @@ makes it safe to depend on from everywhere (SDP).
|
|
|
265
269
|
3. **Immutable value objects.** A frame is validated at construction and then
|
|
266
270
|
treated as read-only. Operations (`collapse`, `select`, `with_metadata`)
|
|
267
271
|
**return new frames**; they never mutate in place. (Directly forbids the
|
|
268
|
-
C-184 cross-repo-mutation anti-pattern.) **Enforced for the index,
|
|
269
|
-
|
|
270
|
-
write-protected (`setflags(write=False)`); the `values` buffer
|
|
271
|
-
**writeable on purpose** — so structural ops can share it zero-copy — and
|
|
272
|
-
immutable *by convention
|
|
273
|
-
|
|
274
|
-
|
|
272
|
+
C-184 cross-repo-mutation anti-pattern.) **Enforced for the index, and since 2.0.0
|
|
273
|
+
for the value buffer too:** the identifier arrays (`time`/`unit`) are
|
|
274
|
+
write-protected (`setflags(write=False)`); the `values` buffer was left
|
|
275
|
+
**writeable on purpose** — so structural ops can share it zero-copy — and was
|
|
276
|
+
immutable *by convention* **until 2.0.0**, which enforced it: `values` is now a
|
|
277
|
+
read-only **view**, so an in-place mutation raises instead of silently corrupting
|
|
278
|
+
buffer-sharing frames, while zero-copy, mmap and the caller's own array are all
|
|
279
|
+
preserved (ADR-028 / register C-66; the convention-only era is ADR-025 / C-63). **Copy-vs-view:** structural and
|
|
275
280
|
metadata-only operations (`with_metadata`, contiguous `select`) return frames
|
|
276
281
|
that **share** the underlying `values` buffer (numpy view / zero-copy), and a
|
|
277
282
|
`mmap`-backed frame stays `mmap`-backed — a new frame must never copy a
|
|
@@ -4,7 +4,11 @@
|
|
|
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:** **
|
|
7
|
+
> **Status:** **v2.0.0 — the version in this tree.** For what is actually published, see
|
|
8
|
+
> [PyPI](https://pypi.org/project/views-frames/): between a release commit and its publish
|
|
9
|
+
> job this number is deliberately ahead, so this banner tracks the source and never claims
|
|
10
|
+
> a publication it cannot verify. API frozen at v1.0.0 (ADR-018) and broken once in 2.0.0
|
|
11
|
+
> (ADR-028; the
|
|
8
12
|
> v1.1 surface is
|
|
9
13
|
> purely additive — the coherent posterior summary, ADR-019; v1.2.0 rebuilt the tower
|
|
10
14
|
> `outside-in`, C-44; v1.3.0 makes the tower summary distribution-agnostic — no magnitude
|
|
@@ -21,7 +25,7 @@
|
|
|
21
25
|
> `research/figures/` tower-figure toolkit; v1.10.0 adds the **dense-grid fill**
|
|
22
26
|
> primitive — `reindex_fill(other, *, fill_value)` on all three frames +
|
|
23
27
|
> `SpatioTemporalIndex.cartesian` + the published `assert_reindex_fill_law`, ADR-026,
|
|
24
|
-
> unblocking pandas-free FAO ingestion; v1.10.1 makes `io.arrow.load` fail loudly on a parquet whose row order breaks the wire contract, #199; v1.10.2 changes no behaviour — it arms the documentation check in CI, turns eleven falsification tests from README-wording checks into behaviour checks, and records ADR-027 declining the construction shortcut; v1.11.0 corrects the published MAP-containment law, which was wrong on tied draws and so failed ~6% of rows on integer count posteriors — register C-88 — and makes the two IO codecs agree on a non-JSON metadata value, C-90; the rest of that release makes the governance documents match the code and adds the CI checks that keep them matching). This
|
|
28
|
+
> unblocking pandas-free FAO ingestion; v1.10.1 makes `io.arrow.load` fail loudly on a parquet whose row order breaks the wire contract, #199; v1.10.2 changes no behaviour — it arms the documentation check in CI, turns eleven falsification tests from README-wording checks into behaviour checks, and records ADR-027 declining the construction shortcut; v1.11.0 corrects the published MAP-containment law, which was wrong on tied draws and so failed ~6% of rows on integer count posteriors — register C-88 — and makes the two IO codecs agree on a non-JSON metadata value, C-90; the rest of that release makes the governance documents match the code and adds the CI checks that keep them matching; **v2.0.0 is the first MAJOR** — a falsification audit found that a frame's `index` was never type-checked, so a frame (or any object with an `n_rows`) was accepted silently and the published `assert_summarizer_contract` then certified the result, and it now raises `TypeError`; riding it, `frame.values` becomes read-only (C-66) and `map_estimate` rejects non-finite draws (C-57), and `CONFORMANCE_FLOOR` moves to `2.0.0` — a correct consumer changes its version constraint and re-locks, nothing else, ADR-028). This
|
|
25
29
|
> README is the design
|
|
26
30
|
> bible; the contract it specifies is realised in `src/views_frames/` (index, frames,
|
|
27
31
|
> io, conformance suite) plus the `src/views_frames_summarize/` sibling package
|
|
@@ -233,13 +237,14 @@ makes it safe to depend on from everywhere (SDP).
|
|
|
233
237
|
3. **Immutable value objects.** A frame is validated at construction and then
|
|
234
238
|
treated as read-only. Operations (`collapse`, `select`, `with_metadata`)
|
|
235
239
|
**return new frames**; they never mutate in place. (Directly forbids the
|
|
236
|
-
C-184 cross-repo-mutation anti-pattern.) **Enforced for the index,
|
|
237
|
-
|
|
238
|
-
write-protected (`setflags(write=False)`); the `values` buffer
|
|
239
|
-
**writeable on purpose** — so structural ops can share it zero-copy — and
|
|
240
|
-
immutable *by convention
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
C-184 cross-repo-mutation anti-pattern.) **Enforced for the index, and since 2.0.0
|
|
241
|
+
for the value buffer too:** the identifier arrays (`time`/`unit`) are
|
|
242
|
+
write-protected (`setflags(write=False)`); the `values` buffer was left
|
|
243
|
+
**writeable on purpose** — so structural ops can share it zero-copy — and was
|
|
244
|
+
immutable *by convention* **until 2.0.0**, which enforced it: `values` is now a
|
|
245
|
+
read-only **view**, so an in-place mutation raises instead of silently corrupting
|
|
246
|
+
buffer-sharing frames, while zero-copy, mmap and the caller's own array are all
|
|
247
|
+
preserved (ADR-028 / register C-66; the convention-only era is ADR-025 / C-63). **Copy-vs-view:** structural and
|
|
243
248
|
metadata-only operations (`with_metadata`, contiguous `select`) return frames
|
|
244
249
|
that **share** the underlying `values` buffer (numpy view / zero-copy), and a
|
|
245
250
|
`mmap`-backed frame stays `mmap`-backed — a new frame must never copy a
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "views-frames"
|
|
7
|
-
version = "
|
|
7
|
+
version = "2.0.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" },
|
|
@@ -19,7 +19,8 @@ the envelope has one written authority rather than re-asserted copies that drift
|
|
|
19
19
|
assert_frame_envelope(my_metric_frame())
|
|
20
20
|
|
|
21
21
|
The floor is governed in ``GOVERNANCE.md``; ``CONFORMANCE_FLOOR`` records the version
|
|
22
|
-
this suite belongs to.
|
|
22
|
+
this suite belongs to. It moved to ``2.0.0`` with the 2.0.0 MAJOR (ADR-028) — the first
|
|
23
|
+
move since the v1.0.0 freeze.
|
|
23
24
|
"""
|
|
24
25
|
|
|
25
26
|
from __future__ import annotations
|
|
@@ -29,7 +30,11 @@ from typing import Any
|
|
|
29
30
|
|
|
30
31
|
import numpy as np
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
# Bumped to 2.0.0 on 2026-08-18 — the FIRST move since the freeze. `assert_summarizer_
|
|
34
|
+
# contract` now rejects a frame whose index is not an index, which narrows what the
|
|
35
|
+
# published suite accepts, and GOVERNANCE bumps the floor on any breaking change to any
|
|
36
|
+
# published entry point. A consumer reading this learns which contract its CI asserts.
|
|
37
|
+
CONFORMANCE_FLOOR = "2.0.0"
|
|
33
38
|
|
|
34
39
|
__all__ = [
|
|
35
40
|
"CONFORMANCE_FLOOR",
|
|
@@ -41,6 +41,10 @@ class FeatureFrame:
|
|
|
41
41
|
f"trailing sample axis (ADR-012), got ndim={values.ndim}. "
|
|
42
42
|
"Use FeatureFrame.from_2d to build a frame from a 2-D (N, F) array."
|
|
43
43
|
)
|
|
44
|
+
if not isinstance(index, SpatioTemporalIndex):
|
|
45
|
+
raise TypeError(
|
|
46
|
+
f"index must be a SpatioTemporalIndex, got {type(index).__name__}"
|
|
47
|
+
)
|
|
44
48
|
if values.shape[0] != index.n_rows:
|
|
45
49
|
raise ValueError(
|
|
46
50
|
f"y_features has {values.shape[0]} rows but index has {index.n_rows}"
|
|
@@ -50,7 +54,13 @@ class FeatureFrame:
|
|
|
50
54
|
f"feature_names length ({len(feature_names)}) must match the "
|
|
51
55
|
f"feature axis ({values.shape[1]})"
|
|
52
56
|
)
|
|
53
|
-
|
|
57
|
+
# A read-only *view*, not `values.setflags(write=False)` (register C-66).
|
|
58
|
+
# `coerce_values` returns the caller's own array when it is already float32
|
|
59
|
+
# (the C-07 zero-copy guarantee), so locking it in place would silently make
|
|
60
|
+
# the CALLER's array read-only — action at a distance well outside this
|
|
61
|
+
# contract. The view shares the buffer, so zero-copy and mmap are preserved.
|
|
62
|
+
self._values: NDArray[np.float32] = values.view()
|
|
63
|
+
self._values.setflags(write=False)
|
|
54
64
|
self._index = index
|
|
55
65
|
self._feature_names = list(feature_names)
|
|
56
66
|
self._metadata = metadata if metadata is not None else FrameMetadata()
|
|
@@ -47,7 +47,11 @@ class SpatioTemporalIndex:
|
|
|
47
47
|
is_array = isinstance(time, np.ndarray) and time.ndim >= 1
|
|
48
48
|
n = int(time.shape[0]) if is_array else -1
|
|
49
49
|
validate_identifiers({"time": time, "unit": unit}, n_rows=n)
|
|
50
|
-
#
|
|
50
|
+
# Write-protect the identifier arrays so the value object cannot be mutated
|
|
51
|
+
# in place. Note `ascontiguousarray` returns the *same* array when the input
|
|
52
|
+
# is already contiguous, so this also write-protects the caller's array. That
|
|
53
|
+
# is long-standing index behaviour, deliberately left alone here; the frames
|
|
54
|
+
# take a read-only view instead, for the reason given in their constructors.
|
|
51
55
|
self._time = np.ascontiguousarray(time)
|
|
52
56
|
self._unit = np.ascontiguousarray(unit)
|
|
53
57
|
self._time.setflags(write=False)
|
|
@@ -173,6 +177,16 @@ class SpatioTemporalIndex:
|
|
|
173
177
|
)
|
|
174
178
|
|
|
175
179
|
def _require_same_level(self, other: SpatioTemporalIndex) -> None:
|
|
180
|
+
# Type first. Every same-level binary op (`searchsorted`/`reindex`,
|
|
181
|
+
# `is_superset_of`, `intersect`) routes through here, so this one guard is
|
|
182
|
+
# what stops a non-index argument reaching `other._level` and leaking
|
|
183
|
+
# `AttributeError: '...' object has no attribute '_level'` — a private
|
|
184
|
+
# attribute of a class the caller never named. ADR-008 requires a
|
|
185
|
+
# ValueError/TypeError at every validation guard (falsify audit 2026-08-18).
|
|
186
|
+
if not isinstance(other, SpatioTemporalIndex):
|
|
187
|
+
raise TypeError(
|
|
188
|
+
f"expected a SpatioTemporalIndex, got {type(other).__name__}"
|
|
189
|
+
)
|
|
176
190
|
if self._level != other._level:
|
|
177
191
|
raise ValueError(
|
|
178
192
|
"same-level operation requires equal SpatialLevel; "
|
|
@@ -37,11 +37,21 @@ class PredictionFrame:
|
|
|
37
37
|
raise ValueError(
|
|
38
38
|
f"PredictionFrame y_pred must be 2D (N, S), got ndim={values.ndim}"
|
|
39
39
|
)
|
|
40
|
+
if not isinstance(index, SpatioTemporalIndex):
|
|
41
|
+
raise TypeError(
|
|
42
|
+
f"index must be a SpatioTemporalIndex, got {type(index).__name__}"
|
|
43
|
+
)
|
|
40
44
|
if values.shape[0] != index.n_rows:
|
|
41
45
|
raise ValueError(
|
|
42
46
|
f"y_pred has {values.shape[0]} rows but index has {index.n_rows}"
|
|
43
47
|
)
|
|
44
|
-
|
|
48
|
+
# A read-only *view*, not `values.setflags(write=False)` (register C-66).
|
|
49
|
+
# `coerce_values` returns the caller's own array when it is already float32
|
|
50
|
+
# (the C-07 zero-copy guarantee), so locking it in place would silently make
|
|
51
|
+
# the CALLER's array read-only — action at a distance well outside this
|
|
52
|
+
# contract. The view shares the buffer, so zero-copy and mmap are preserved.
|
|
53
|
+
self._values: NDArray[np.float32] = values.view()
|
|
54
|
+
self._values.setflags(write=False)
|
|
45
55
|
self._index = index
|
|
46
56
|
self._metadata = metadata if metadata is not None else FrameMetadata()
|
|
47
57
|
|
|
@@ -36,11 +36,21 @@ class TargetFrame:
|
|
|
36
36
|
"TargetFrame y_true must have shape (N, 1) with an explicit "
|
|
37
37
|
f"trailing axis (ADR-012), got shape {values.shape}"
|
|
38
38
|
)
|
|
39
|
+
if not isinstance(index, SpatioTemporalIndex):
|
|
40
|
+
raise TypeError(
|
|
41
|
+
f"index must be a SpatioTemporalIndex, got {type(index).__name__}"
|
|
42
|
+
)
|
|
39
43
|
if values.shape[0] != index.n_rows:
|
|
40
44
|
raise ValueError(
|
|
41
45
|
f"y_true has {values.shape[0]} rows but index has {index.n_rows}"
|
|
42
46
|
)
|
|
43
|
-
|
|
47
|
+
# A read-only *view*, not `values.setflags(write=False)` (register C-66).
|
|
48
|
+
# `coerce_values` returns the caller's own array when it is already float32
|
|
49
|
+
# (the C-07 zero-copy guarantee), so locking it in place would silently make
|
|
50
|
+
# the CALLER's array read-only — action at a distance well outside this
|
|
51
|
+
# contract. The view shares the buffer, so zero-copy and mmap are preserved.
|
|
52
|
+
self._values: NDArray[np.float32] = values.view()
|
|
53
|
+
self._values.setflags(write=False)
|
|
44
54
|
self._index = index
|
|
45
55
|
self._metadata = metadata if metadata is not None else FrameMetadata()
|
|
46
56
|
|
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
|
|
12
|
+
from views_frames import SpatioTemporalIndex
|
|
12
13
|
from views_frames_summarize import config
|
|
13
14
|
from views_frames_summarize._common import AnyFrame
|
|
14
15
|
from views_frames_summarize.bimodality import bimodality
|
|
@@ -44,6 +45,23 @@ def assert_summarizer_contract(frame: AnyFrame) -> None:
|
|
|
44
45
|
AssertionError: a summarizer violates its output contract.
|
|
45
46
|
"""
|
|
46
47
|
_require_assertions()
|
|
48
|
+
|
|
49
|
+
# The frame must be structurally sound before any law about it means anything.
|
|
50
|
+
# Every summarizer below reads `frame.values` and `frame.n_rows` and none reads
|
|
51
|
+
# `frame.index`, so before 2.0.0 this suite returned cleanly for a frame whose
|
|
52
|
+
# index was not an index at all — a published checker issuing a false pass, which
|
|
53
|
+
# `docs/CICs/Conformance.md` names as the failure mode this module must never
|
|
54
|
+
# have (falsify audit 2026-08-18).
|
|
55
|
+
#
|
|
56
|
+
# Construction now rejects such a frame, so this is unreachable through the
|
|
57
|
+
# ordinary constructors. It is kept because consumers run this suite against
|
|
58
|
+
# *their own* frame factories under ADR-016, and `with_metadata` already builds
|
|
59
|
+
# frames through `Frame.__new__` rather than `__init__` — so a bypass path exists
|
|
60
|
+
# today and more may be added.
|
|
61
|
+
assert isinstance(frame.index, SpatioTemporalIndex), (
|
|
62
|
+
"frame.index must be a SpatioTemporalIndex, got "
|
|
63
|
+
f"{type(frame.index).__name__} — the frame misreports itself"
|
|
64
|
+
)
|
|
47
65
|
n = frame.n_rows
|
|
48
66
|
|
|
49
67
|
point = collapse(frame, np.mean)
|
|
@@ -44,6 +44,14 @@ def map_estimate(
|
|
|
44
44
|
on such distributions; a robust mode estimator is tracked separately (#89).
|
|
45
45
|
"""
|
|
46
46
|
values = frame.values
|
|
47
|
+
if not bool(np.isfinite(values).all()):
|
|
48
|
+
raise ValueError(
|
|
49
|
+
"map_estimate is undefined on non-finite draws (NaN or ±inf); strip or "
|
|
50
|
+
"impute upstream. Until 2.0.0 an inf draw reached the histogram and "
|
|
51
|
+
"crashed with an obscure IndexError from an integer overflow in the bin "
|
|
52
|
+
"index — loud, but it named neither the cause nor the caller. This is the "
|
|
53
|
+
"same guard exceedance and expected_shortfall carry (register C-57/C-50)"
|
|
54
|
+
)
|
|
47
55
|
lead = values.shape[:-1]
|
|
48
56
|
s = values.shape[-1]
|
|
49
57
|
# Bin in the input dtype, exactly as the v0.2.0 per-row np.histogram did —
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|