mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
feat(epc-prediction): report floor-area MAE + MAPE vs typical size
Adds a floor_area line giving MAE (m2), MAPE (% of actual), and the typical (median actual) size, so the absolute error reads relative to dwelling size. Corpus: MAE 10.48 m2 / MAPE 13.2% / typical 61 m2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
aea2d7150f
commit
d8f015fb0e
1 changed files with 24 additions and 0 deletions
|
|
@ -29,6 +29,7 @@ from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
||||||
|
from domain.epc_prediction.comparable_properties import Comparable
|
||||||
from domain.epc_prediction.validation import (
|
from domain.epc_prediction.validation import (
|
||||||
evaluate_component_accuracy,
|
evaluate_component_accuracy,
|
||||||
iter_predictions,
|
iter_predictions,
|
||||||
|
|
@ -70,6 +71,7 @@ def main() -> None:
|
||||||
if total:
|
if total:
|
||||||
print(f"CLASSIFICATION {name}: {hits}/{total} = {hits / total:.1%}")
|
print(f"CLASSIFICATION {name}: {hits}/{total} = {hits / total:.1%}")
|
||||||
print()
|
print()
|
||||||
|
_floor_area_error(cohorts)
|
||||||
_residual("floor_area (m2)", accuracy.residuals.get("floor_area", []))
|
_residual("floor_area (m2)", accuracy.residuals.get("floor_area", []))
|
||||||
_residual("window_count", accuracy.residuals.get("window_count", []))
|
_residual("window_count", accuracy.residuals.get("window_count", []))
|
||||||
_residual(
|
_residual(
|
||||||
|
|
@ -131,6 +133,28 @@ def main() -> None:
|
||||||
_sap_line(" floor: PEI |calc(actual) − lodged|", pei_floor)
|
_sap_line(" floor: PEI |calc(actual) − lodged|", pei_floor)
|
||||||
|
|
||||||
|
|
||||||
|
def _floor_area_error(cohorts: list[list[Comparable]]) -> None:
|
||||||
|
"""Floor-area accuracy as MAE (m²) and MAPE (% of the actual), plus the
|
||||||
|
typical (median actual) size — so the absolute error can be read relative to
|
||||||
|
how big dwellings are. The predicted area is the cohort median, set
|
||||||
|
independently of the geo/similarity weighting that drives the categoricals."""
|
||||||
|
pairs = [
|
||||||
|
(predicted.total_floor_area_m2, actual.total_floor_area_m2)
|
||||||
|
for predicted, actual in iter_predictions(cohorts)
|
||||||
|
]
|
||||||
|
valid = [(p, a) for p, a in pairs if a]
|
||||||
|
if not valid:
|
||||||
|
print("RESIDUAL floor_area: (none)")
|
||||||
|
return
|
||||||
|
mae = statistics.mean(abs(p - a) for p, a in valid)
|
||||||
|
mape = statistics.mean(abs(p - a) / a for p, a in valid)
|
||||||
|
typical = statistics.median(a for _, a in valid)
|
||||||
|
print(
|
||||||
|
f"RESIDUAL floor_area: MAE {mae:.2f} m2 | MAPE {mape:.1%} | "
|
||||||
|
f"typical (median actual) {typical:.0f} m2 (n={len(valid)})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _residual(label: str, values: list[float]) -> None:
|
def _residual(label: str, values: list[float]) -> None:
|
||||||
if not values:
|
if not values:
|
||||||
print(f"RESIDUAL {label}: (none)")
|
print(f"RESIDUAL {label}: (none)")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue