Model/domain/epc_prediction
Khalim Conn-Kowlessar bbfcf1385b Resolve a historic EPC's stable attributes into cohort code spaces 🟩
property_type/built_form reuse the override code mapping; wall material
prefix -> RdSAP wall_construction; E&W age-band strings -> Table S1
letters; main_fuel display text (stripping " (not community)") -> modern
RdSAP-20/21 codes; TFA -> float. Unresolvable -> None (filter inactive).
Roof construction is deferred: its RdSAP code table isn't pinned in-repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:22:31 +00:00
..
__init__.py feat(epc-prediction): Comparable Properties selection ladder (ADR-0029) 2026-06-13 23:44:57 +00:00
comparable_properties.py refactor(epc-prediction): PR review — rename ComparableProperty, relocate PredictionTarget 2026-06-16 13:34:44 +00:00
epc_prediction.py Predict ventilation kind from the cohort mode 🟩 2026-06-26 10:54:11 +00:00
historic_conditioning.py Resolve a historic EPC's stable attributes into cohort code spaces 🟩 2026-07-06 08:22:31 +00:00
prediction_comparison.py feat(epc-prediction): roof-insulation +/-1-bucket reporting 2026-06-15 14:04:18 +00:00
prediction_target.py refactor(epc-prediction): PR review — rename ComparableProperty, relocate PredictionTarget 2026-06-16 13:34:44 +00:00
README.md docs(epc-prediction): module README + end-to-end showcase test 2026-06-16 04:13:30 +00:00
validation.py refactor(epc-prediction): PR review — rename ComparableProperty, relocate PredictionTarget 2026-06-16 13:34:44 +00:00

EPC Prediction

Predict a structured EpcPropertyData for an EPC-less UK home from its postcode neighbours, so it flows through the rest of the pipeline (Baseline, Bill Derivation, Modelling) exactly like a home that has an EPC. It is deterministic neighbour synthesis — cohort modes + a coherent template + per-component weighting — not ML. ~30% of UK homes (typically long-tenure) have no EPC.

  • Design: ADR-0029 (algorithm), ADR-0030 (validation), ADR-0031 (production wiring).
  • Glossary: see EPC Prediction, Comparable Properties, Component Accuracy, EPC Anomaly Flag in CONTEXT.md.

The flow (gap-fill)

Ingestion: a Property has no lodged EPC (epc_fetcher.get_by_uprn → None)
   │
   ├─ resolve its attributes (property_type/built_form/wall) from Landlord Overrides
   │     └─ property_type unknown? → GATED OUT, not predicted (no national defaults)
   ├─ build a PredictionTarget (postcode + coordinates + attributes)
   ├─ ComparableProperties repo: fetch the postcode cohort (search → per-cert → coords)
   ├─ select_comparables(): filter to the reference cohort (type-hard, built-form-soft)
   ├─ EpcPrediction.predict(): synthesise the picture (modes + template + donor + weights)
   └─ persist to the Property's PREDICTED slot  (source = "predicted")
            │
Modelling/Baseline: Property.effective_epc returns the predicted picture
            (source_path == "predicted"), scored like any other Effective EPC.

A lodged EPC always wins — prediction is last-resort gap-fill.

Where the pieces live

Concern File
Synthesis (modes, template, heating donor, geo/recency/similarity weights) epc_prediction.py
Cohort selection (filter-then-relax ladder) comparable_properties.py
Target assembly + eligibility gate prediction_target.py
Cohort IO port + EPC-API/geospatial adapter repositories/comparable_properties/
Predicted-EPC persistence (source discriminator) repositories/epc/
predicted source path on the aggregate domain/property/property.py
Ingestion wiring (gate → predict → persist) orchestration/ingestion_orchestrator.py
Validation (leave-one-out, component-first) + ratcheting gate validation.py, tests/domain/epc_prediction/test_component_accuracy_gate.py

See it run

tests/e2e/test_epc_prediction_e2e.py — the whole flow against the real DB + repos, only the external HTTP clients faked. Start there.

Status

Algorithm + validation: built. Production gap-fill wiring: built behind seams (slices 5a5e). Two things finish it — a DB migration and the property_overrides read adapter — see the wiring handover and the migration note. EPC Anomaly Flags (predict for every home, compare to lodged) is the designed next step the storage already supports.

Run the tests

PYTHONPATH=. python -m pytest tests/e2e/test_epc_prediction_e2e.py \
  tests/domain/epc_prediction tests/orchestration/test_ingestion_prediction.py \
  tests/repositories/comparable_properties tests/repositories/epc/test_epc_predicted_slot.py \
  -o addopts="" -q