dataeval-plots 0.0.11__tar.gz → 0.0.12__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 (17) hide show
  1. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/PKG-INFO +3 -3
  2. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/pyproject.toml +11 -3
  3. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/_version.py +2 -2
  4. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/backends/_shared.py +58 -3
  5. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/protocols.py +37 -16
  6. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/.gitignore +0 -0
  7. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/README.md +0 -0
  8. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/__init__.py +0 -0
  9. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/_registry.py +0 -0
  10. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/backends/__init__.py +0 -0
  11. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/backends/_altair.py +0 -0
  12. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/backends/_base.py +0 -0
  13. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/backends/_feasibility_layout.py +0 -0
  14. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/backends/_matplotlib.py +0 -0
  15. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/backends/_plotly.py +0 -0
  16. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/backends/_seaborn.py +0 -0
  17. {dataeval_plots-0.0.11 → dataeval_plots-0.0.12}/src/dataeval_plots/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dataeval-plots
3
- Version: 0.0.11
3
+ Version: 0.0.12
4
4
  Summary: DataEval companion package for plotting utilities
5
5
  Project-URL: Homepage, https://dataeval.ai/
6
6
  Project-URL: Repository, https://github.com/aria-ml/dataeval/
@@ -13,12 +13,12 @@ Classifier: Intended Audience :: Science/Research
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Operating System :: OS Independent
15
15
  Classifier: Programming Language :: Python :: 3 :: Only
16
- Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
19
18
  Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
20
  Classifier: Topic :: Scientific/Engineering
21
- Requires-Python: >=3.10
21
+ Requires-Python: >=3.11
22
22
  Requires-Dist: matplotlib>=3.7.1
23
23
  Requires-Dist: numpy>=1.24.2; python_version < '3.12'
24
24
  Requires-Dist: numpy>=1.26.2; python_version >= '3.12'
@@ -6,7 +6,7 @@ authors = [
6
6
  { name = "Andrew Weng", email = "andrew.weng@ariacoustics.com" },
7
7
  { name = "Ryan Wood", email = "ryan.wood@ariacoustics.com" },
8
8
  ]
9
- requires-python = ">=3.10"
9
+ requires-python = ">=3.11"
10
10
  readme = "README.md"
11
11
  license = "MIT"
