dataeval-plots 0.0.14__tar.gz → 0.0.15__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.14 → dataeval_plots-0.0.15}/PKG-INFO +8 -2
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/README.md +7 -1
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/_version.py +2 -2
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/backends/_shared.py +75 -23
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/protocols.py +6 -1
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/.gitignore +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/pyproject.toml +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/__init__.py +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/_registry.py +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/backends/__init__.py +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/backends/_altair.py +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/backends/_base.py +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/backends/_feasibility_layout.py +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/backends/_matplotlib.py +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/backends/_plotly.py +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/src/dataeval_plots/backends/_seaborn.py +0 -0
- {dataeval_plots-0.0.14 → dataeval_plots-0.0.15}/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.
|
|
3
|
+
Version: 0.0.15
|
|
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/
|
|
@@ -238,7 +238,13 @@ up, so rounding never overstates what the evidence shows. Requirements and confi
|
|
|
238
238
|
"95.5%" or "at 97.5% joint confidence".
|
|
239
239
|
|
|
240
240
|
When a ceiling bounds the band, the caption ends with the footnote "\* Ruled out under the recorded assumptions, at
|
|
241
|
-
the stated confidence."
|
|
241
|
+
the stated confidence."
|
|
242
|
+
|
|
243
|
+
A recall requirement is titled "Required recall for Tank (at most 5% false positives)". Only a witness whose own upper
|
|
244
|
+
confidence bound on false positives is within that limit counts; the caption adds its bound after the observed
|
|
245
|
+
sentence, for example "Its upper confidence bound on false positives is 4.1% (observed 2.6% of 651 other images)." In
|
|
246
|
+
the evidence view, a witness over the limit keeps its recall bound and observed score but is drawn faint, its label
|
|
247
|
+
saying why it is not counted, for example "66.0% lower bound · not counted: false positives ≤8.3% (limit 5%)".
|
|
242
248
|
|
|
243
249
|
A ceiling that applies only to narrower models than the band judges never draws a zone. It appears as a note instead,
|
|
244
250
|
for example "Group-based ceiling (72.1%) applies only to models that give one answer per group."
|
|
@@ -192,7 +192,13 @@ up, so rounding never overstates what the evidence shows. Requirements and confi
|
|
|
192
192
|
"95.5%" or "at 97.5% joint confidence".
|
|
193
193
|
|
|
194
194
|
When a ceiling bounds the band, the caption ends with the footnote "\* Ruled out under the recorded assumptions, at
|
|
195
|
-
the stated confidence."
|
|
195
|
+
the stated confidence."
|
|
196
|
+
|
|
197
|
+
A recall requirement is titled "Required recall for Tank (at most 5% false positives)". Only a witness whose own upper
|
|
198
|
+
confidence bound on false positives is within that limit counts; the caption adds its bound after the observed
|
|
199
|
+
sentence, for example "Its upper confidence bound on false positives is 4.1% (observed 2.6% of 651 other images)." In
|
|
200
|
+
the evidence view, a witness over the limit keeps its recall bound and observed score but is drawn faint, its label
|
|
201
|
+
saying why it is not counted, for example "66.0% lower bound · not counted: false positives ≤8.3% (limit 5%)".
|
|
196
202
|
|
|
197
203
|
A ceiling that applies only to narrower models than the band judges never draws a zone. It appears as a note instead,
|
|
198
204
|
for example "Group-based ceiling (72.1%) applies only to models that give one answer per group."
|
|
@@ -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.15'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 15)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -697,6 +697,7 @@ _RULED_OUT = "ruled out*"
|
|
|
697
697
|
_AGGREGATION_WORDS = {None: "", "sequence_mean": "per-sequence mean ", "frame_pooled": "frame-pooled "}
|
|
698
698
|
_GROUP_CEILING = "group-based ceiling"
|
|
699
699
|
_NO_BOUND = "no bound available"
|
|
700
|
+
_FALSE_POSITIVE = "false_positive_rate"
|
|
700
701
|
|
|
701
702
|
|
|
702
703
|
def _pct(value: float, direction: str | None = None) -> str:
|
|
@@ -712,6 +713,11 @@ def _pct(value: float, direction: str | None = None) -> str:
|
|
|
712
713
|
return f"{tenths / 10:.1f}%"
|
|
713
714
|
|
|
714
715
|
|
|
716
|
+
def _confidence(value: float, direction: str) -> str:
|
|
717
|
+
"""Round a confidence down or an error share up to 0.1%, dropping a trailing ".0", as the printout does."""
|
|
718
|
+
return _pct(value, direction).replace(".0%", "%")
|
|
719
|
+
|
|
720
|
+
|
|
715
721
|
def _percent(value: float) -> str:
|
|
716
722
|
"""A threshold, confidence or alpha as DataEval prints it: no rounding to whole percents, no trailing zeros."""
|
|
717
723
|
return f"{value * 100:g}%"
|
|
@@ -769,6 +775,16 @@ def _feasibility_range(values: Iterable[float]) -> tuple[float, float]:
|
|
|
769
775
|
return round(max(low, 0.0), 2), 1.0
|
|
770
776
|
|
|
771
777
|
|
|
778
|
+
def _not_counted(e: dict[str, Any]) -> str:
|
|
779
|
+
"""The label suffix of a recall witness that does not count: its false-positive bound is over the limit or none."""
|
|
780
|
+
if not e["uncertified"]:
|
|
781
|
+
return ""
|
|
782
|
+
fp = e["false_positive_bound"]
|
|
783
|
+
seen = "no false-positive bound" if fp is None else f"false positives ≤{_pct(fp, 'upper')}"
|
|
784
|
+
limit = "" if fp is None else f" (limit {_percent(e['false_positive_limit'])})"
|
|
785
|
+
return f" · not counted: {seen}{limit}"
|
|
786
|
+
|
|
787
|
+
|
|
772
788
|
def _feasibility_evidence(
|
|
773
789
|
evidence: list[dict[str, Any]], scope: str, unit: str
|
|
774
790
|
) -> tuple[list[FeasibilityEvidenceRow], list[str]]:
|
|
@@ -802,10 +818,10 @@ def _feasibility_evidence(
|
|
|
802
818
|
observed=observed,
|
|
803
819
|
restricted=restricted,
|
|
804
820
|
label=label,
|
|
805
|
-
bound_label=_NO_BOUND if bound is None else f"{_pct(bound, kind)} {kind} bound",
|
|
821
|
+
bound_label=_NO_BOUND if bound is None else f"{_pct(bound, kind)} {kind} bound" + _not_counted(e),
|
|
806
822
|
observed_label=None if observed is None else f"{_pct(observed)} observed",
|
|
807
823
|
bound_left=kind == "lower",
|
|
808
|
-
faint=restricted or bound is None,
|
|
824
|
+
faint=restricted or bound is None or e["uncertified"],
|
|
809
825
|
)
|
|
810
826
|
)
|
|
811
827
|
if restricted and kind == "upper" and bound is not None:
|
|
@@ -849,18 +865,29 @@ def _unit(rows: list[dict[str, Any]], evidence: list[dict[str, Any]]) -> str | N
|
|
|
849
865
|
return None
|
|
850
866
|
|
|
851
867
|
|
|
868
|
+
def _other(fp: dict[str, Any]) -> str:
|
|
869
|
+
"""The audit negatives a false-positive bound was measured on, such as ``651 other images``."""
|
|
870
|
+
return ("" if fp["n"] is None else f"{fp['n']:,} ") + f"other {fp['unit']}s"
|
|
871
|
+
|
|
872
|
+
|
|
852
873
|
def _feasibility_title(spec: dict[str, Any]) -> str:
|
|
853
874
|
"""The axis title, naming the AP class (or macro) and any aggregation so per-class bands differ."""
|
|
854
875
|
metric = spec["metric"]
|
|
855
876
|
if metric == "ap":
|
|
856
877
|
metric = "macro AP" if spec.get("class_name") is None else f"AP for class {spec['class_name']}"
|
|
878
|
+
elif metric == "recall":
|
|
879
|
+
f0 = _percent(spec["max_false_positive_rate"])
|
|
880
|
+
metric = f"recall for {spec['class_name']} (at most {f0} false positives)"
|
|
857
881
|
return "Required " + _AGGREGATION_WORDS[spec.get("aggregation")] + metric
|
|
858
882
|
|
|
859
883
|
|
|
860
884
|
def _feasibility_band(
|
|
861
|
-
rows: list[dict[str, Any]],
|
|
885
|
+
rows: list[dict[str, Any]], all_evidence: list[dict[str, Any]], alpha: float, noun: str
|
|
862
886
|
) -> FeasibilityBand:
|
|
863
887
|
"""One band; ``noun`` is the unit the printout's footer names for the whole call."""
|
|
888
|
+
# Bands and evidence rows draw the metric; a recall witness's false-positive bound only joins the caption
|
|
889
|
+
evidence = [e for e in all_evidence if e["quantity"] == "metric"]
|
|
890
|
+
false_positives = [e for e in all_evidence if e["quantity"] == _FALSE_POSITIVE]
|
|
864
891
|
scope = rows[0]["judged_scope"]
|
|
865
892
|
unit = _unit(rows, evidence) or "image"
|
|
866
893
|
# A lower bound of 0% demonstrates nothing and an upper bound of 100% rules nothing out
|
|
@@ -887,7 +914,7 @@ def _feasibility_band(
|
|
|
887
914
|
|
|
888
915
|
conflict_caption = (
|
|
889
916
|
f"Bounds disagree: check the assumptions, such as evaluation {unit}s entering training. "
|
|
890
|
-
f"Even when they hold, the chance that any bound fails is at most {
|
|
917
|
+
f"Even when they hold, the chance that any bound fails is at most {_confidence(alpha, 'upper')}."
|
|
891
918
|
)
|
|
892
919
|
if conflict:
|
|
893
920
|
caption = [conflict_caption]
|
|
@@ -896,25 +923,38 @@ def _feasibility_band(
|
|
|
896
923
|
witness = None if best is None else _deciding(best, evidence)[0]
|
|
897
924
|
if witness and witness["observed"] is not None and w is not None:
|
|
898
925
|
on = "the " if witness["n"] is None else f"{witness['n']:,} "
|
|
926
|
+
# A recall witness's count is the class's positives, which the printout names by class
|
|
927
|
+
spec = json.loads(rows[0]["target_spec"])
|
|
928
|
+
scored = f"{_pct(witness['observed'])} on {on}evaluation {unit}s"
|
|
929
|
+
if spec["metric"] == "recall":
|
|
930
|
+
scored = f"{_pct(witness['observed'])} recall on {on}{spec['class_name']} {unit}s"
|
|
899
931
|
caption.append(
|
|
900
|
-
f"{_sentence_case(witness['source'])} scored {
|
|
901
|
-
f"
|
|
932
|
+
f"{_sentence_case(witness['source'])} scored {scored}; given sampling uncertainty, its lower "
|
|
933
|
+
f"confidence bound is {_pct(w, 'lower')}."
|
|
902
934
|
)
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
935
|
+
fp = None
|
|
936
|
+
if witness and w is not None:
|
|
937
|
+
fp = next(
|
|
938
|
+
(
|
|
939
|
+
e
|
|
940
|
+
for e in false_positives
|
|
941
|
+
if e["evidence_id"] == witness["evidence_id"]
|
|
942
|
+
and e["target_spec"] == witness["target_spec"]
|
|
943
|
+
and e["bound"] is not None
|
|
944
|
+
),
|
|
945
|
+
None,
|
|
946
|
+
)
|
|
947
|
+
if fp is not None:
|
|
948
|
+
seen = "" if fp["observed"] is None else f" (observed {_pct(fp['observed'])} of {_other(fp)})"
|
|
949
|
+
caption.append(f"Its upper confidence bound on false positives is {_pct(fp['bound'], 'upper')}{seen}.")
|
|
950
|
+
joint = _confidence(1 - alpha, "lower")
|
|
951
|
+
caption.append(f"Bounds apply to new {noun}s from the same population, at {joint} joint confidence.")
|
|
906
952
|
# The "*" footnote to every "ruled out*" goes last, as a footnote does
|
|
907
953
|
if deciding is not None and c is not None:
|
|
908
|
-
|
|
909
|
-
sign_off = ceiling is not None and ceiling["source"] == _GROUP_CEILING
|
|
910
|
-
caption.append(
|
|
911
|
-
"* Ruled out under the recorded assumptions, at the stated confidence"
|
|
912
|
-
+ (" (group-based ceiling; validity pending lead-scientist sign-off)" if sign_off else "")
|
|
913
|
-
+ "."
|
|
914
|
-
)
|
|
954
|
+
caption.append("* Ruled out under the recorded assumptions, at the stated confidence.")
|
|
915
955
|
evidence_caption = [
|
|
916
956
|
f"Observed on the evaluation {unit}s; bounds apply to new {unit}s from the same population, "
|
|
917
|
-
f"at {
|
|
957
|
+
f"at {_confidence(1 - alpha, 'lower')} joint confidence."
|
|
918
958
|
]
|
|
919
959
|
if conflict:
|
|
920
960
|
evidence_caption.append(conflict_caption)
|
|
@@ -962,6 +1002,10 @@ _EVIDENCE_SCHEMA = {
|
|
|
962
1002
|
"bound": pl.Float64,
|
|
963
1003
|
"n": pl.Int64,
|
|
964
1004
|
"state": pl.String,
|
|
1005
|
+
"quantity": pl.String,
|
|
1006
|
+
"uncertified": pl.Boolean,
|
|
1007
|
+
"false_positive_bound": pl.Float64,
|
|
1008
|
+
"false_positive_limit": pl.Float64,
|
|
965
1009
|
}
|
|
966
1010
|
|
|
967
1011
|
|
|
@@ -971,10 +1015,14 @@ def _evidence_rows(output: PlottableFeasibility) -> pl.DataFrame:
|
|
|
971
1015
|
rows = []
|
|
972
1016
|
for spec, target in targets.items():
|
|
973
1017
|
for eid, ev in output.evidence.items():
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1018
|
+
# An older DataEval has no quantity; its endpoints all bound the metric
|
|
1019
|
+
mine = [(e, getattr(e, "quantity", "metric")) for e in ev.endpoints if e.target_spec == spec]
|
|
1020
|
+
fp = next((e.value for e, q in mine if q == _FALSE_POSITIVE), None)
|
|
1021
|
+
f0 = json.loads(spec).get("max_false_positive_rate")
|
|
1022
|
+
for e, q in mine:
|
|
1023
|
+
stats = ev.details["endpoints"].get(spec if q == "metric" else f"{spec}#{q}") or {}
|
|
1024
|
+
# A recall witness counts only when its own false-positive upper bound is within the limit
|
|
1025
|
+
uncertified = q == "metric" and e.direction == "lower" and f0 is not None and (fp is None or fp > f0)
|
|
978
1026
|
rows.append(
|
|
979
1027
|
{
|
|
980
1028
|
"target_spec": spec,
|
|
@@ -988,10 +1036,14 @@ def _evidence_rows(output: PlottableFeasibility) -> pl.DataFrame:
|
|
|
988
1036
|
"unit": "sequence" if ev.unit == "group" else "image",
|
|
989
1037
|
"bound_type": e.direction,
|
|
990
1038
|
# A ceiling's mean is panel agreement, not an observed score
|
|
991
|
-
"observed": stats.get("mean") if e.direction == "lower" else None,
|
|
1039
|
+
"observed": stats.get("mean") if e.direction == "lower" or q == _FALSE_POSITIVE else None,
|
|
992
1040
|
"bound": e.value,
|
|
993
1041
|
"n": stats.get("n"),
|
|
994
1042
|
"state": e.state,
|
|
1043
|
+
"quantity": q,
|
|
1044
|
+
"uncertified": uncertified,
|
|
1045
|
+
"false_positive_bound": fp,
|
|
1046
|
+
"false_positive_limit": f0,
|
|
995
1047
|
}
|
|
996
1048
|
)
|
|
997
1049
|
return pl.DataFrame(rows, schema=_EVIDENCE_SCHEMA)
|
|
@@ -1022,7 +1074,7 @@ def prepare_feasibility_data(output: PlottableFeasibility) -> list[FeasibilityBa
|
|
|
1022
1074
|
groups.setdefault(json.dumps(spec, sort_keys=True), []).append(row)
|
|
1023
1075
|
evidence = _evidence_rows(output).to_dicts()
|
|
1024
1076
|
alpha = output.meta().state["alpha"]
|
|
1025
|
-
noun = _unit(rows, evidence) or "image" # the printout's footer noun
|
|
1077
|
+
noun = _unit(rows, [e for e in evidence if e["quantity"] == "metric"]) or "image" # the printout's footer noun
|
|
1026
1078
|
bands = []
|
|
1027
1079
|
for band in groups.values():
|
|
1028
1080
|
specs = {r["target_spec"] for r in band}
|
|
@@ -290,7 +290,12 @@ class PlottableDriftMVDC(Plottable, Protocol):
|
|
|
290
290
|
|
|
291
291
|
|
|
292
292
|
class FeasibilityEndpoint(Protocol):
|
|
293
|
-
"""
|
|
293
|
+
"""
|
|
294
|
+
One bound on one target, as DataEval's ``Endpoint`` records it; only the fields the plot reads.
|
|
295
|
+
|
|
296
|
+
Newer outputs also give ``quantity``, ``"metric"`` or ``"false_positive_rate"``; the plot reads it with a default
|
|
297
|
+
of ``"metric"`` so older outputs still plot.
|
|
298
|
+
"""
|
|
294
299
|
|
|
295
300
|
@property
|
|
296
301
|
def target_spec(self) -> str: ...
|
|
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.14 → dataeval_plots-0.0.15}/src/dataeval_plots/backends/_feasibility_layout.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|