mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-22 08:48:38 +00:00
Pure compare_prediction (TDD): wall-construction classification hit + signed residuals on floor area, window count, total window area, building-parts count. Plus validate_epc_prediction.py (IO plumbing): drops each cert from its postcode cohort, predicts from the rest on guaranteed inputs only, aggregates the metrics, and reports SAP three ways (pred-calc vs lodged / vs calc-on-actual / vs the neighbour-mean baseline). Smoke run: wall 90.9%, floor-area mean|·| 42.6 m2 (a real signal — template-copied floor area is noisy), SAP pred-calc edges baseline. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
60 lines
2.1 KiB
Python
60 lines
2.1 KiB
Python
"""Per-Property prediction comparison for the EPC Prediction validation harness
|
||
(ADR-0029).
|
||
|
||
`compare_prediction` scores a predicted `EpcPropertyData` against the actual one
|
||
on the accuracy signals the leave-one-out harness aggregates: classification
|
||
matches on the key categoricals (wall / roof / floor construction + insulation,
|
||
construction age band) and residuals on the geometry (window area + count,
|
||
building-parts count, floor area). Pure — the SAP residual is computed in the
|
||
runner, which has the calculator and the lodged SAP.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
|
||
from datatypes.epc.domain.epc_property_data import EpcPropertyData, SapBuildingPart
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class PredictionComparison:
|
||
"""One Property's prediction accuracy: classification hits + geometry
|
||
residuals (predicted − actual)."""
|
||
|
||
wall_construction_correct: bool
|
||
floor_area_residual: float
|
||
building_parts_residual: int
|
||
window_count_residual: int
|
||
total_window_area_residual: float
|
||
|
||
|
||
def _main(epc: EpcPropertyData) -> SapBuildingPart:
|
||
return epc.sap_building_parts[0]
|
||
|
||
|
||
def _total_window_area(epc: EpcPropertyData) -> float:
|
||
return sum(w.window_width * w.window_height for w in epc.sap_windows)
|
||
|
||
|
||
def compare_prediction(
|
||
predicted: EpcPropertyData, actual: EpcPropertyData
|
||
) -> PredictionComparison:
|
||
"""Compare a predicted picture against the actual one, field by field. All
|
||
residuals are signed, predicted − actual."""
|
||
return PredictionComparison(
|
||
wall_construction_correct=(
|
||
_main(predicted).wall_construction == _main(actual).wall_construction
|
||
),
|
||
floor_area_residual=(
|
||
predicted.total_floor_area_m2 - actual.total_floor_area_m2
|
||
),
|
||
building_parts_residual=(
|
||
len(predicted.sap_building_parts) - len(actual.sap_building_parts)
|
||
),
|
||
window_count_residual=(
|
||
len(predicted.sap_windows) - len(actual.sap_windows)
|
||
),
|
||
total_window_area_residual=(
|
||
_total_window_area(predicted) - _total_window_area(actual)
|
||
),
|
||
)
|