12
12
  maintainers = [
@@ -18,10 +18,10 @@ classifiers = [
18
18
  "Intended Audience :: Science/Research",
19
19
  "License :: OSI Approved :: MIT License",
20
20
  "Programming Language :: Python :: 3 :: Only",
21
- "Programming Language :: Python :: 3.10",
22
21
  "Programming Language :: Python :: 3.11",
23
22
  "Programming Language :: Python :: 3.12",
24
23
  "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
25
  "Topic :: Scientific/Engineering",
26
26
  ]
27
27
  dependencies = [
@@ -86,6 +86,14 @@ dev = [
86
86
  "ipywidgets",
87
87
  ]
88
88
 
89
+ [tool.uv]
90
+ constraint-dependencies = [
91
+ "pillow>=13.0.0", # Pillow 13.0.0 security release (decoder/encoder OOB reads and writes, DoS):
92
+ # CVE-2026-112395, CVE-2026-112397, CVE-2026-112400,
93
+ # CVE-2026-112403, CVE-2026-112404, CVE-2026-112895,
94
+ # CVE-2026-112896, CVE-2026-112897, CVE-2026-112899
95
+ ]
96
+
89
97
  [[tool.uv.index]]
90
98
  name = "pytorch-cpu"
91
99
  url = "https://download.pytorch.org/whl/cpu"
@@ -155,7 +163,7 @@ exclude = [
155
163
  ]
156
164
  line-length = 120
157
165
  indent-width = 4
158
- target-version = "py310"
166
+ target-version = "py311"
159
167
 
160
168
  [tool.ruff.lint]
161
169
  select = ["A", "ANN", "C4", "C90", "E", "F", "I", "NPY", "S", "SIM", "RET", "RUF100", "UP"]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.0.11'
22
- __version_tuple__ = version_tuple = (0, 0, 11)
21
+ __version__ = version = '0.0.12'
22
+ __version_tuple__ = version_tuple = (0, 0, 12)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -167,8 +167,8 @@ def reduce_embeddings(
167
167
 
168
168
  # scikit-learn 1.8 renamed the non-metric switch from `metric` to `metric_mds`
169
169
  # -- `metric` now names the distance metric -- and moved `init` from fit() onto
170
- # the constructor. 1.8 requires Python >=3.11, so on the 3.10 leg the lockfile
171
- # necessarily resolves 1.7.x, where neither new spelling exists.
170
+ # the constructor. The projection extra still allows older releases, where neither
171
+ # new spelling exists.
172
172
  #
173
173
  # Ask the signature rather than compare version strings: it needs no extra
174
174
  # dependency and tests exactly the thing being depended on. Omitting `init` on
@@ -932,6 +932,61 @@ def _feasibility_band(
932
932
  )
933
933
 
934
934
 
935
+ # These mirror DataEval's performance/_feasibility.py: _SOURCES, _source_name, _unit_noun and FeasibilityOutput._bounds
936
+ _SOURCE_NAMES = {
937
+ "replica_ceiling/v1": "repeated labels",
938
+ "builtin_witness/v1:knn": "built-in k-NN",
939
+ "builtin_witness/v1:probe": "built-in linear probe",
940
+ "user_witness/v1": "your model",
941
+ }
942
+ _EVIDENCE_SCHEMA = {
943
+ "target_spec": pl.String,
944
+ "metric": pl.String,
945
+ "threshold": pl.Float64,
946
+ "source": pl.String,
947
+ "evidence_id": pl.String,
948
+ "information_scope": pl.String,
949
+ "unit": pl.String,
950
+ "bound_type": pl.String,
951
+ "observed": pl.Float64,
952
+ "bound": pl.Float64,
953
+ "n": pl.Int64,
954
+ "state": pl.String,
955
+ }
956
+
957
+
958
+ def _evidence_rows(output: PlottableFeasibility) -> pl.DataFrame:
959
+ """One row per endpoint on a target in ``determinations``, in their order, then the order of ``evidence``."""
960
+ targets = {r["target_spec"]: r for r in output.determinations.iter_rows(named=True)}
961
+ rows = []
962
+ for spec, target in targets.items():
963
+ for eid, ev in output.evidence.items():
964
+ for e in ev.endpoints:
965
+ if e.target_spec != spec:
966
+ continue
967
+ stats = ev.details["endpoints"][spec] or {}
968
+ rows.append(
969
+ {
970
+ "target_spec": spec,
971
+ "metric": target["metric"],
972
+ "threshold": target["threshold"],
973
+ "source": _GROUP_CEILING
974
+ if ev.kind.startswith("local_panel_ceiling/v1")
975
+ else _SOURCE_NAMES.get(eid, eid),
976
+ "evidence_id": eid,
977
+ "information_scope": ev.information_scope,
978
+ "unit": "sequence" if ev.unit == "group" else "image",
979
+ "bound_type": e.direction,
980
+ # A ceiling's mean is panel agreement, not an observed score
981
+ "observed": stats.get("mean") if e.direction == "lower" else None,
982
+ "bound": e.value,
983
+ "n": stats.get("n"),
984
+ "state": e.state,
985
+ }
986
+ )
987
+ return pl.DataFrame(rows, schema=_EVIDENCE_SCHEMA)
988
+
989
+
935
990
  def prepare_feasibility_data(output: PlottableFeasibility) -> list[FeasibilityBand]:
936
991
  """
937
992
  Prepare feasibility data for plotting across all backends.
@@ -955,7 +1010,7 @@ def prepare_feasibility_data(output: PlottableFeasibility) -> list[FeasibilityBa
955
1010
  spec = json.loads(row["target_spec"])
956
1011
  spec.pop("threshold", None)
957
1012
  groups.setdefault(json.dumps(spec, sort_keys=True), []).append(row)
958
- evidence = output.aggregate_by_evidence().to_dicts()
1013
+ evidence = _evidence_rows(output).to_dicts()
959
1014
  alpha = output.meta().state["alpha"]
960
1015
  noun = _unit(rows, evidence) or "image" # the printout's footer noun, for the whole call
961
1016
  bands = []
@@ -22,14 +22,16 @@ from datetime import datetime
22
22
  from typing import (
23
23
  Any,
24
24
  Literal,
25
+ NotRequired,
25
26
  Protocol,
27
+ Required,
26
28
  TypedDict,
27
29
  runtime_checkable,
28
30
  )
29
31
 
30
32
  import polars as pl
31
33
  from numpy.typing import NDArray
32
- from typing_extensions import NotRequired, ReadOnly, Required
34
+ from typing_extensions import ReadOnly
33
35
 
34
36
 
35
37
  class ExecutionMetadata(Protocol):
@@ -287,36 +289,55 @@ class PlottableDriftMVDC(Plottable, Protocol):
287
289
  def plot_type(self) -> Literal["drift_mvdc"]: ...
288
290
 
289
291
 
292
+ class FeasibilityEndpoint(Protocol):
293
+ """One bound on one target, as DataEval's ``Endpoint`` records it; only the fields the plot reads."""
294
+
295
+ @property
296
+ def target_spec(self) -> str: ...
297
+ @property
298
+ def value(self) -> float | None: ...
299
+ @property
300
+ def direction(self) -> Literal["lower", "upper"]: ...
301
+ @property
302
+ def state(self) -> str: ...
303
+
304
+
305
+ class FeasibilityEvidence(Protocol):
306
+ """One evidence item, as DataEval's ``Evidence`` records it; only the fields the plot reads."""
307
+
308
+ @property
309
+ def kind(self) -> str: ...
310
+ @property
311
+ def information_scope(self) -> str: ...
312
+ @property
313
+ def unit(self) -> str: ...
314
+ @property
315
+ def endpoints(self) -> Sequence[FeasibilityEndpoint]: ...
316
+ @property
317
+ def details(self) -> Mapping[str, Any]: ...
318
+
319
+
290
320
  @runtime_checkable
291
321
  class PlottableFeasibility(Plottable, Protocol):
292
322
  """Protocol for feasibility plot outputs.
293
323
 
294
324
  Required attributes:
295
325
  - determinations: One row per target (DataFrame)
296
- - aggregate_by_evidence(): One row per evidence item and target (DataFrame)
326
+ - evidence: Every evidence item of the call, by evidence id
297
327
  - meta().state["alpha"]: The call's error budget
298
328
  - plot_type -> 'feasibility'
299
329
 
300
- DataFrame structures:
330
+ Structures:
301
331
  - determinations columns: target_spec, metric, threshold, judged_scope, determination, ceiling,
302
332
  ceiling_scope, witness, witness_source
303
- - aggregate_by_evidence() columns: target_spec, metric, threshold, source, evidence_id,
304
- information_scope, unit, bound_type, observed, bound, n, state, alpha, basis
333
+ - each evidence item: kind, information_scope, unit (``item`` or ``group``), endpoints (target_spec, value,
334
+ direction, state) and details["endpoints"][target_spec], which is None or holds the bound's mean and n
305
335
  """
306
336
 
307
337
  @property
308
338
  def determinations(self) -> pl.DataFrame: ...
309
- def aggregate_by_evidence(self) -> pl.DataFrame:
310
- """Evidence as a polars DataFrame.
311
-
312
- Returns
313
- -------
314
- pl.DataFrame
315
- One row per evidence item and target: each source's bound on the target, and its observed score
316
- where one exists
317
- """
318
- ...
319
-
339
+ @property
340
+ def evidence(self) -> Mapping[str, FeasibilityEvidence]: ...
320
341
  @property
321
342
  def plot_type(self) -> Literal["feasibility"]: ...
322
343