Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions src/rtichoke/_viz_spec_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,22 +102,29 @@ def _lift_v2_spec_from_performance_data(
references: list[dict[str, object]] = [
{"type": "horizontal", "value": 1.0, "scope": "global", "label": "Random"}
]
perfect_heights: list[float] = []
for population in populations:
p = prevalence[population]
if p > 0:
perfect_height = 1.0 / p
perfect_heights.append(perfect_height)
references.append(
{
"type": "path",
"scope": "population",
"population": population,
"label": "Perfect Model",
"points": [
{"x": 0.0, "y": 1.0 / p},
{"x": p, "y": 1.0 / p},
{"x": 0.0, "y": perfect_height},
{"x": p, "y": perfect_height},
{"x": 1.0, "y": 1.0},
],
}
)
spec["yAxis"] = {
"label": "Lift",
"domain": [0, _lift_y_axis_upper_bound(performance_data, perfect_heights)],
}
spec["references"] = references
return spec

Expand Down Expand Up @@ -356,8 +363,11 @@ def _lift_times_v2_spec_from_performance_data(
references: list[dict[str, object]] = [
{"type": "horizontal", "value": 1.0, "scope": "global", "label": "Random"}
]
perfect_heights: list[float] = []
for (population, horizon), risk in risks.items():
if risk > 0:
perfect_height = 1.0 / risk
perfect_heights.append(perfect_height)
references.append(
{
"type": "path",
Expand All @@ -366,8 +376,8 @@ def _lift_times_v2_spec_from_performance_data(
"horizon": horizon,
"label": "Perfect Model",
"points": [
{"x": 0.0, "y": 1.0 / risk},
{"x": risk, "y": 1.0 / risk},
{"x": 0.0, "y": perfect_height},
{"x": risk, "y": perfect_height},
{"x": 1.0, "y": 1.0},
],
}
Expand All @@ -382,7 +392,10 @@ def _lift_times_v2_spec_from_performance_data(
"x": "ppcr",
"y": "lift",
"xAxis": {"label": "Predicted Positives (Rate)", "domain": [0, 1]},
"yAxis": {"label": "Lift", "domain": [0, None]},
"yAxis": {
"label": "Lift",
"domain": [0, _lift_y_axis_upper_bound(performance_data, perfect_heights)],
},
"references": references,
}

Expand Down Expand Up @@ -473,6 +486,16 @@ def _gains_population_prevalence(
return prevalence


def _lift_y_axis_upper_bound(
performance_data: pl.DataFrame,
perfect_heights: list[float],
) -> float:
"""Return the finite numeric Lift bound implied by existing plot quantities."""
observed_lift = performance_data["lift"].max()
assert isinstance(observed_lift, (int, float))
return max(1.0, float(observed_lift), *perfect_heights)


def _curve_v2_spec_from_performance_data(
performance_data: pl.DataFrame,
evaluation_metadata: Mapping[str, _EvaluationMetadata],
Expand Down Expand Up @@ -609,6 +632,9 @@ def _curve_v2_spec_from_performance_data(
"x": "ppcr",
"y": "lift",
"xAxis": {"label": "Predicted Positives (Rate)", "domain": [0, 1]},
"yAxis": {"label": "Lift", "domain": [0, None]},
"yAxis": {
"label": "Lift",
"domain": [0, _lift_y_axis_upper_bound(performance_data, [])],
},
"references": [],
}
25 changes: 25 additions & 0 deletions tests/test_lift_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,31 @@ def test_static_lift_v2_spec_single_model():
]


def test_static_lift_v2_y_axis_domain_is_numeric_and_covers_plot():
probs = {
"Population A": np.array([0.1, 0.2, 0.7, 0.9]),
"Population B": np.array([0.1, 0.3, 0.4, 0.8]),
}
reals = {
"Population A": np.array([0, 0, 1, 1]),
"Population B": np.array([0, 0, 0, 1]),
}
spec = _spec(probs, reals)

upper = spec["yAxis"]["domain"][1]
observed_upper = max(float(row["lift"]) for row in spec["data"])
perfect_upper = max(
float(point["y"])
for reference in spec["references"]
if reference["type"] == "path"
for point in reference["points"]
)

assert isinstance(upper, (int, float))
assert np.isfinite(upper)
assert spec["yAxis"]["domain"] == [0, max(1.0, observed_upper, perfect_upper)]


def test_static_lift_v2_spec_shared_population():
probs, reals = _shared_model_inputs()
spec = _spec(probs, reals)
Expand Down
18 changes: 18 additions & 0 deletions tests/test_time_lift_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,24 @@ def test_time_lift_uses_one_evaluation_and_series_per_model_horizon():
}


def test_time_lift_v2_y_axis_domain_is_numeric_and_covers_plot():
probs, reals, times = _shared_inputs()
spec = _spec(probs, reals, times)

upper = spec["yAxis"]["domain"][1]
observed_upper = max(float(row["lift"]) for row in spec["data"])
perfect_upper = max(
float(point["y"])
for reference in spec["references"]
if reference["type"] == "path"
for point in reference["points"]
)

assert isinstance(upper, (int, float))
assert np.isfinite(upper)
assert spec["yAxis"]["domain"] == [0, max(1.0, observed_upper, perfect_upper)]


def test_equal_risk_population_horizons_remain_distinct_lift_reference_owners():
probs = {
"Population A": np.array([0.05, 0.2, 0.7, 0.95]),
Expand Down
Loading