Metrics API Reference¶
Evaluation¶
evaluate_detections ¶
evaluate_detections(
predictions: DataFrame | LazyFrame,
ground_truth: DataFrame | LazyFrame,
*,
geometry: str | Sequence[str] = "bbox",
box_format: BoxFormat | None = None,
image_id: str = COL_IMAGE_ID,
class_id: str | None = COL_CLASS_ID,
score: str = "score",
iou_thresholds: Literal["coco"]
| float
| Sequence[float] = "coco",
interpolation: APInterpolation | None = None,
max_detections: int | None | Literal["coco"] = "coco",
images: Iterable[str]
| Series
| DataFrame
| LazyFrame
| None = None,
weight: str | None = None,
group: str | None = None,
fp_rates: Sequence[float] | None = None,
extrapolate: Extrapolate = "none",
) -> DetectionReport
Evaluate object detections (boxes, polygons or instance masks).
The one-call form of :func:match_detections plus the summary metrics,
COCO-style by default::
report = evaluate_detections(preds, gts, box_format="xyxy")
report.summary # map, map_50, map_75, mar
report.per_class # AP per class
report.ci("map") # bootstrap interval
report.matches(0.5) # each prediction's TP/FP, for error analysis
With the defaults (iou_thresholds="coco") matching is redone at each
IoU threshold 0.50:0.05:0.95, AP is COCO's 101-point interpolation, each
image keeps its 100 highest-scoring predictions per class, and a class
without ground truth is left out of the means — COCO's evaluation, except
that it has no area ranges and no crowd/ignore regions (and ties between
equal scores form one PR point rather than following input order).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predictions
|
DataFrame | LazyFrame
|
One row per predicted object ( |
required |
ground_truth
|
DataFrame | LazyFrame
|
One row per ground-truth object. |
required |
geometry
|
str | Sequence[str]
|
The geometry column (or four coordinate columns); see
:func: |
'bbox'
|
box_format
|
BoxFormat | None
|
|
None
|
image_id
|
str
|
The image identifier column. |
COL_IMAGE_ID
|
class_id
|
str | None
|
The class column, or |
COL_CLASS_ID
|
score
|
str
|
The prediction confidence column. |
'score'
|
iou_thresholds
|
Literal['coco'] | float | Sequence[float]
|
|
'coco'
|
interpolation
|
APInterpolation | None
|
AP interpolation; |
None
|
max_detections
|
int | None | Literal['coco']
|
Per (image, class) cap on predictions; |
'coco'
|
images
|
Iterable[str] | Series | DataFrame | LazyFrame | None
|
The image population (see :func: |
None
|
weight
|
str | None
|
A per-image weight column of the |
None
|
group
|
str | None
|
A per-image subgroup column of the |
None
|
fp_rates
|
Sequence[float] | None
|
FROC operating points to summarise ( |
None
|
extrapolate
|
Extrapolate
|
How FROC curves are read past their last point. |
'none'
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
DetectionReport
|
class: |
evaluate_heatmaps ¶
evaluate_heatmaps(
data: DataFrame | LazyFrame,
*,
heatmap: str,
gt: str,
image_id: str | None = None,
class_id: str | None = None,
iou_threshold: float | Sequence[float] = 0.5,
fp_rates: Sequence[float] = FROC_RATES,
extrapolate: Extrapolate = "none",
interpolation: APInterpolation = "all_points",
weight: str | None = None,
group: str | None = None,
**matcher_options: Any,
) -> DetectionReport
Evaluate dense predictions (heatmaps, probability maps) against masks.
One row per image: each heatmap is thresholded into candidate regions,
each region scored from the heatmap, and the regions matched to the
ground-truth mask's regions by :class:ContourMatcher (any of its options
can be passed through, e.g. match_by="coverage",
extraction_threshold=0.3, score_reduction="mean"). The report
summarises FROC — sensitivity at each of fp_rates and their mean, the
CPM — beside AP::
report = evaluate_heatmaps(df, heatmap="prob", gt="mask")
report.summary # map, mar, sensitivity@…, cpm
report.ci("cpm")
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame | LazyFrame
|
One row per image. |
required |
heatmap
|
str
|
The prediction column (any format a polars-cv source reads). |
required |
gt
|
str
|
The ground-truth mask (or contour) column. |
required |
image_id
|
str | None
|
An image identifier column (default: the row index). |
None
|
class_id
|
str | None
|
A class column (one row per image and class). |
None
|
iou_threshold
|
float | Sequence[float]
|
The matching threshold, or several. |
0.5
|
fp_rates
|
Sequence[float]
|
FROC operating points (false positives per image). |
FROC_RATES
|
extrapolate
|
Extrapolate
|
How the FROC curve is read past its last point
( |
'none'
|
interpolation
|
APInterpolation
|
AP interpolation. |
'all_points'
|
weight
|
str | None
|
A per-image weight column of |
None
|
group
|
str | None
|
A per-image subgroup column of |
None
|
**matcher_options
|
Any
|
:class: |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
DetectionReport
|
class: |
evaluate_segmentation ¶
evaluate_segmentation(
data: DataFrame | LazyFrame,
*,
pred: str,
target: str,
image_id: str | None = None,
threshold: float = 0.5,
frame: Literal["image"] | None = "image",
sample_step: float | None = 1.0,
spacing: tuple[float, float] | None = None,
) -> SegmentationReport
Evaluate semantic segmentation masks: Dice, IoU, ASSD, HD, HD95.
::
report = evaluate_segmentation(df, pred="pred_mask", target="gt_mask")
report.summary # mean of each measure, with undefined counts
report.per_image # one row per image
report.ci("dice")
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame | LazyFrame
|
One row per image. |
required |
pred
|
str
|
The predicted mask column. |
required |
target
|
str
|
The reference mask column. |
required |
image_id
|
str | None
|
An image identifier column (default: the row index). |
None
|
threshold
|
float
|
Foreground threshold for both masks. |
0.5
|
frame
|
Literal['image'] | None
|
|
'image'
|
sample_step
|
float | None
|
Boundary sample spacing (see :func: |
1.0
|
spacing
|
tuple[float, float] | None
|
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
SegmentationReport
|
class: |
segmentation_measures ¶
segmentation_measures(
pred: str | Expr,
target: str | Expr,
*,
threshold: float = 0.5,
frame: Literal["image"] | None = "image",
sample_step: float | None = 1.0,
spacing: tuple[float, float] | None = None,
) -> pl.Expr
Overlap and boundary agreement of two masks, one struct per row.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pred
|
str | Expr
|
The predicted mask column (any format a polars-cv source reads:
encoded image bytes, a nested |
required |
target
|
str | Expr
|
The reference mask column, the same size. |
required |
threshold
|
float
|
Pixels above it are foreground (0.5 suits 0/1 and 0/255 masks and probability maps alike). |
0.5
|
frame
|
Literal['image'] | None
|
|
'image'
|
sample_step
|
float | None
|
Spacing of the boundary samples along each edge, in
output units (pixels, or |
1.0
|
spacing
|
tuple[float, float] | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
Expr
|
``Struct{dice, iou, assd, hd, hd95, mean_pred_to_target, |
Expr
|
mean_target_to_pred}`` (Float64). Two empty masks agree perfectly |
Expr
|
(Dice and IoU 1); one empty mask has Dice and IoU 0. Boundary |
Expr
|
distances are null whenever either mask is empty (no boundary to |
Expr
|
measure to) — as MONAI's are NaN. |
DetectionReport
dataclass
¶
A detection evaluation: summary, per-class and per-threshold tables.
Every number comes from a :class:Statistic on :attr:table (or one
threshold's view of it); :meth:ci bootstraps the same statistics.
Attributes:
| Name | Type | Description |
|---|---|---|
table |
DetectionTable
|
The matched :class: |
interpolation |
APInterpolation
|
The AP interpolation of every AP in the report. |
fp_rates |
tuple[float, ...] | None
|
The FROC operating points summarised (sensitivity at each,
and their mean, the CPM), or |
extrapolate |
Extrapolate
|
How FROC curves are read past their last point. |
summary
cached
property
¶
[metric, value]: mAP (COCO: classes without ground truth left
out), mAP at 0.5 / 0.75 when swept, mean recall (mar) and, with
fp_rates, FROC sensitivities and their mean (cpm).
per_class
cached
property
¶
[class_id, ap, ap_50, ap_75, recall, n_gts, n_preds]: AP and
recall averaged over the matched thresholds (ap_50/ap_75 when
those were matched).
per_threshold
cached
property
¶
[iou_threshold, class_id, ap, recall, n_gts, n_preds], one row
per matched threshold and class. ap/recall are null for a class
without ground truth (undefined, as COCO's -1).
ci ¶
ci(
metric: str | Sequence[str] | None = None,
*,
by: str | list[str] | None = None,
n_bootstrap: int = 1000,
confidence: float = 0.95,
seed: int | None = None,
sample_col: str | None = None,
strata: str | list[str] | None = None,
weight_rtol: float | None = None,
weight_scheme: WeightScheme = "reestimate",
) -> pl.DataFrame
Percentile bootstrap intervals for summary numbers.
Each is :func:bootstrap_ci of the statistic behind the number:
images are resampled (stratified by whether they hold ground truth),
and the classes and IoU thresholds a number averages over are drawn
together, so an mAP interval reflects one resample per replicate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metric
|
str | Sequence[str] | None
|
A summary name, several, or |
None
|
by
|
str | list[str] | None
|
Optional metadata column(s) to report per group (e.g.
|
None
|
n_bootstrap
|
int
|
Replicates. |
1000
|
confidence
|
float
|
Confidence level in |
0.95
|
seed
|
int | None
|
Optional seed ( |
None
|
sample_col
|
str | None
|
Optional entity column (e.g. a patient id) to resample instead of images. |
None
|
strata
|
str | list[str] | None
|
Optional metadata column(s) crossed into the weight cells. |
None
|
weight_rtol
|
float | None
|
Relative tolerance within which weights share a cell. |
None
|
weight_scheme
|
WeightScheme
|
What the weights are, and so how the draw treats
them: |
'reestimate'
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
|
matches ¶
Every prediction with its outcome at one threshold, for error analysis.
For a report from :func:evaluate_detections, the prediction input's
rows with is_tp, iou and matched_gt_row (the matched
ground truth's row in the ground-truth input). Otherwise the
detections table.
pr_curve ¶
pr_curve(
class_id: str | None = None,
iou_threshold: float | None = None,
) -> PrecisionRecallResult
The precision-recall curve of one class at one threshold.
froc ¶
froc(
iou_threshold: float | None = None,
*,
group_by: str | list[str] | None = None,
) -> pl.DataFrame
The FROC curve at one threshold (default: the lowest matched).
confusion ¶
confusion(
score_threshold: float,
*,
class_id: str | None = None,
iou_threshold: float | None = None,
) -> ConfusionResult
TP/FP/FN of the detections scoring at least score_threshold.
at ¶
The table at one IoU threshold (default: the lowest matched).
SegmentationReport
dataclass
¶
Per-image measures of a segmentation evaluation and their summary.
Attributes:
| Name | Type | Description |
|---|---|---|
per_image |
DataFrame
|
|
summary
cached
property
¶
[metric, value, n, n_undefined]: the mean of each measure over
the images where it is defined (boundary distances are undefined when
a mask is empty), and how many images that left out.
ci ¶
ci(
metric: str | Sequence[str] | None = None,
*,
n_bootstrap: int = 1000,
confidence: float = 0.95,
seed: int | None = None,
) -> pl.DataFrame
Percentile bootstrap intervals of the summary means over images.
Images are drawn by the same seeded hash resampler as the detection
intervals (:func:~polars_cv.metrics.bootstrap_ci), so a seed means the
same thing in both.
Returns:
| Type | Description |
|---|---|
DataFrame
|
|
ReportMetric
dataclass
¶
A reported number: the statistic and the table view it reads.
match_detections ¶
match_detections(
predictions: DataFrame | LazyFrame,
ground_truth: DataFrame | LazyFrame,
*,
geometry: Geometry = "bbox",
box_format: BoxFormat | None = None,
image_id: str = COL_IMAGE_ID,
class_id: str | None = COL_CLASS_ID,
score: str = "score",
iou_thresholds: float | Sequence[float] = 0.5,
max_detections: int | None = None,
images: Iterable[str]
| Series
| DataFrame
| LazyFrame
| None = None,
weight: str | None = None,
group: str | None = None,
carry: Carry | None = None,
) -> DetectionTable
Match object-level predictions to ground truth: the general entry point.
Takes the long tables detections usually come in — one row per object —
and returns the :class:DetectionTable every metric, statistic and
confidence interval reads. The geometry decides the matcher:
- boxes — a
BBOX_SCHEMAcolumn, or four numbers per object (aList/Arraycolumn, or a tuple of four column names) inbox_format: matched by box IoU (:class:BBoxMatcher); - polygons — a
CONTOUR_SCHEMAcolumn: matched by region IoU (:class:ContourMatcher); - instance masks — one binary mask per object (encoded image bytes or
a 2-D
List/Array): its outline is extracted and matched as a polygon. A mask must hold exactly one connected region; a mask with several (or none) fails the query, naming its image — split it, or pass polygons.
Matching is greedy in descending confidence, each prediction taking the
unmatched ground truth it overlaps most at or above the threshold — the
COCO rule. A sequence of iou_thresholds matches once per threshold
([0.5, 0.55, …, 0.95] for COCO), so a detection that loses its ground
truth at one threshold can claim another at a higher one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predictions
|
DataFrame | LazyFrame
|
One row per predicted object ( |
required |
ground_truth
|
DataFrame | LazyFrame
|
One row per ground-truth object. |
required |
geometry
|
Geometry
|
The geometry column (same name in both tables), or a tuple of four coordinate columns. |
'bbox'
|
box_format
|
BoxFormat | None
|
|
None
|
image_id
|
str
|
The image identifier column. |
COL_IMAGE_ID
|
class_id
|
str | None
|
The class column, or |
COL_CLASS_ID
|
score
|
str
|
The prediction confidence column. |
'score'
|
iou_thresholds
|
float | Sequence[float]
|
One IoU threshold, or several to match at each. |
0.5
|
max_detections
|
int | None
|
Keep each (image, class)'s |
None
|
images
|
Iterable[str] | Series | DataFrame | LazyFrame | None
|
The image population: ids, or a frame with an |
None
|
weight
|
str | None
|
A per-image weight column of the |
None
|
group
|
str | None
|
A per-image subgroup column of the |
None
|
carry
|
Carry | None
|
Further per-image columns of the |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
The |
DetectionTable
|
class: |
DetectionTable
|
position in its (image, class) list; :func: |
|
DetectionTable
|
back to the input row. |
group_objects ¶
group_objects(
predictions: DataFrame | LazyFrame,
ground_truth: DataFrame | LazyFrame,
*,
geometry: str,
image_id: str = COL_IMAGE_ID,
class_id: str | None = COL_CLASS_ID,
score: str = "score",
images: Iterable[str]
| Series
| DataFrame
| LazyFrame
| None = None,
max_detections: int | None = None,
image_columns: Sequence[str] = (),
) -> pl.LazyFrame
Long object tables → one row per (image, class) with object lists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predictions
|
DataFrame | LazyFrame
|
One row per predicted object: |
required |
ground_truth
|
DataFrame | LazyFrame
|
One row per ground-truth object: |
required |
geometry
|
str
|
The geometry column, the same name in both tables. |
required |
image_id
|
str
|
The image identifier column. |
COL_IMAGE_ID
|
class_id
|
str | None
|
The class column, or |
COL_CLASS_ID
|
score
|
str
|
The prediction confidence column. |
'score'
|
images
|
Iterable[str] | Series | DataFrame | LazyFrame | None
|
The image population: ids (or a frame with an |
None
|
max_detections
|
int | None
|
Keep only each (image, class)'s highest-scoring
predictions — COCO's |
None
|
image_columns
|
Sequence[str]
|
Columns of the |
()
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
A lazy frame with |
LazyFrame
|
|
LazyFrame
|
aligned with |
LazyFrame
|
(each object's row index in its input table). Every image of the |
LazyFrame
|
population is crossed with every class of either table. |
Statistics¶
COCO_IOU_THRESHOLDS (0.50:0.05:0.95, COCO's IoU sweep) and FROC_RATES
(0.125 to 8 false positives per image, the standard FROC operating points) are
the defaults the evaluation functions use; both are importable from
polars_cv.metrics.
Statistic ¶
Bases: ABC
A metric as a grouped lazy reduction of a :class:DetectionTable.
Subclasses implement :meth:_by_group; callers use :meth:by_group (any
grouping, lazily) or :meth:value (the ungrouped scalar).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
AP
dataclass
¶
Bases: Statistic
Weighted average precision.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
interpolation
|
APInterpolation
|
|
'all_points'
|
empty_value
property
¶
The value of a group with no detections (a bootstrap draw of none).
require_both_classes
property
¶
Whether a group needs negative images too (two-class rank statistics).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
Recall
dataclass
¶
Bases: Statistic
Weighted recall over every detection: the highest recall reached.
Averaged over classes and IoU thresholds, this is COCO's average recall
(AR) at the detections kept (max_detections).
empty_value
property
¶
The value of a group with no detections (a bootstrap draw of none).
require_both_classes
property
¶
Whether a group needs negative images too (two-class rank statistics).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
PrecisionAt
dataclass
¶
Bases: _AtScore
Weighted precision of the detections scoring at least score_threshold
(1.0 when none do).
empty_value
property
¶
The value of a group with no detections (a bootstrap draw of none).
require_both_classes
property
¶
Whether a group needs negative images too (two-class rank statistics).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
RecallAt
dataclass
¶
Bases: _AtScore
Weighted recall of the detections scoring at least score_threshold.
empty_value
property
¶
The value of a group with no detections (a bootstrap draw of none).
require_both_classes
property
¶
Whether a group needs negative images too (two-class rank statistics).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
F1At
dataclass
¶
Bases: _AtScore
Weighted F1 of the detections scoring at least score_threshold.
empty_value
property
¶
The value of a group with no detections (a bootstrap draw of none).
require_both_classes
property
¶
Whether a group needs negative images too (two-class rank statistics).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
FROCSensitivity
dataclass
¶
Bases: Statistic
FROC sensitivity at fp_per_image false positives per image.
Null where the curve does not reach that rate and extrapolate="none".
empty_value
property
¶
The value of a group with no detections (a bootstrap draw of none).
require_both_classes
property
¶
Whether a group needs negative images too (two-class rank statistics).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
CPM
dataclass
¶
Bases: Statistic
Competition performance metric: the mean FROC sensitivity over
fp_rates (LUNA16 / ANODE09; by default the seven rates 1/8 … 8).
Null where the curve does not reach a rate and extrapolate="none";
LUNA16 reads the curve with extrapolate="flat".
empty_value
property
¶
The value of a group with no detections (a bootstrap draw of none).
require_both_classes
property
¶
Whether a group needs negative images too (two-class rank statistics).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
FROCAUC
dataclass
¶
Bases: Statistic
FROC AUC (see :func:~polars_cv.metrics.froc_auc for the arguments).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
LROCAUC
dataclass
¶
Bases: Statistic
LROC AUC (see :func:~polars_cv.metrics.lroc_auc for the arguments).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
LROCSensitivity
dataclass
¶
Bases: Statistic
LROC sensitivity at a false-positive fraction fpf.
empty_value
property
¶
The value of a group with no detections (a bootstrap draw of none).
require_both_classes
property
¶
Whether a group needs negative images too (two-class rank statistics).
by_group ¶
by_group(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The statistic per group, as a lazy [*keys, name] frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
The detection table. |
required |
keys
|
str | Sequence[str] | None
|
Grouping column(s) on the detections or the metadata.
|
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
support ¶
support(
table: DetectionTable,
keys: list[str],
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The groups where the statistic is defined, as [*keys].
Detection statistics are defined where there is ground truth to find:
a group with no weighted ground-truth mass has no recall (COCO scores
such a class -1 and leaves it out of the mean).
value ¶
The ungrouped statistic as a Python float (one collect).
A table with no row for the statistic (no detections) gives
:attr:empty_value; a null (an operating point the curve does not
reach) is returned as nan.
MeanOver
dataclass
¶
Bases: Statistic
The mean of statistic over facets — mAP is MeanOver(AP()).
The inner statistic is computed per [*keys, *over] and averaged within
keys, over the facets where it is defined (:meth:Statistic.support):
a facet with ground truth but no detections counts as the inner
statistic's empty value (AP 0). A facet with no ground truth is handled by
undefined:
"exclude"leaves it out of the mean, as COCO does (an absent class is not a class the model failed on);"zero"averages it in as0(what :func:~polars_cv.metrics.mean_average_precisionhas always done).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
statistic
|
Statistic
|
The statistic to average. |
required |
over
|
tuple[str, ...]
|
The facet columns; |
(COL_CLASS_ID, 'iou_threshold')
|
undefined
|
Literal['exclude', 'zero']
|
The policy for facets without ground truth (required: the two conventions give different numbers). |
required |
label
|
str | None
|
The output column; |
None
|
per_facet ¶
per_facet(
table: DetectionTable,
keys: str | Sequence[str] | None = None,
*,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
The inner statistic per [*keys, *facets] over the averaging grid.
One row per facet the mean averages over: undefined="exclude" keeps
only facets with ground truth; "zero" keeps every facet of the
metadata, scoring those without ground truth 0.
mean_ap ¶
mean_ap(
interpolation: APInterpolation = "all_points",
*,
undefined: Literal["exclude", "zero"] = "exclude",
) -> MeanOver
mAP: :class:AP averaged over classes and IoU thresholds.
bootstrap_ci ¶
bootstrap_ci(
table: DetectionTable,
statistic: Statistic,
*,
group_by: str | list[str] | None = None,
n_bootstrap: int = 1000,
confidence: float = 0.95,
seed: int | None = None,
sample_col: str | None = None,
strata: str | list[str] | None = None,
weight_rtol: float | None = None,
weight_scheme: WeightScheme = "reestimate",
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
Lazy, group-aware percentile bootstrap CI for any :class:Statistic.
The one CI engine: froc_auc_ci_lazy, lroc_auc_ci_lazy and
average_precision_ci_lazy are this with FROCAUC, LROCAUC and
AP. The point estimate is statistic.by_group(table, group_by); each
replicate is the same statistic on a resample of the table keyed by
bootstrap_id. Resampling, stratification (gt_label and the
weight_scheme's weight cells), entity-level draws and the
degenerate-group rules are those of :func:froc_auc_ci_lazy (see it for
the details).
Images are the resampling unit and group_by partitions the draw, so
keys a statistic averages over (a :class:MeanOver facet such as
class_id or iou_threshold) stay paired: a drawn image brings its
rows for every class and threshold into the same replicate. Pass them as
group_by instead to get one independently-resampled interval per
facet.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
statistic
|
Statistic
|
The statistic to bootstrap, e.g. |
required |
group_by
|
str | list[str] | None
|
Optional grouping column(s). |
None
|
n_bootstrap
|
int
|
Number of bootstrap replicates. |
1000
|
confidence
|
float
|
Confidence level in |
0.95
|
seed
|
int | None
|
Optional RNG seed ( |
None
|
sample_col
|
str | None
|
Optional entity column to resample at the entity level. |
None
|
strata
|
str | list[str] | None
|
Optional metadata column(s) crossed into the weight cells. |
None
|
weight_rtol
|
float | None
|
Relative tolerance within which weights share a cell
( |
None
|
weight_scheme
|
WeightScheme
|
What the weights are — |
'reestimate'
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
|
Core Types¶
DetectionTable
dataclass
¶
Canonical intermediate representation consumed by all metric functions.
Wraps two aligned lazy frames:
- detections — one row per detection with
image_id,class_id,score,is_tp,gt_idx,iou,det_idx. - image_metadata — one row per (image, class) with
n_gts,weight,gt_label, and optionallygroup_id.
When the same image_id (and class_id, when present) appears more
than once in image_metadata (e.g. one rendered image owned by two
cases), FROC/LROC resolve the per-key weight to a single value so detections
are not fan-out-multiplied. Equal weights on the duplicates are the common
case; disagreeing weights are resolved by the metric's weight_agg policy
("first" default, or "min"/"max"/"mean"/"sum") rather than
raising — supplying consistent weights is the caller's responsibility. Prefer
a composite key in image_id when each ownership should be a distinct
evaluation unit.
Use :meth:from_matched to construct with schema validation.
iou_thresholds
property
¶
The IoU thresholds matched: several for a sweep, one for a single matching at a known threshold, none when unknown (pre-matched, or a per-row threshold).
from_matched
classmethod
¶
from_matched(
detections: LazyFrame | DataFrame,
image_meta: LazyFrame | DataFrame,
*,
matching_iou_threshold: float | None = None,
has_iou: bool = True,
) -> DetectionTable
Construct a DetectionTable with planning-time schema validation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
detections
|
LazyFrame | DataFrame
|
Per-detection rows. Must contain columns |
required |
image_meta
|
LazyFrame | DataFrame
|
Per-image metadata. Must contain |
required |
matching_iou_threshold
|
float | None
|
The IoU threshold used by the matcher.
Stored so that :meth: |
None
|
has_iou
|
bool
|
Whether the |
True
|
Returns:
| Type | Description |
|---|---|
DetectionTable
|
Validated |
Raises:
| Type | Description |
|---|---|
ValueError
|
If required columns are missing. |
with_group ¶
Return a copy with group_id set from an existing metadata column.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
group_col
|
str
|
Column in |
required |
Returns:
| Type | Description |
|---|---|
DetectionTable
|
New |
filter_class ¶
Return a copy filtered to a single class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
class_id
|
str
|
The class to retain. |
required |
Returns:
| Type | Description |
|---|---|
DetectionTable
|
Filtered |
class_ids ¶
Return distinct class IDs present in the detections.
Note: triggers a partial collect on the metadata frame.
at_iou_threshold ¶
Return the table evaluated at one IoU threshold.
On a sweep (a table matched at several thresholds) a matched threshold
is selected exactly: its own matching, as the matcher produced it at
that threshold. Any other threshold — and every threshold of a single
matching — re-thresholds a matching instead: the stored iou is
compared against iou_threshold to set is_tp, without matching
again (on a sweep, the matching at the highest threshold below it).
Re-thresholding is not re-matching: a detection that lost its ground
truth at the matching threshold to a higher-scoring one is not given
it back, which COCO's per-threshold matching would do.
.. warning::
Re-thresholding only works reliably when *raising* the threshold
above the original matching IoU. Lowering it has no effect because
detections that were unmatched at the original threshold have no
stored ``gt_idx``/``iou`` to re-evaluate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
iou_threshold
|
float
|
New IoU threshold to apply. |
required |
Returns:
| Type | Description |
|---|---|
DetectionTable
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the table has no IoU values (a pre-matched table
built without |
frames ¶
(detections, image_metadata) for an evaluation grouped by keys.
The frames every metric reads. A sweep holds one matching per IoU threshold, so an evaluation that does not keep the thresholds apart would count each ground truth once per threshold: it is refused here, once, for every metric.
Raises:
| Type | Description |
|---|---|
ValueError
|
A sweep read without |
stack
classmethod
¶
One sweep table from single matchings of the same data, keyed by their IoU threshold. A single entry is returned as it is.
filter_images ¶
Return a copy restricted to a subset of images.
For stratified evaluation: the same metrics over the images of one
device, scan type or size bucket. The stored matcher settings (the
matching IoU threshold, whether iou holds overlaps) are kept, so
the subset is evaluated exactly as the whole table would be.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
images
|
Expr | Iterable[str] | Series
|
Either the ids of the images to keep (an iterable of
|
required |
Returns:
| Type | Description |
|---|---|
DetectionTable
|
Filtered |
Raises:
| Type | Description |
|---|---|
TypeError
|
For a bare string, which would otherwise be read as a sequence of one-character ids. |
to_per_image ¶
One row per image (per (image_id, class_id)) with its detections summarised.
The image metadata (gt_label, weight, n_gts …) plus:
max_score: the highest detection score (null without detections);top_is_tp: whether the highest-scoring detection is a true positive. Tied top scores go to the earliest detection (lowestdet_idx), the order the matcher ranked them in;best_tp_score: the highest true-positive score (null without one).
LROC's per-image commitment (best_tp / top_scoring) reads these.
Every aggregate is native to the streaming engine; no per-image list is
built.
collect ¶
Materialize both frames in one pass.
Uses pl.collect_all so that a shared upstream subplan (e.g. the
matcher's cached contour-extraction/correspond graph, feeding both the
detections and the image metadata) executes once across both frames
rather than once per frame. Common-subplan elimination is what makes the
decode-once, collect-once path hold end to end.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
EngineType
|
Polars' engine. |
'auto'
|
Returns:
| Type | Description |
|---|---|
tuple[DataFrame, DataFrame]
|
Tuple of |
MetricResult
dataclass
¶
Base class for all detection metric results.
Subclasses (e.g. PrecisionRecallResult) add metric-specific convenience
methods with pre-bound column names. This base carries the PR-curve auc
(with optional partial-AUC range and correction), computed through the lazy
_auc_expr integral authority — never a second, eager implementation.
FROC/LROC curve interpolation does not go through here — those helpers
build on the lazy _auc_expr.interpolate_curve_lazy authority directly
(see froc_sensitivity_at_fp / froc_summary_table).
Attributes:
| Name | Type | Description |
|---|---|---|
curve |
DataFrame
|
DataFrame containing the computed metric curve. |
metadata |
dict[str, Any]
|
Arbitrary metadata about the computation. |
auc ¶
auc(
*,
x_col: str,
y_col: str,
x_range: tuple[float, float] | None = None,
correction: CorrectionMethod = None,
) -> float
Compute (partial) AUC under the curve.
Routes through the lazy _auc_expr authority: :func:collapse_curve
reduces the curve to strictly-increasing x with the upper-envelope y (a
curve carries many rows tied at one x — a run that changes only y leaves
x fixed — and collapsing each tie to its max y is deterministic and the
ROC/FROC convention), then :func:trapz_auc / :func:partial_auc
integrates it in one streaming collect.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_col
|
str
|
Column name for the x-axis values. |
required |
y_col
|
str
|
Column name for the y-axis values. |
required |
x_range
|
tuple[float, float] | None
|
Optional |
None
|
correction
|
CorrectionMethod
|
Optional correction for partial AUC.
|
None
|
Returns:
| Type | Description |
|---|---|
float
|
Area under the curve (or partial area). |
PrecisionRecallResult
dataclass
¶
Bases: MetricResult
Precision-Recall curve result.
Attributes:
| Name | Type | Description |
|---|---|---|
curve |
DataFrame
|
DataFrame with |
total_gts |
int
|
Total ground-truth count for this class. |
weighted_gts |
float
|
Weighted ground-truth mass |
class_id |
str
|
Class this curve was computed for. |
auc ¶
auc(
*,
method: Literal[
"all_points", "11_point", "101_point", "trapezoidal"
] = "all_points",
) -> float
Compute Average Precision (AUC of the PR curve).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
Literal['all_points', '11_point', '101_point', 'trapezoidal']
|
Computation method.
|
'all_points'
|
Returns:
| Type | Description |
|---|---|
float
|
Average Precision value. |
precision_at ¶
Precision at a given score threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
threshold
|
float
|
Score threshold. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Precision value. |
recall_at ¶
Recall at a given score threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
threshold
|
float
|
Score threshold. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Recall value. |
ConfusionResult
dataclass
¶
True/false positive and false negative counts at a score threshold.
Detection problems have no true negatives (the background is unbounded), so
only tp, fp and fn are reported — as raw counts of the table's
detections and ground truths, and as weighted masses (each detection or
ground truth carrying its (image[, class]) weight). The derived
:attr:precision / :attr:recall / :attr:f1 read the weighted masses, so
they agree with :func:precision_at_threshold and friends; under unit
weights the two sets of numbers are equal. Call :meth:to_dict for the
legacy count mapping.
Attributes:
| Name | Type | Description |
|---|---|---|
tp |
int
|
True positives (matched detections at or above the threshold). |
fp |
int
|
False positives (unmatched detections at or above the threshold). |
fn |
int
|
False negatives (ground truths with no matching detection). |
weighted_tp |
float
|
|
weighted_fp |
float
|
|
weighted_fn |
float
|
|
Matchers¶
Matcher ¶
Bases: Protocol
Protocol for detection matchers.
A matcher takes a raw data frame with prediction and ground-truth columns
and produces a canonical :class:DetectionTable.
match ¶
match(
data: LazyFrame | DataFrame,
*,
pred_col: str,
gt_col: str,
score_col: str | None = None,
class_col: str | None = None,
image_id_col: str | None = None,
weight_col: str | None = None,
group_col: str | None = None,
carry: Carry | None = None,
) -> DetectionTable
Produce a canonical DetectionTable from raw predictions and GTs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
LazyFrame | DataFrame
|
Input frame with one image/sample per row. |
required |
pred_col
|
str
|
Prediction column name (format depends on matcher). |
required |
gt_col
|
str
|
Ground-truth column name (format depends on matcher). |
required |
score_col
|
str | None
|
Optional confidence score column. |
None
|
class_col
|
str | None
|
Optional class label column. |
None
|
image_id_col
|
str | None
|
Optional image identifier column. |
None
|
weight_col
|
str | None
|
Optional sample weight column. |
None
|
group_col
|
str | None
|
Optional grouping column. |
None
|
carry
|
Carry | None
|
Columns to copy into |
None
|
Returns:
| Type | Description |
|---|---|
DetectionTable
|
A validated |
ContourMatcher ¶
Match detections from heatmaps (or contours) and GT masks (or contours).
It extracts contours from both predictions and GT masks, scores
predictions against the heatmap, and runs greedy IoU matching via
.contour.correspond(). Predictions given as contours with their own
scores skip the extraction and scoring (see :meth:match).
iou_threshold, extraction_threshold, min_contour_area,
min_contour_area_fraction and coverage_tolerance may each be a
Polars expression, read per row — e.g. a physical tolerance,
coverage_tolerance=5.0 / pl.col("spacing_mm"). A literal is checked
here; an expression is checked per row when the query runs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
iou_threshold
|
FloatOrExpr | Sequence[float]
|
IoU threshold for TP matching (literal or per-row
expression), or a sequence of literal thresholds to match once
per threshold (a sweep; see :class: |
0.5
|
extraction_threshold
|
FloatOrExpr
|
Threshold for contour extraction from heatmaps. |
0.1
|
min_contour_area
|
FloatOrExpr
|
Minimum polygon area of an extracted prediction
contour. Contours are traced along pixel edges, so a region's area
is its pixel count (for a region with holes, including the hole
pixels). The default |
0.0
|
auto_resize
|
bool
|
Whether to resize heatmaps to mask shapes automatically.
Ground truth given as contours has no mask to take a size from, so
it needs |
True
|
gt_min_contour_area
|
float
|
Minimum polygon area of a ground-truth contour,
independent of |
1.0
|
match_by
|
str
|
|
'iou'
|
coverage_tolerance
|
FloatOrExpr | None
|
Pixels a GT sample may lie from a prediction and
still count as covered; required with |
None
|
duplicates
|
str
|
|
'false_positive'
|
score_reduction
|
str
|
How a detection is scored from the heatmap pixels
in its region ( |
'max'
|
score_region_mode
|
str
|
Which pixels are a detection's region
( |
'interior'
|
min_contour_area_fraction
|
FloatOrExpr | None
|
Minimum area of an extracted prediction
contour as a fraction of the image's pixel count
( |
None
|
match ¶
match(
data: LazyFrame | DataFrame,
*,
pred_col: str | LazyPipelineExpr,
gt_col: str | LazyPipelineExpr,
score_col: str | None = None,
class_col: str | None = None,
image_id_col: str | None = None,
weight_col: str | None = None,
group_col: str | None = None,
carry: Carry | None = None,
) -> DetectionTable
Produce a DetectionTable from heatmap (or contour) + mask data.
pred_col / gt_col accept either a column name (any format a
polars-cv source supports: nested List[List[...]], VIEW Binary
blob, or fixed-size Array[...] — the format is auto-detected) or a
pre-decoded LazyPipelineExpr. Passing a pre-decoded expr lets the
matcher extend the caller's own decoded buffer instead of decoding a
column itself, so a segmentation graph and the contour extraction can
share one decode (collapsed by CSE) and stream from a single collect.
pred_col may instead name a contour or contour-set column —
for a model that emits masks or polygons with its own per-object
scores. Those contours are the detections as they are (no extraction,
no heatmap scoring), each scored by score_col; auto_resize
must then be False (the contours are in the GT's coordinates).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
LazyFrame | DataFrame
|
Input frame with one image/sample per row. |
required |
pred_col
|
str | LazyPipelineExpr
|
Prediction heatmap column name, or a pre-decoded
|
required |
gt_col
|
str | LazyPipelineExpr
|
Ground-truth mask column name, or a pre-decoded
|
required |
score_col
|
str | None
|
Each prediction contour's score, for contour
predictions: |
None
|
class_col
|
str | None
|
Optional class label column for multi-class metrics. |
None
|
image_id_col
|
str | None
|
Optional image identifier column (defaults to row index). |
None
|
weight_col
|
str | None
|
Optional sample weight column. |
None
|
group_col
|
str | None
|
Optional grouping column. |
None
|
carry
|
Carry | None
|
Columns to copy into |
None
|
Returns:
| Type | Description |
|---|---|
DetectionTable
|
Validated |
BBoxMatcher ¶
Match detections from bounding-box lists via IoU matching.
Expects prediction and ground-truth columns as List[Struct{x, y, width,
height}] (i.e. List[BBOX_SCHEMA]). Scores should be provided as a
separate List[Float64] column aligned with the prediction bboxes.
Matching calls .bbox.correspond(), supplying a confidence order: the
IoU of two boxes is computed analytically and the greedy assignment is the
one .contour.correspond() uses.
Multi-class data is one row per (image, class); each detection keeps its own row's class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
iou_threshold
|
float | Sequence[float]
|
IoU threshold for TP matching, or a sequence of them to
match once per threshold (COCO's |
0.5
|
match ¶
match(
data: LazyFrame | DataFrame,
*,
pred_col: str,
gt_col: str,
score_col: str | None = None,
class_col: str | None = None,
image_id_col: str | None = None,
weight_col: str | None = None,
group_col: str | None = None,
carry: Carry | None = None,
) -> DetectionTable
Produce a DetectionTable from bbox prediction/GT lists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
LazyFrame | DataFrame
|
Input frame with one image/sample per row. |
required |
pred_col
|
str
|
Prediction bboxes column ( |
required |
gt_col
|
str
|
Ground-truth bboxes column ( |
required |
score_col
|
str | None
|
Per-prediction score column ( |
None
|
class_col
|
str | None
|
Optional class label column. |
None
|
image_id_col
|
str | None
|
Optional image identifier column. |
None
|
weight_col
|
str | None
|
Optional sample weight column. |
None
|
group_col
|
str | None
|
Optional grouping column. |
None
|
carry
|
Carry | None
|
Columns to copy into |
None
|
Returns:
| Type | Description |
|---|---|
DetectionTable
|
Validated |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
PreMatchedAdapter ¶
Adapter for data that already has per-detection TP/FP assignments.
Expects a flat table where each row is one detection with at minimum:
score(float) — confidence scoreis_tp(bool) — whether this detection is a true positive
Plus per-image metadata either via image_meta or derived from the
detection rows (n_gts column / defaults).
.. warning::
Without ``image_meta``, the adapter derives the image population
by grouping the detection frame. Images with no detections are
then missing from ``image_metadata``, which silently deletes the
negative population and inflates recall / FP-per-image. Prefer
passing an explicit ``image_meta`` that covers the full evaluation
population whenever any image may carry zero detections.
match ¶
match(
data: LazyFrame | DataFrame,
*,
pred_col: str = "score",
gt_col: str = "is_tp",
score_col: str | None = None,
class_col: str | None = None,
image_id_col: str | None = None,
weight_col: str | None = None,
group_col: str | None = None,
n_gts_col: str | None = None,
gt_label_col: str | None = None,
iou_col: str | None = None,
det_idx_col: str | None = None,
image_meta: LazyFrame | DataFrame | None = None,
carry: Carry | None = None,
) -> DetectionTable
Wrap pre-matched data into a DetectionTable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
LazyFrame | DataFrame
|
Input frame with one row per detection. |
required |
pred_col
|
str
|
Column with confidence scores (aliased to |
'score'
|
gt_col
|
str
|
Column with TP flag (aliased to |
'is_tp'
|
score_col
|
str | None
|
Alias for |
None
|
class_col
|
str | None
|
Class label column. Required when the data carries a
|
None
|
image_id_col
|
str | None
|
Image identifier column (required, or row index used). |
None
|
weight_col
|
str | None
|
Optional sample weight column. |
None
|
group_col
|
str | None
|
Optional grouping column. |
None
|
n_gts_col
|
str | None
|
Column with per-image GT count. |
None
|
gt_label_col
|
str | None
|
Column with per-image positive/negative label. |
None
|
iou_col
|
str | None
|
Optional column with per-detection IoU values. Without
it the table's |
None
|
det_idx_col
|
str | None
|
Optional column with detection index within image. |
None
|
image_meta
|
LazyFrame | DataFrame | None
|
Optional per-image (or per-image-class) frame that
defines the evaluation population. Must use the canonical
column names |
None
|
carry
|
Carry | None
|
Columns to copy into |
None
|
Returns:
| Type | Description |
|---|---|
DetectionTable
|
Validated |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Metric Functions¶
precision_recall_curve ¶
precision_recall_curve(
table: DetectionTable,
*,
class_id: str | None = None,
weight_agg: WeightAgg = "first",
) -> PrecisionRecallResult
Compute a weighted precision-recall curve from a DetectionTable.
Detections are bucketed by distinct confidence score and the buckets accumulated in descending score order — one PR point per distinct score, after the whole tied block, so the curve does not depend on the input row order among equal scores. Precision and recall are weighted (see the module docstring).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
class_id
|
str | None
|
Restrict to a specific class. |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy (see
:func: |
'first'
|
Returns:
| Type | Description |
|---|---|
PrecisionRecallResult
|
|
average_precision ¶
average_precision(
table: DetectionTable,
*,
class_id: str | None = None,
interpolation: APInterpolation = "all_points",
weight_agg: WeightAgg = "first",
) -> float
Compute weighted Average Precision for a single class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
class_id
|
str | None
|
Restrict to a specific class. |
None
|
interpolation
|
APInterpolation
|
|
'all_points'
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
Returns:
| Type | Description |
|---|---|
float
|
AP value in [0, 1]. |
mean_average_precision ¶
mean_average_precision(
table: DetectionTable,
*,
iou_thresholds: list[float] | None = None,
interpolation: APInterpolation = "all_points",
weight_agg: WeightAgg = "first",
) -> float
Compute weighted Mean Average Precision across classes and IoU thresholds.
The mean of :class:~polars_cv.metrics.AP over every (threshold,
class) cell of the metadata — MeanOver(AP(interpolation),
undefined="zero"): a class without ground truth averages in as 0
(COCO leaves it out instead; see :func:~polars_cv.metrics.mean_ap).
On a table matched at several thresholds (a matcher given a sequence of
iou_threshold), each matched threshold is evaluated on its own
matching, as COCO does. Any other threshold — and every threshold of a
single matching — is evaluated by re-thresholding the stored iou
(:meth:DetectionTable.at_iou_threshold), which does not re-match.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
iou_thresholds
|
list[float] | None
|
IoU thresholds to average over. Defaults to the
table's matched thresholds on a sweep, else |
None
|
interpolation
|
APInterpolation
|
AP interpolation method. |
'all_points'
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
Returns:
| Type | Description |
|---|---|
float
|
mAP value in [0, 1]. |
precision_at_threshold ¶
precision_at_threshold(
table: DetectionTable,
threshold: float,
*,
class_id: str | None = None,
weight_agg: WeightAgg = "first",
) -> float
Weighted precision at a given score threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
threshold
|
float
|
Score threshold. |
required |
class_id
|
str | None
|
Optional class filter. |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
Returns:
| Type | Description |
|---|---|
float
|
Precision value; |
float
|
threshold. |
recall_at_threshold ¶
recall_at_threshold(
table: DetectionTable,
threshold: float,
*,
class_id: str | None = None,
weight_agg: WeightAgg = "first",
) -> float
Weighted recall at a given score threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
threshold
|
float
|
Score threshold. |
required |
class_id
|
str | None
|
Optional class filter. |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
Returns:
| Type | Description |
|---|---|
float
|
Recall value; |
f1_at_threshold ¶
f1_at_threshold(
table: DetectionTable,
threshold: float,
*,
class_id: str | None = None,
weight_agg: WeightAgg = "first",
) -> float
Weighted F1 score at a given score threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
threshold
|
float
|
Score threshold. |
required |
class_id
|
str | None
|
Optional class filter. |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
Returns:
| Type | Description |
|---|---|
float
|
F1 value in [0, 1]. |
froc_auc ¶
froc_auc(
table: DetectionTable,
*,
method: Literal[
"trapezoidal", "mann_whitney"
] = "trapezoidal",
fp_range: tuple[float, float] | None = None,
correction: CorrectionMethod = "normalize",
level: Literal["detection", "image"] = "detection",
group_by: str | list[str] | None = None,
weight_agg: WeightAgg = "first",
extrapolate: Extrapolate = "none",
) -> pl.LazyFrame
Compute FROC AUC as a lazy, group-aware frame — one row per group.
The single authority for the FROC integral: the reusable expressions in
:mod:polars_cv.metrics._auc_expr. A scalar is
froc_auc(table, fp_range=(0.0, 8.0)).collect().item().
The trapezoidal method requires an explicit fp_range. FROC's x-axis
is false positives per image — unbounded, with a model-dependent observed
maximum — so an all-range area is not comparable across models and there is
no reasonable default window. With the default correction="normalize" the
result is the mean sensitivity over fp_range (bounded to [0, 1] when
the curve is); pass correction=None for the raw partial area.
A curve stops at the highest FP/image any threshold reaches (detections
from a thresholded mask exist at one operating point). Past that point the
sensitivity was not observed, so by default an fp_range reaching beyond
it gives a null AUC — as :func:froc_summary_table gives a null
sensitivity there; extrapolate="flat" extends the last sensitivity.
Both families are weighted by image_metadata.weight: the trapezoidal path
through the weighted curve, and Mann-Whitney through a weighted rank-sum
(collapse_scores + mann_whitney_auc). Unit weights recover the
unweighted statistic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
method
|
Literal['trapezoidal', 'mann_whitney']
|
|
'trapezoidal'
|
fp_range
|
tuple[float, float] | None
|
|
None
|
correction
|
CorrectionMethod
|
Partial-AUC correction (trapezoidal only). |
'normalize'
|
level
|
Literal['detection', 'image']
|
Mann-Whitney granularity — |
'detection'
|
group_by
|
str | list[str] | None
|
Optional grouping column(s). |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy (see
:func: |
'first'
|
extrapolate
|
Extrapolate
|
How the curve is read past its observed FP/image range:
|
'none'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
A |
LazyFrame
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
froc_curve_lazy ¶
froc_curve_lazy(
table: DetectionTable,
*,
group_by: str | list[str] | None = None,
thresholds: list[float] | None = None,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
Build the FROC curve as a lazy, group-aware frame.
Every cumulative sum restarts per group (:func:grouped_scan) and the weighted denominators
are per-group aggregations rather than eager .item() Python floats. With
group_by=None the whole table is one group; with a grouping column each
group is computed as if on its filtered sub-table (so
froc_curve_lazy(group_by="class_id") per class equals froc_curve_lazy
on table.filter_class(cid)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table produced by a matcher. |
required |
group_by
|
str | list[str] | None
|
Optional column(s) partitioning the curve. May name a column
on the detections (e.g. |
None
|
thresholds
|
list[float] | None
|
Optional explicit score thresholds to keep. |
None
|
weight_agg
|
WeightAgg
|
How to resolve duplicate metadata weights for one
|
'first'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
A |
LazyFrame
|
fp_per_image, sensitivity] |
LazyFrame
|
|
froc_sensitivity_at_fp ¶
froc_sensitivity_at_fp(
table: DetectionTable,
fp_per_image: float,
*,
group_by: str | list[str] | None = None,
thresholds: list[float] | None = None,
weight_agg: WeightAgg = "first",
extrapolate: Extrapolate = "none",
) -> pl.LazyFrame
Interpolate FROC sensitivity at a requested FP/image rate, lazily.
Builds the curve via :func:froc_curve_lazy and interpolates it with the
shared lazy geometry (upper-envelope) — nothing is collected here; the
caller owns the collect.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
fp_per_image
|
float
|
Target false-positive-per-image rate. |
required |
group_by
|
str | list[str] | None
|
Optional grouping column(s); |
None
|
thresholds
|
list[float] | None
|
Optional explicit score thresholds to keep. |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
extrapolate
|
Extrapolate
|
How the curve is read past its observed FP/image range:
|
'none'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
A |
LazyFrame
|
group; |
LazyFrame
|
observed range of the curve and |
LazyFrame
|
A scalar is |
froc_summary_table ¶
froc_summary_table(
table: DetectionTable,
fp_rates: list[float] | None = None,
*,
group_by: str | list[str] | None = None,
weight_agg: WeightAgg = "first",
extrapolate: Extrapolate = "none",
) -> pl.LazyFrame
Sensitivity at standard FP/image operating points, lazily.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
fp_rates
|
list[float] | None
|
Operating points. Defaults to the standard radiology set. |
None
|
group_by
|
str | list[str] | None
|
Optional grouping column(s); |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
extrapolate
|
Extrapolate
|
How the curve is read past its observed FP/image range:
|
'none'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
A |
LazyFrame
|
per group and operating point ( |
LazyFrame
|
|
froc_operating_range ¶
froc_operating_range(
table: DetectionTable,
*,
group_by: str | list[str] | None = None,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
How far each FROC curve reaches, lazily — one row per group.
A curve's last operating point is the highest FP/image any threshold
reaches; beyond it :func:froc_auc and :func:froc_summary_table report
null unless asked to extrapolate. Report this alongside them so a
truncated curve is visible.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
group_by
|
str | list[str] | None
|
Optional grouping column(s). |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
A |
lroc_auc ¶
lroc_auc(
table: DetectionTable,
*,
variant: Literal["best_tp", "top_scoring"] = "best_tp",
method: Literal[
"trapezoidal", "mann_whitney"
] = "trapezoidal",
fpf_range: tuple[float, float] | None = None,
correction: CorrectionMethod = "normalize",
level: Literal["detection", "image"] = "image",
group_by: str | list[str] | None = None,
weight_agg: WeightAgg = "first",
extrapolate: Extrapolate = "none",
) -> pl.LazyFrame
Compute LROC AUC as a lazy, group-aware frame — one row per group.
The single authority for the LROC integral. A scalar is
lroc_auc(table).collect().item().
LROC's x-axis is the false-positive fraction, bounded to [0, 1], so the
trapezoidal method integrates the full [0, 1] domain by default (the
standard LROC figure of merit) and no range need be given — unlike
:func:froc_auc, whose unbounded FP/image axis has no reasonable default.
With the default correction="normalize" the result is the mean
sensitivity over the window; over the full [0, 1] domain that equals the
raw area, so the classic LROC AUC is preserved. Pass correction=None for
the raw partial area, or an explicit fpf_range for a narrower window.
Both families are weighted by image_metadata.weight: the trapezoidal path
through the weighted curve, and Mann-Whitney through a weighted rank-sum
(collapse_scores + mann_whitney_auc). Unit weights recover the
unweighted statistic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
variant
|
Literal['best_tp', 'top_scoring']
|
|
'best_tp'
|
method
|
Literal['trapezoidal', 'mann_whitney']
|
|
'trapezoidal'
|
fpf_range
|
tuple[float, float] | None
|
|
None
|
correction
|
CorrectionMethod
|
Partial-AUC correction (trapezoidal only). |
'normalize'
|
level
|
Literal['detection', 'image']
|
Mann-Whitney granularity — |
'image'
|
group_by
|
str | list[str] | None
|
Optional grouping column(s). |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy (see
:func: |
'first'
|
extrapolate
|
Extrapolate
|
How the curve is read outside its observed FPF range:
|
'none'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
A |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
lroc_curve_lazy ¶
lroc_curve_lazy(
table: DetectionTable,
*,
variant: Literal["best_tp", "top_scoring"] = "best_tp",
group_by: str | list[str] | None = None,
weight_agg: WeightAgg = "first",
) -> pl.LazyFrame
Build the LROC curve as a lazy, group-aware frame.
Cumulative sums restart per group (:func:grouped_scan) and the weighted denominators are
per-group aggregations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table produced by a matcher. |
required |
variant
|
Literal['best_tp', 'top_scoring']
|
|
'best_tp'
|
group_by
|
str | list[str] | None
|
Optional per-image column(s) partitioning the curve (e.g.
|
None
|
weight_agg
|
WeightAgg
|
How to resolve duplicate metadata weights for one
|
'first'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
A |
lroc_sensitivity_at_fpf ¶
lroc_sensitivity_at_fpf(
table: DetectionTable,
fpf: float,
*,
variant: Literal["best_tp", "top_scoring"] = "best_tp",
group_by: str | list[str] | None = None,
weight_agg: WeightAgg = "first",
extrapolate: Extrapolate = "none",
) -> pl.LazyFrame
Interpolate LROC sensitivity at a requested false-positive fraction, lazily.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
fpf
|
float
|
Target false-positive fraction. |
required |
variant
|
Literal['best_tp', 'top_scoring']
|
|
'best_tp'
|
group_by
|
str | list[str] | None
|
Optional grouping column(s); |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy. |
'first'
|
extrapolate
|
Extrapolate
|
How the curve is read outside its observed FPF range:
|
'none'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
A |
LazyFrame
|
|
LazyFrame
|
the curve. The caller collects. |
confusion_at_threshold ¶
confusion_at_threshold(
table: DetectionTable,
threshold: float,
*,
class_id: str | None = None,
weight_agg: WeightAgg = "first",
) -> ConfusionResult
Compute TP, FP, FN counts and weighted masses at a score threshold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
threshold
|
float
|
Score threshold — detections with |
required |
class_id
|
str | None
|
Optional class filter. |
None
|
weight_agg
|
WeightAgg
|
Duplicate-weight resolution policy (see
:func: |
'first'
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
ConfusionResult
|
class: |
Bootstrap confidence intervals (lazy, group-aware)¶
froc_auc_ci_lazy ¶
froc_auc_ci_lazy(
table: DetectionTable,
*,
group_by: str | list[str] | None = None,
n_bootstrap: int = 1000,
confidence: float = 0.95,
seed: int | None = None,
method: Literal[
"trapezoidal", "mann_whitney"
] = "trapezoidal",
fp_range: tuple[float, float] | None = None,
correction: CorrectionMethod = "normalize",
level: Literal["detection", "image"] = "detection",
sample_col: str | None = None,
extrapolate: Extrapolate = "none",
strata: str | list[str] | None = None,
weight_rtol: float | None = None,
weight_scheme: WeightScheme = "reestimate",
) -> pl.LazyFrame
Lazy, group-aware bootstrap confidence interval for FROC AUC.
Returns a LazyFrame with columns [*group_by, auc, ci_lower, ci_upper]
— one row per group (a single row when group_by is None). The auc
column is the deterministic point estimate (froc_auc); only the bounds are
bootstrapped, and they are seed-reproducible. Nothing is collected here — the
caller owns the collect.
Each group resamples within itself (its own images, to its own size),
stratified by gt_label. A degenerate group (no positive targets) keeps
its point estimate but yields null ci_lower/ci_upper instead of raising.
So does a group any of whose replicates is undefined — a resampled curve
that stops short of fp_range under extrapolate="none": its AUC was
not observed, and is never scored as an empty draw's 0.0 — or
degenerate, a draw with no positive image (or, for Mann-Whitney, no
negative one), which only a draw that does not fix the gt_label counts
can make (a cell-stratified or sample_col draw).
Weighted tables. weight_scheme says what the weights are:
"reestimate"(default) — weights estimated from the sample: an importance weightp / q̂over target distributions (conditional ones included), post-stratification, raking over crossed cells. Units (images, orsample_colentities) with the same weight form a weight cell, and each(group, cell)is redrawn to its own size. The weighted statistics are weight-scale-invariant ratios, so a weight that is a function of its cell's count is then exactly the weight re-estimated inside every replicate — computed from the weights given, with no target distributions needed.gt_labelis not crossed with the cells: each cell's positive count is random when the weights were estimated over all images, and fixing it narrows the interval (in simulation, an SE about 10% low and a 95% interval covering 0.90-0.93). Targets conditioned ongt_labelgive each label its own weights, so their cells already carry the label. A group with a single cell (unit weights) keeps thegt_labelstratum, so its draw is the unweighted one."stratified"— each cell's positive count was fixed by the study design: the draw keeps every(group, gt_label, cell)count."fixed"— known weights (design weights, a continuous weight): carried unchanged on thegt_label-stratified draw, with no weight cells, sostrataandweight_rtolraise.
Weights computed per group or globally are both exact (draws never cross a
group). Weights within weight_rtol of each other share a cell. Under
sample_col the draw redraws entities, so a replicate's image mix can
still wander (entities differ in size): each image's weight is then rescaled
by (n_c/N) / (n*_c/N*) — its cell's full-sample over replicate share of
the group's images — which is the p / q̂ weight re-estimated on the
replicate (exactly 1 for image-level draws). With weight cells, a group
with a cell holding a single unit nulls its bounds: that cell has no
bootstrap variance, and a continuous weight (every unit its own cell) would
otherwise report a zero-width interval — use "fixed" for one. A cell of
zero weights (images outside the target) is exempt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
group_by
|
str | list[str] | None
|
Optional grouping column(s). |
None
|
n_bootstrap
|
int
|
Number of bootstrap replicates. |
1000
|
confidence
|
float
|
Confidence level in |
0.95
|
seed
|
int | None
|
Optional RNG seed. |
None
|
method
|
Literal['trapezoidal', 'mann_whitney']
|
|
'trapezoidal'
|
fp_range
|
tuple[float, float] | None
|
|
None
|
correction
|
CorrectionMethod
|
Partial-AUC correction (trapezoidal only); |
'normalize'
|
level
|
Literal['detection', 'image']
|
Mann-Whitney granularity — |
'detection'
|
sample_col
|
str | None
|
Optional entity column (e.g. |
None
|
extrapolate
|
Extrapolate
|
Off-curve policy passed to :func: |
'none'
|
strata
|
str | list[str] | None
|
Optional metadata column(s) crossed into the weight cells — the
columns the weights were computed over (e.g. |
None
|
weight_rtol
|
float | None
|
Relative tolerance within which two weights share a cell
(sorted distinct weights split where consecutive values differ by
more than |
None
|
weight_scheme
|
WeightScheme
|
|
'reestimate'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
|
lroc_auc_ci_lazy ¶
lroc_auc_ci_lazy(
table: DetectionTable,
*,
group_by: str | list[str] | None = None,
n_bootstrap: int = 1000,
confidence: float = 0.95,
seed: int | None = None,
variant: Literal["best_tp", "top_scoring"] = "best_tp",
method: Literal[
"trapezoidal", "mann_whitney"
] = "trapezoidal",
fpf_range: tuple[float, float] | None = None,
correction: CorrectionMethod = "normalize",
level: Literal["detection", "image"] = "image",
sample_col: str | None = None,
extrapolate: Extrapolate = "none",
strata: str | list[str] | None = None,
weight_rtol: float | None = None,
weight_scheme: WeightScheme = "reestimate",
) -> pl.LazyFrame
Lazy, group-aware bootstrap confidence interval for LROC AUC.
The LROC counterpart of :func:froc_auc_ci_lazy; see it for the shared
behavior, weight_scheme included. Returns
[*group_by, auc, ci_lower, ci_upper].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
group_by
|
str | list[str] | None
|
Optional grouping column(s). |
None
|
n_bootstrap
|
int
|
Number of bootstrap replicates. |
1000
|
confidence
|
float
|
Confidence level in |
0.95
|
seed
|
int | None
|
Optional RNG seed ( |
None
|
variant
|
Literal['best_tp', 'top_scoring']
|
|
'best_tp'
|
method
|
Literal['trapezoidal', 'mann_whitney']
|
|
'trapezoidal'
|
fpf_range
|
tuple[float, float] | None
|
|
None
|
correction
|
CorrectionMethod
|
Partial-AUC correction (trapezoidal only); |
'normalize'
|
level
|
Literal['detection', 'image']
|
Mann-Whitney granularity — |
'image'
|
sample_col
|
str | None
|
Optional entity column to resample at the entity level. |
None
|
extrapolate
|
Extrapolate
|
Off-curve policy passed to :func: |
'none'
|
strata
|
str | list[str] | None
|
Optional metadata column(s) crossed into the weight cells (see
:func: |
None
|
weight_rtol
|
float | None
|
Relative tolerance within which weights share a cell
( |
None
|
weight_scheme
|
WeightScheme
|
What the weights are — |
'reestimate'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
|
average_precision_ci_lazy ¶
average_precision_ci_lazy(
table: DetectionTable,
*,
group_by: str | list[str] | None = None,
n_bootstrap: int = 1000,
confidence: float = 0.95,
seed: int | None = None,
class_id: str | None = None,
sample_col: str | None = None,
strata: str | list[str] | None = None,
weight_rtol: float | None = None,
weight_scheme: WeightScheme = "reestimate",
) -> pl.LazyFrame
Lazy, group-aware bootstrap confidence interval for weighted all-points AP.
Returns [*group_by, ap, ci_lower, ci_upper]. The ap column is the
deterministic point estimate (the same all-points estimator as
:func:~polars_cv.metrics.average_precision); only the bounds are
bootstrapped. Nothing is collected here. The AP is weighted by
image_metadata.weight and its resample follows weight_scheme, as for
:func:froc_auc_ci_lazy (see it for the rules).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
DetectionTable
|
Canonical detection table. |
required |
group_by
|
str | list[str] | None
|
Optional grouping column(s). |
None
|
n_bootstrap
|
int
|
Number of bootstrap replicates. |
1000
|
confidence
|
float
|
Confidence level in |
0.95
|
seed
|
int | None
|
Optional RNG seed ( |
None
|
class_id
|
str | None
|
Optional class filter applied before sampling and scoring. |
None
|
sample_col
|
str | None
|
Optional entity column to resample at the entity level. |
None
|
strata
|
str | list[str] | None
|
Optional metadata column(s) crossed into the weight cells. |
None
|
weight_rtol
|
float | None
|
Relative tolerance within which weights share a cell
( |
None
|
weight_scheme
|
WeightScheme
|
What the weights are — |
'reestimate'
|
Returns:
| Type | Description |
|---|---|
LazyFrame
|
|