dataeval-plots 0.0.16__tar.gz → 0.0.18__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.
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/PKG-INFO +1 -1
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/_version.py +2 -2
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/backends/_shared.py +93 -4
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/protocols.py +44 -3
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/.gitignore +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/README.md +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/pyproject.toml +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/__init__.py +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/_registry.py +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/backends/__init__.py +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/backends/_altair.py +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/backends/_base.py +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/backends/_feasibility_layout.py +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/backends/_matplotlib.py +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/backends/_plotly.py +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/backends/_seaborn.py +0 -0
- {dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/py.typed +0 -0
|
@@ -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.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
21
|
+
__version__ = version = '0.0.18'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 18)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -15,6 +15,8 @@ import numpy as np
|
|
|
15
15
|
import polars as pl
|
|
16
16
|
from numpy.typing import NDArray
|
|
17
17
|
|
|
18
|
+
from dataeval_plots.protocols import PlottableFeasibilityBounds
|
|
19
|
+
|
|
18
20
|
if TYPE_CHECKING:
|
|
19
21
|
from dataeval_plots.protocols import (
|
|
20
22
|
PlottableBalance,
|
|
@@ -22,6 +24,7 @@ if TYPE_CHECKING:
|
|
|
22
24
|
PlottableDrift,
|
|
23
25
|
PlottableDriftMVDC,
|
|
24
26
|
PlottableFeasibility,
|
|
27
|
+
PlottableFeasibilityDeterminations,
|
|
25
28
|
)
|
|
26
29
|
|
|
27
30
|
MethodType = Literal[
|
|
@@ -1181,12 +1184,19 @@ def _feasibility_band(
|
|
|
1181
1184
|
)
|
|
1182
1185
|
|
|
1183
1186
|
|
|
1184
|
-
#
|
|
1187
|
+
# The readable name of each source: by DataEval 1.2.0's evidence id (these mirror its performance/_feasibility.py:
|
|
1188
|
+
# _SOURCES, _source_name, _unit_noun and FeasibilityOutput._bounds), and by the `source` value of the determinations and
|
|
1189
|
+
# bounds shape. Any other value shows its own text.
|
|
1185
1190
|
_SOURCE_NAMES = {
|
|
1186
1191
|
"replica_ceiling/v1": "repeated labels",
|
|
1187
1192
|
"builtin_witness/v1:knn": "built-in k-NN",
|
|
1188
1193
|
"builtin_witness/v1:probe": "built-in linear probe",
|
|
1189
1194
|
"user_witness/v1": "your model",
|
|
1195
|
+
"local_panels": _GROUP_CEILING,
|
|
1196
|
+
"replicas": "repeated labels",
|
|
1197
|
+
"knn": "built-in k-NN",
|
|
1198
|
+
"linear_probe": "built-in linear probe",
|
|
1199
|
+
"user_model": "your model",
|
|
1190
1200
|
}
|
|
1191
1201
|
_EVIDENCE_SCHEMA = {
|
|
1192
1202
|
"target_spec": pl.String,
|
|
@@ -1208,7 +1218,7 @@ _EVIDENCE_SCHEMA = {
|
|
|
1208
1218
|
}
|
|
1209
1219
|
|
|
1210
1220
|
|
|
1211
|
-
def _evidence_rows(output:
|
|
1221
|
+
def _evidence_rows(output: PlottableFeasibilityDeterminations) -> pl.DataFrame:
|
|
1212
1222
|
"""One row per endpoint on a target in ``determinations``, in their order, then the order of ``evidence``."""
|
|
1213
1223
|
targets = {r["target_spec"]: r for r in output.determinations.iter_rows(named=True)}
|
|
1214
1224
|
rows = []
|
|
@@ -1248,6 +1258,85 @@ def _evidence_rows(output: PlottableFeasibility) -> pl.DataFrame:
|
|
|
1248
1258
|
return pl.DataFrame(rows, schema=_EVIDENCE_SCHEMA)
|
|
1249
1259
|
|
|
1250
1260
|
|
|
1261
|
+
def _bounds_inputs(output: PlottableFeasibilityBounds) -> tuple[list[dict[str, Any]], pl.DataFrame]:
|
|
1262
|
+
"""
|
|
1263
|
+
Read the determinations and bounds shape as the determination rows and evidence frame the bands are built from.
|
|
1264
|
+
|
|
1265
|
+
The rows use the determinations and evidence shape's vocabulary, so everything downstream reads either shape
|
|
1266
|
+
alike: an unresolved determination with limitation ``conflict`` is an ``evidence_conflict``, and ``envelope`` is a
|
|
1267
|
+
``ceiling``.
|
|
1268
|
+
"""
|
|
1269
|
+
determinations = list(output.determinations.iter_rows(named=True))
|
|
1270
|
+
bounds = list(output.bounds.iter_rows(named=True))
|
|
1271
|
+
rows: list[dict[str, Any]] = []
|
|
1272
|
+
evidence: list[dict[str, Any]] = []
|
|
1273
|
+
for d in determinations:
|
|
1274
|
+
# ponytail: a private join key in the old reader's vocabulary; typed keys replace it when that reader goes
|
|
1275
|
+
spec = json.dumps(
|
|
1276
|
+
{
|
|
1277
|
+
"metric": d["metric"],
|
|
1278
|
+
"scope": d["judged_scope"],
|
|
1279
|
+
"class_name": d["class_name"],
|
|
1280
|
+
"aggregation": d["aggregation"],
|
|
1281
|
+
"ap_functional": d["ap_functional"],
|
|
1282
|
+
"max_false_positive_rate": d["max_false_positive_rate"],
|
|
1283
|
+
"threshold": d["threshold"],
|
|
1284
|
+
},
|
|
1285
|
+
sort_keys=True,
|
|
1286
|
+
)
|
|
1287
|
+
conflict = d["determination"] == "unresolved" and d["limitation"] == "conflict"
|
|
1288
|
+
rows.append(
|
|
1289
|
+
{
|
|
1290
|
+
"target_spec": spec,
|
|
1291
|
+
"metric": d["metric"],
|
|
1292
|
+
"threshold": d["threshold"],
|
|
1293
|
+
"judged_scope": d["judged_scope"],
|
|
1294
|
+
"determination": "evidence_conflict" if conflict else d["determination"],
|
|
1295
|
+
"ceiling": d["envelope"],
|
|
1296
|
+
"ceiling_scope": d["envelope_scope"],
|
|
1297
|
+
"witness": d["witness"],
|
|
1298
|
+
"witness_source": d["witness_source"],
|
|
1299
|
+
}
|
|
1300
|
+
)
|
|
1301
|
+
mine = [b for b in bounds if b["requirement"] == d["requirement"]]
|
|
1302
|
+
f0 = d["max_false_positive_rate"]
|
|
1303
|
+
for b in mine:
|
|
1304
|
+
q = _FALSE_POSITIVE if b["quantity"] == _FALSE_POSITIVE else "metric"
|
|
1305
|
+
fp = next(
|
|
1306
|
+
(x["bound"] for x in mine if x["source"] == b["source"] and x["quantity"] == _FALSE_POSITIVE), None
|
|
1307
|
+
)
|
|
1308
|
+
# A recall witness counts only when its own false-positive upper bound is within the limit
|
|
1309
|
+
uncertified = q == "metric" and b["bound_type"] == "lower" and f0 is not None and (fp is None or fp > f0)
|
|
1310
|
+
evidence.append(
|
|
1311
|
+
{
|
|
1312
|
+
"target_spec": spec,
|
|
1313
|
+
"metric": d["metric"],
|
|
1314
|
+
"threshold": d["threshold"],
|
|
1315
|
+
"source": _SOURCE_NAMES.get(b["source"], b["source"]),
|
|
1316
|
+
"evidence_id": b["source"],
|
|
1317
|
+
"information_scope": b["information_scope"],
|
|
1318
|
+
"unit": b["unit"],
|
|
1319
|
+
"bound_type": b["bound_type"],
|
|
1320
|
+
"observed": b["observed"],
|
|
1321
|
+
"bound": b["bound"],
|
|
1322
|
+
"n": b["n"],
|
|
1323
|
+
"state": b["state"],
|
|
1324
|
+
"quantity": q,
|
|
1325
|
+
"uncertified": uncertified,
|
|
1326
|
+
"false_positive_bound": fp,
|
|
1327
|
+
"false_positive_limit": f0,
|
|
1328
|
+
}
|
|
1329
|
+
)
|
|
1330
|
+
return rows, pl.DataFrame(evidence, schema=_EVIDENCE_SCHEMA)
|
|
1331
|
+
|
|
1332
|
+
|
|
1333
|
+
def _feasibility_inputs(output: PlottableFeasibility) -> tuple[list[dict[str, Any]], pl.DataFrame]:
|
|
1334
|
+
"""The determination rows and evidence frame of a feasibility output in either shape."""
|
|
1335
|
+
if isinstance(output, PlottableFeasibilityBounds):
|
|
1336
|
+
return _bounds_inputs(output)
|
|
1337
|
+
return list(output.determinations.iter_rows(named=True)), _evidence_rows(output)
|
|
1338
|
+
|
|
1339
|
+
|
|
1251
1340
|
def prepare_feasibility_data(output: PlottableFeasibility) -> list[FeasibilityBand]:
|
|
1252
1341
|
"""
|
|
1253
1342
|
Prepare feasibility data for plotting across all backends.
|
|
@@ -1265,13 +1354,13 @@ def prepare_feasibility_data(output: PlottableFeasibility) -> list[FeasibilityBa
|
|
|
1265
1354
|
list[FeasibilityBand]
|
|
1266
1355
|
One band per quantity, in the order the determinations first name it
|
|
1267
1356
|
"""
|
|
1268
|
-
rows =
|
|
1357
|
+
rows, evidence_frame = _feasibility_inputs(output)
|
|
1269
1358
|
groups: dict[str, list[dict[str, Any]]] = {}
|
|
1270
1359
|
for row in rows:
|
|
1271
1360
|
spec = json.loads(row["target_spec"])
|
|
1272
1361
|
spec.pop("threshold", None)
|
|
1273
1362
|
groups.setdefault(json.dumps(spec, sort_keys=True), []).append(row)
|
|
1274
|
-
evidence =
|
|
1363
|
+
evidence = evidence_frame.to_dicts()
|
|
1275
1364
|
alpha = output.meta().state["alpha"]
|
|
1276
1365
|
noun = _unit(rows, [e for e in evidence if e["quantity"] == "metric"]) or "image" # the printout's footer noun
|
|
1277
1366
|
bands = []
|
|
@@ -13,7 +13,9 @@ The protocol hierarchy:
|
|
|
13
13
|
- PlottableStats
|
|
14
14
|
- PlottableDriftMVDC
|
|
15
15
|
- PlottableDrift
|
|
16
|
-
-
|
|
16
|
+
- PlottableFeasibilityDeterminations
|
|
17
|
+
- PlottableFeasibilityBounds
|
|
18
|
+
- PlottableFeasibility (either of the two feasibility shapes)
|
|
17
19
|
"""
|
|
18
20
|
|
|
19
21
|
from __future__ import annotations
|
|
@@ -358,8 +360,8 @@ class FeasibilityEvidence(Protocol):
|
|
|
358
360
|
|
|
359
361
|
|
|
360
362
|
@runtime_checkable
|
|
361
|
-
class
|
|
362
|
-
"""Protocol for feasibility plot outputs.
|
|
363
|
+
class PlottableFeasibilityDeterminations(Plottable, Protocol):
|
|
364
|
+
"""Protocol for feasibility plot outputs in the ``determinations`` and ``evidence`` shape.
|
|
363
365
|
|
|
364
366
|
Required attributes:
|
|
365
367
|
- determinations: One row per target (DataFrame)
|
|
@@ -382,6 +384,45 @@ class PlottableFeasibility(Plottable, Protocol):
|
|
|
382
384
|
def plot_type(self) -> Literal["feasibility"]: ...
|
|
383
385
|
|
|
384
386
|
|
|
387
|
+
@runtime_checkable
|
|
388
|
+
class PlottableFeasibilityBounds(Plottable, Protocol):
|
|
389
|
+
"""Protocol for feasibility plot outputs in the ``determinations`` and ``bounds`` shape.
|
|
390
|
+
|
|
391
|
+
Required attributes:
|
|
392
|
+
- determinations: One row per requirement (DataFrame)
|
|
393
|
+
- bounds: One row per source, requirement and quantity (DataFrame)
|
|
394
|
+
- meta().state["alpha"]: The call's error budget
|
|
395
|
+
- plot_type -> 'feasibility'
|
|
396
|
+
|
|
397
|
+
Structures:
|
|
398
|
+
- determinations columns: requirement, metric, threshold, judged_scope, class_name, aggregation, ap_functional,
|
|
399
|
+
max_false_positive_rate, determination, limitation, envelope, envelope_scope, witness, witness_source
|
|
400
|
+
- bounds columns: requirement, source, role, quantity, bound_type, information_scope, unit, state, bound,
|
|
401
|
+
observed, n; ``requirement`` joins a bound to its determinations row
|
|
402
|
+
|
|
403
|
+
Enum columns are read as strings, with these values:
|
|
404
|
+
- determination: demonstrated_feasible, infeasible_under_recorded_assumptions, not_ruled_out, unresolved
|
|
405
|
+
- limitation: only ``conflict`` is read; with determination ``unresolved`` it marks a conflict between a witness
|
|
406
|
+
and an envelope
|
|
407
|
+
- judged_scope, envelope_scope, information_scope: full_input, fixed_representation, partition_restricted
|
|
408
|
+
- source, witness_source: local_panels, replicas, knn, linear_probe, user_model (any other value shows its text)
|
|
409
|
+
- role: envelope, witness. bound_type: lower, upper
|
|
410
|
+
- quantity: a metric name (accuracy, ap, recall, recall_at_fp, mota) or false_positive_rate
|
|
411
|
+
- state: unavailable, uninformative, informative. unit: image, sequence
|
|
412
|
+
- aggregation: frame_pooled, sequence_mean. ap_functional: population_ap_integral, population_ap_101
|
|
413
|
+
"""
|
|
414
|
+
|
|
415
|
+
@property
|
|
416
|
+
def determinations(self) -> pl.DataFrame: ...
|
|
417
|
+
@property
|
|
418
|
+
def bounds(self) -> pl.DataFrame: ...
|
|
419
|
+
@property
|
|
420
|
+
def plot_type(self) -> Literal["feasibility"]: ...
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
PlottableFeasibility = PlottableFeasibilityDeterminations | PlottableFeasibilityBounds
|
|
424
|
+
"""A feasibility output in either shape; DataEval 1.2.0 gives the first, later releases the second."""
|
|
425
|
+
|
|
385
426
|
# Type alias for all plottable types
|
|
386
427
|
PlottableType = (
|
|
387
428
|
Dataset
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dataeval_plots-0.0.16 → dataeval_plots-0.0.18}/src/dataeval_plots/backends/_feasibility_layout.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|