setting up the wall mapping functions

This commit is contained in:
Khalim Conn-Kowlessar 2026-01-29 18:45:42 +00:00
parent d1c20944af
commit 28e9f37739
8 changed files with 672 additions and 61 deletions

View file

@ -59,24 +59,24 @@ def app():
Property UPRN
"""
data_folder = "/Users/khalimconn-kowlessar/Documents/hestia/Customers/Hackney"
data_filename = "Domna SHF Wave 3 (3).xlsx"
sheet_name = "Domna Wave 3"
postcode_column = 'Postcode'
address1_column = "Address 1"
address1_method = None
fulladdress_column = None
address_cols_to_concat = ["Address 1"]
data_folder = "/Users/khalimconn-kowlessar/Documents/hestia/Customers/NCHA/20260129 SAL"
data_filename = "NCHA ASSET LIST 1.xlsx"
sheet_name = "NCHA ASSET LIST"
postcode_column = 'POSTCODE'
address1_column = None
address1_method = "house_number_extraction"
fulladdress_column = 'ADDRESS'
address_cols_to_concat = []
missing_postcodes_method = None
landlord_year_built = "Construction Years"
landlord_os_uprn = "UPRN"
landlord_property_type = "Type"
landlord_built_form = "Attachment"
landlord_wall_construction = "Wall type"
landlord_year_built = None
landlord_os_uprn = None
landlord_property_type = "PROPERTY TYPE"
landlord_built_form = "BUILD FORM"
landlord_wall_construction = "wall combined"
landlord_roof_construction = None
landlord_heating_system = None
landlord_existing_pv = None
landlord_property_id = "Row ID"
landlord_property_id = "UPRN"
landlord_sap = None
outcomes_filename = None
outcomes_sheetname = None

View file

@ -427,6 +427,8 @@ PROPERTY_MAPPING = {
'End Terrace': 'unknown',
'Detached': 'unknown',
'Mid-terrace': 'unknown',
'MID - TERRACE': 'unknown'
'MID - TERRACE': 'unknown',
'COMOFF': 'unknown',
'LOTS': 'unknown'
}

View file

@ -354,6 +354,15 @@ WALL_CONSTRUCTION_MAPPINGS = {
'System built Internal': 'insulated system built',
'Cavity: AsBuilt (1976-1982), TimberFrame: AsBuilt': 'cavity unknown insulation',
'Cavity: FilledCavityPlusExternal': 'filled cavity'
'Cavity: FilledCavityPlusExternal': 'filled cavity',
'Cavity, Filled Cavity': 'filled cavity',
'Solid Brick, As Built': 'solid brick unknown insulation',
'Cavity, As Built': 'cavity unknown insulation',
'Sandstone, As Built': 'sandstone or limestone unknown insulation',
'Timber Frame, As Built': 'timber frame unknown insulation',
'Solid Brick, Internal Insulation': 'insulated solid brick',
'Granite or Whinstone, As Built': 'granite or whinstone unknown insulation',
'Solid Brick, External': 'insulated solid brick'
}

View file

View file

@ -0,0 +1,247 @@
import re
from enum import Enum
from typing import Callable, Union, List
class EpcConstructionAgeBand(Enum):
before_1900: str = 'England and Wales: before 1900'
from_1900_to_1929: str = 'England and Wales: 1900-1929'
from_1930_to_1949: str = 'England and Wales: 1930-1949'
from_1950_to_1966: str = 'England and Wales: 1950-1966'
from_1967_to_1975: str = 'England and Wales: 1967-1975'
from_1976_to_1982: str = 'England and Wales: 1976-1982'
from_1983_to_1990: str = 'England and Wales: 1983-1990'
from_1991_to_1995: str = 'England and Wales: 1991-1995'
from_1996_to_2002: str = 'England and Wales: 1996-2002'
from_2003_to_2006: str = 'England and Wales: 2003-2006'
from_2007_to_2011: str = 'England and Wales: 2007-2011'
from_2012_onwards: str = 'England and Wales: 2012-onwards'
from_2012_to_2022: str = 'England and Wales: 2012-2022'
from_2023_onwards: str = 'England and Wales: 2023 onwards'
def start_year(self) -> int:
"""
Extract the starting year of the age band.
"""
value = self.value.lower()
if 'before' in value:
return 0
match = re.search(r'(\d{4})', value)
if not match:
raise ValueError(f"Cannot determine start year from '{self.value}'")
return int(match.group(1))
@classmethod
def from_year_onwards(cls, year: int) -> List["EpcConstructionAgeBand"]:
"""
Return all age bands whose starting year is >= the given year.
"""
return [
band
for band in cls
if band.start_year() >= year
]
class EpcWallDescriptions(Enum):
# Cavity wall descriptions
cavity_insulated_assumed: str = "Cavity wall, as built, insulated (assumed)"
cavity_partial_insulated_assumed: str = "Cavity wall, as built, partial insulation (assumed)"
cavity_no_insulation_assumed: str = "Cavity wall, as built, no insulation (assumed)"
cavity_filled_cavity: str = "Cavity wall, filled cavity"
cavity_internal_insulation: str = "Cavity wall, with internal insulation"
cavity_external_insulation: str = "Cavity wall, with external insulation"
cavity_filled_plus_internal: str = "Cavity wall, filled cavity and internal insulation"
cavity_filled_plus_external: str = "Cavity wall, filled cavity and external insulation"
# Solid wall descriptions
solid_brick_internal_insulation: str = "Solid brick, with internal insulation"
solid_brick_external_insulation: str = "Solid brick, with external insulation"
solid_brick_no_insulation_assumed: str = 'Solid brick, as built, no insulation (assumed)'
solid_brick_partial_insulated_assumed: str = 'Solid brick, as built, partial insulation (assumed)'
solid_brick_insulated_assumed: str = 'Solid brick, as built, insulated (assumed)'
# System
system_external_insulation: str = "System built, with external insulation"
system_internal_insulation: str = "System built, with internal insulation"
system_no_insulation_assumed: str = "System built, as built, no insulation (assumed)"
system_partial_insulated_assumed: str = "System built, as built, partial insulation (assumed)"
system_insulated_assumed: str = "System built, as built, insulated (assumed)"
# Timber
timber_frame_internal_insulation: str = "Timber frame, with internal insulation"
timber_frame_external_insulation: str = "Timber frame, with external insulation"
timber_frame_no_insulation_assumed: str = "Timber frame, as built, no insulation (assumed)"
timber_frame_partial_insulated_assumed: str = "Timber frame, as built, partial insulation (assumed)"
timber_frame_insulated_assumed: str = "Timber frame, as built, insulated (assumed)"
# Granite/whinstone
granite_whinstone_external_insulation: str = "Granite or whin, with external insulation"
granite_whinstone_internal_insulation: str = "Granite or whin, with internal insulation"
granite_whinstone_no_insulation_assumed: str = "Granite or whin, as built, no insulation (assumed)"
granite_whinstone_partial_insulated_assumed: str = "Granite or whin, as built, partial insulation (assumed)"
granite_whinestone_insulated_assumed: str = "Granite or whin, as built, insulated (assumed)"
# Sandstone/limestone
sandstone_limestone_internal_insulation: str = "Sandstone, with internal insulation"
sandstone_limestone_external_insulation: str = "Sandstone, with external insulation"
sandstone_limestone_no_insulation_assumed: str = "Sandstone, as built, no insulation (assumed)"
sandstone_limestone_partial_insulated_assumed: str = "Sandstone, as built, partial insulation (assumed)"
sandstone_limestone_insulated_assumed: str = "Sandstone, as built, insulated (assumed)"
# Cob
cob_as_built_average = "Cob, as built"
cob_as_built_good = "Cob, as built"
class EpcEfficiency(Enum):
VERY_POOR = "Very Poor"
POOR = "Poor"
AVERAGE = "Average"
GOOD = "Good"
VERY_GOOD = "Very Good"
NA = "N/A"
EfficiencyRule = Union[
EpcEfficiency,
Callable[[EpcConstructionAgeBand], EpcEfficiency],
]
def cavity_filled_efficiency(age_band: EpcConstructionAgeBand) -> EpcEfficiency:
""""
Maps cavity filled to efficiency based on construction age band.
:param age_band: EpcConstructionAgeBand
:return: EpcEfficiency
"""
if age_band in {
EpcConstructionAgeBand.from_2023_onwards
}:
return EpcEfficiency.VERY_GOOD
return EpcEfficiency.GOOD
def internal_external_insulation_efficiency(
age_band: EpcConstructionAgeBand,
) -> EpcEfficiency:
"""
Maps:
- cavity unfilled with internal/external insulation to efficiency based on construction age band. We assumed
based on 100mm insulation
- solid brick with internal/external insulation to efficiency based on construction age band. We assumed
based on 100mm insulation
- system built with internal/external insulation to efficiency based on construction age band. We assumed
based on 100mm insulation
All of these wall types have the same behaviour in elmhurst
:param age_band: EpcConstructionAgeBand
:return: EpcEfficiency
"""
if age_band in {
EpcConstructionAgeBand.from_1983_to_1990,
EpcConstructionAgeBand.from_1991_to_1995,
EpcConstructionAgeBand.from_1996_to_2002,
EpcConstructionAgeBand.from_2003_to_2006,
EpcConstructionAgeBand.from_2007_to_2011,
EpcConstructionAgeBand.from_2012_to_2022,
EpcConstructionAgeBand.from_2023_onwards,
}:
return EpcEfficiency.VERY_GOOD
return EpcEfficiency.GOOD
def timber_granite_sandstone_internal_external_efficiency(age_band: EpcConstructionAgeBand) -> EpcEfficiency:
""""
Maps:
- timber frame with internal/external wall insulation to efficiency based on construction age band.
- sandstone/limestone with internal/external wall insulation to efficiency based on construction age band.
- granite/whinstone with internal/external wall insulation to efficiency based on construction age band.
:param age_band: EpcConstructionAgeBand
:return: EpcEfficiency
"""
if age_band in {
EpcConstructionAgeBand.from_2023_onwards
}:
return EpcEfficiency.VERY_GOOD
return EpcEfficiency.GOOD
WALL_DESCRIPTION_METADATA = {
# Note: all function mappings have been defined based on Elmhurst
# Cavity
# value mappings
EpcWallDescriptions.cavity_no_insulation_assumed: EpcEfficiency.POOR,
EpcWallDescriptions.cavity_partial_insulated_assumed: EpcEfficiency.AVERAGE,
EpcWallDescriptions.cavity_insulated_assumed: EpcEfficiency.GOOD,
EpcWallDescriptions.cavity_filled_plus_internal: EpcEfficiency.VERY_GOOD,
EpcWallDescriptions.cavity_filled_plus_external: EpcEfficiency.VERY_GOOD,
# function mappings
EpcWallDescriptions.cavity_filled_cavity: cavity_filled_efficiency,
EpcWallDescriptions.cavity_internal_insulation: internal_external_insulation_efficiency,
EpcWallDescriptions.cavity_external_insulation: internal_external_insulation_efficiency,
# Solid brick
# value mappings
EpcWallDescriptions.solid_brick_no_insulation_assumed: EpcEfficiency.POOR,
EpcWallDescriptions.solid_brick_partial_insulated_assumed: EpcEfficiency.AVERAGE,
EpcWallDescriptions.solid_brick_insulated_assumed: EpcEfficiency.GOOD,
# function mappings
EpcWallDescriptions.solid_brick_internal_insulation: internal_external_insulation_efficiency,
EpcWallDescriptions.solid_brick_external_insulation: internal_external_insulation_efficiency,
# System
# value mappings
EpcWallDescriptions.system_no_insulation_assumed: EpcEfficiency.POOR,
EpcWallDescriptions.system_partial_insulated_assumed: EpcEfficiency.AVERAGE,
EpcWallDescriptions.system_insulated_assumed: EpcEfficiency.GOOD,
# function mappings
EpcWallDescriptions.system_internal_insulation: internal_external_insulation_efficiency,
EpcWallDescriptions.system_external_insulation: internal_external_insulation_efficiency,
# Timber frame
# value mappings
EpcWallDescriptions.timber_frame_no_insulation_assumed: EpcEfficiency.POOR,
EpcWallDescriptions.timber_frame_partial_insulated_assumed: EpcEfficiency.AVERAGE,
EpcWallDescriptions.timber_frame_insulated_assumed: EpcEfficiency.GOOD,
# function mappings
EpcWallDescriptions.timber_frame_internal_insulation: timber_granite_sandstone_internal_external_efficiency,
EpcWallDescriptions.timber_frame_external_insulation: timber_granite_sandstone_internal_external_efficiency,
# Granite / whinstone
EpcWallDescriptions.granite_whinstone_no_insulation_assumed: EpcEfficiency.VERY_POOR,
EpcWallDescriptions.granite_whinstone_partial_insulated_assumed: EpcEfficiency.AVERAGE,
EpcWallDescriptions.granite_whinestone_insulated_assumed: EpcEfficiency.GOOD,
# function mappings
EpcWallDescriptions.granite_whinstone_internal_insulation: timber_granite_sandstone_internal_external_efficiency,
EpcWallDescriptions.granite_whinstone_external_insulation: timber_granite_sandstone_internal_external_efficiency,
# Sandstone / limestone
EpcWallDescriptions.sandstone_limestone_no_insulation_assumed: EpcEfficiency.VERY_POOR,
EpcWallDescriptions.sandstone_limestone_partial_insulated_assumed: EpcEfficiency.AVERAGE,
EpcWallDescriptions.sandstone_limestone_insulated_assumed: EpcEfficiency.GOOD,
# function mappings
EpcWallDescriptions.sandstone_limestone_internal_insulation: timber_granite_sandstone_internal_external_efficiency,
EpcWallDescriptions.sandstone_limestone_external_insulation: timber_granite_sandstone_internal_external_efficiency,
# Cob (special case)
EpcWallDescriptions.cob_as_built_average: EpcEfficiency.AVERAGE,
EpcWallDescriptions.cob_as_built_good: EpcEfficiency.GOOD,
}
def resolve_wall_efficiency(
description: EpcWallDescriptions,
age_band: EpcConstructionAgeBand,
) -> EpcEfficiency:
rule = WALL_DESCRIPTION_METADATA[description]
if isinstance(rule, EpcEfficiency):
return rule
return rule(age_band)

View file

@ -1,14 +1,19 @@
party_map = {
"Before 1900": 'England and Wales: before 1900',
"1900-1929": 'England and Wales: 1900-1929',
"1930-1949": 'England and Wales: 1930-1949',
"1950-1966": 'England and Wales: 1950-1966',
"1967-1975": 'England and Wales: 1967-1975',
"1976-1982": 'England and Wales: 1976-1982',
"1983-1990": 'England and Wales: 1983-1990',
"1991-1995": 'England and Wales: 1991-1995',
"1996-2002": 'England and Wales: 1996-2002',
"2003-2006": 'England and Wales: 2003-2006',
"2007-2011": 'England and Wales: 2007-2011',
"2012 onwards": 'England and Wales: 2012-2021',
from backend.onboarders.epc_descriptions import EpcConstructionAgeBand
parity_map = {
"Before 1900": EpcConstructionAgeBand.before_1900.value,
"1900-1929": EpcConstructionAgeBand.from_1900_to_1929.value,
"1930-1949": EpcConstructionAgeBand.from_1930_to_1949.value,
"1950-1966": EpcConstructionAgeBand.from_1950_to_1966.value,
"1967-1975": EpcConstructionAgeBand.from_1967_to_1975.value,
"1976-1982": EpcConstructionAgeBand.from_1976_to_1982.value,
"1983-1990": EpcConstructionAgeBand.from_1983_to_1990.value,
"1991-1995": EpcConstructionAgeBand.from_1991_to_1995.value,
"1996-2002": EpcConstructionAgeBand.from_1996_to_2002.value,
"2003-2006": EpcConstructionAgeBand.from_2003_to_2006.value,
"2007-2011": EpcConstructionAgeBand.from_2007_to_2011.value,
"2012 onwards": EpcConstructionAgeBand.from_2012_onwards.value,
# Newer age bands, under SAP10
"2012-2022": EpcConstructionAgeBand.from_2012_to_2022.value,
"2023 onwards": EpcConstructionAgeBand.from_2023_onwards.value,
}

View file

@ -0,0 +1,204 @@
def map_cavity_wall_insulation(age_band):
if age_band in [
'England and Wales: before 1900',
'England and Wales: 1900-1929',
'England and Wales: 1930-1949',
'England and Wales: 1950-1966',
'England and Wales: 1967-1975'
]:
return EpcWallDescriptions.cavity_no_insulation_assumed
if age_band in [
'England and Wales: 1976-1982'
]:
return EpcWallDescriptions.cavity_partial_insulated_assumed
if age_band in [
'England and Wales: 1983-1990',
'England and Wales: 1991-1995',
'England and Wales: 1996-2002',
'England and Wales: 2003-2006',
'England and Wales: 2007-2011',
'England and Wales: 2012-2022',
'England and Wales: 2023 onwards',
]:
return EpcWallDescriptions.cavity_insulated_assumed
raise NotImplementedError(f"Age band {age_band} not handled for cavity wall as built insulation mapping")
def map_solid_wall_insulation(age_band):
if age_band in [
'England and Wales: before 1900', 'England and Wales: 1900-1929', 'England and Wales: 1930-1949',
'England and Wales: 1967-1975'
]:
return EpcWallDescriptions.solid_brick_no_insulation_assumed
if age_band in [
'England and Wales: 1976-1982'
]:
return EpcWallDescriptions.solid_brick_partial_insulated_assumed
if age_band in [
'England and Wales: 1983-1990', 'England and Wales: 1991-1995', 'England and Wales: 1996-2002',
'England and Wales: 2003-2006', 'England and Wales: 2007-2011', 'England and Wales: 2012-2022',
'England and Wales: 2023 onwards',
]:
return EpcWallDescriptions.solid_brick_insulated_assumed
def map_timber_frame_wall_insulation(age_band):
# No insulation (Poor)
if age_band in [
'England and Wales: before 1900',
'England and Wales: 1900-1929',
'England and Wales: 1930-1949',
]:
return EpcWallDescriptions.timber_frame_no_insulation_assumed
# Partial insulation (Average)
if age_band in [
'England and Wales: 1950-1966',
'England and Wales: 1967-1975',
]:
return EpcWallDescriptions.timber_frame_partial_insulated_assumed
# Insulated (Good)
if age_band in [
'England and Wales: 1976-1982',
'England and Wales: 1983-1990',
'England and Wales: 1991-1995',
'England and Wales: 1996-2002',
'England and Wales: 2003-2006',
'England and Wales: 2007-2011',
'England and Wales: 2012-2022',
'England and Wales: 2023 onwards',
]:
return EpcWallDescriptions.timber_frame_insulated_assumed
# TODO: Unknown / pre-1930 handling
raise NotImplementedError(f"Age band {age_band} not handled for timber frame wall insulation mapping")
def map_system_build_wall_insulation(age_band):
# No insulation (Poor)
if age_band in [
'England and Wales: before 1900',
'England and Wales: 1900-1929',
'England and Wales: 1930-1949',
'England and Wales: 1950-1966',
'England and Wales: 1967-1975',
]:
return EpcWallDescriptions.system_no_insulation_assumed
# Partial insulation (Average)
if age_band in [
'England and Wales: 1976-1982',
]:
return EpcWallDescriptions.system_partial_insulated_assumed
# Insulated (Good)
if age_band in [
'England and Wales: 1983-1990',
'England and Wales: 1991-1995',
'England and Wales: 1996-2002',
'England and Wales: 2003-2006',
'England and Wales: 2007-2011',
'England and Wales: 2012-2022',
'England and Wales: 2023 onwards',
]:
return EpcWallDescriptions.system_insulated_assumed
# TODO: Unknown / early system build handling
raise NotImplementedError(f"Age band {age_band} not handled for system build wall insulation mapping")
def map_granite_wall_insulation(age_band):
# No insulation (Very Poor)
if age_band in [
'England and Wales: before 1900',
'England and Wales: 1900-1929',
'England and Wales: 1930-1949',
'England and Wales: 1950-1966',
'England and Wales: 1967-1975',
]:
return EpcWallDescriptions.granite_whinstone_no_insulation_assumed
# Partial insulation (Average)
if age_band in [
'England and Wales: 1976-1982',
]:
return EpcWallDescriptions.granite_whinstone_partial_insulated_assumed
# Insulated (Good)
if age_band in [
'England and Wales: 1983-1990',
'England and Wales: 1991-1995',
'England and Wales: 1996-2002',
'England and Wales: 2003-2006',
'England and Wales: 2007-2011',
'England and Wales: 2012-2022',
'England and Wales: 2023 onwards',
]:
return EpcWallDescriptions.granite_whinestone_insulated_assumed
raise NotImplementedError(f"Age band {age_band} not handled for granite wall insulation mapping")
def map_sandstone_wall_insulation(age_band):
# No insulation (Very Poor)
if age_band in [
'England and Wales: before 1900',
'England and Wales: 1900-1929',
'England and Wales: 1930-1949',
'England and Wales: 1950-1966',
'England and Wales: 1967-1975',
]:
return EpcWallDescriptions.sandstone_limestone_no_insulation_assumed
# Partial insulation (Average)
if age_band in [
'England and Wales: 1976-1982',
]:
return EpcWallDescriptions.sandstone_limestone_partial_insulated_assumed
# Insulated (Good)
if age_band in [
'England and Wales: 1983-1990',
'England and Wales: 1991-1995',
'England and Wales: 1996-2002',
'England and Wales: 2003-2006',
'England and Wales: 2007-2011',
'England and Wales: 2012-2022',
'England and Wales: 2023 onwards',
]:
return EpcWallDescriptions.sandstone_limestone_insulated_assumed
raise NotImplementedError(f"Age band {age_band} not handled for sandstone wall insulation mapping")
def map_cob_wall_insulation(age_band):
# Cob, as built (Average)
if age_band in [
'England and Wales: before 1900',
'England and Wales: 1900-1929',
'England and Wales: 1930-1949',
'England and Wales: 1950-1966',
'England and Wales: 1967-1975',
'England and Wales: 1976-1982',
]:
return EpcWallDescriptions.cob_as_built_average
# Cob, as built (Good)
if age_band in [
'England and Wales: 1983-1990',
'England and Wales: 1991-1995',
'England and Wales: 1996-2002',
'England and Wales: 2003-2006',
'England and Wales: 2007-2011',
'England and Wales: 2012-2022',
'England and Wales: 2023 onwards',
]:
return EpcWallDescriptions.cob_as_built_good
raise NotImplementedError(f"Age band {age_band} not handled for cob wall insulation mapping")

View file

@ -1,8 +1,9 @@
import pandas as pd
from etl.epc.DataProcessor import construction_age_bounds_map
from backend.onboarders.mappings.property_type import parity_map as property_map
from backend.onboarders.mappings.age_band import party_map as age_band_map
from backend.onboarders.mappings.age_band import parity_map as age_band_map
from backend.onboarders.mappings.built_form import parity_map as built_form_map
from onboarders.epc_descriptions import EpcWallDescriptions, EpcConstructionAgeBand
def check_nulls(data, original_column, mapped_column):
@ -17,7 +18,6 @@ def check_nulls(data, original_column, mapped_column):
# Sample input data
data = pd.read_excel(
"/Users/khalimconn-kowlessar/Documents/hestia/Customers/Peabody/Nov 2025 Consulting Project/2025_11_11 - Peabody "
"- Data Extracts for Domna.xlsx",
@ -30,18 +30,6 @@ data = pd.read_excel(
# 3) Indicate already installed measures
# ------------ construction_age_band ------------
# Map to EPC age bands
# def construction_date_to_band(year):
# if pd.isnull(year):
# return None
# # Get the year from the date which is numpy datetime format
# for label, ranges in construction_age_bounds_map.items():
# if ranges["l"] <= year <= ranges["u"]:
# return label
# raise NotImplementedError("year out of bounds")
#
#
# data["construction_age_band"] = pd.to_datetime(data["Construction Date"]).dt.year.apply(construction_date_to_band)
data["construction_age_band"] = data["Construction Years"].map(age_band_map)
@ -59,30 +47,186 @@ assert pd.isnull(data["built_form"]).sum() == 0, "Some built forms were not mapp
# ------------ Wall Construction ------------
data["walls_combined"] = data["Wall Construction"] + "+" + data["Wall Insulation"].fillna("Unknown Insulation")
data["Wall Insulation"].value_counts()
data["Wall Construction"].value_counts()
# Unique combindations
wall_mapping = {
# Cavity walls
('Cavity', 'FilledCavity'): EpcWallDescriptions.cavity_filled_cavity.value,
('Cavity', 'Internal'): EpcWallDescriptions.cavity_internal_insulation.value,
('Cavity', 'External'): EpcWallDescriptions.cavity_external_insulation.value,
('Cavity', 'FilledCavityPlusInternal'): EpcWallDescriptions.cavity_filled_plus_internal.value,
('Cavity', 'FilledCavityPlusExternal'): EpcWallDescriptions.cavity_filled_plus_external.value,
('Cavity', 'AsBuilt'): None, # To be classified
('Cavity', 'Unknown'): None, # To be classified
as_built_map = {
"Cavity": {"insulated_age_bands": [], "partial_insulated_age_bands": []},
"Solid Brick": {"insulated_age_bands": [], "partial_insulated_age_bands": []},
"System": {"insulated_age_bands": [], "partial_insulated_age_bands": []},
"Timber Frame": {"insulated_age_bands": [], "partial_insulated_age_bands": []},
"Sandstone": {"insulated_age_bands": [], "partial_insulated_age_bands": []},
"Granite": {"insulated_age_bands": [], "partial_insulated_age_bands": []},
"Cob": {"insulated_age_bands": [], "partial_insulated_age_bands": []},
# System built walls
('System', 'External'): EpcWallDescriptions.system_external_insulation.value,
('System', 'Internal'): EpcWallDescriptions.system_internal_insulation.value,
('System', 'AsBuilt'): None, # To be classified
('System', 'Unknown'): None,
# Timber Frame walls
('Timber Frame', 'Internal'): EpcWallDescriptions.timber_frame_internal_insulation.value,
('Timber Frame', 'External'): EpcWallDescriptions.timber_frame_external_insulation.value,
('Timber Frame', 'AsBuilt'): None, # To be classified
('Timber Frame', 'Unknown'): None,
# Solid Brick walls
('Solid Brick', 'External'): EpcWallDescriptions.solid_brick_external_insulation.value,
('Solid Brick', 'Internal'): EpcWallDescriptions.solid_brick_internal_insulation.value,
('Solid Brick', 'AsBuilt'): None, # To be classified
('Solid Brick', 'Unknown'): None,
# Granite walls
('Granite', 'External'): EpcWallDescriptions.granite_whinstone_external_insulation.value,
("Granite", 'Internal'): EpcWallDescriptions.granite_whinstone_internal_insulation.value,
('Granite', 'AsBuilt'): None,
('Granite', 'Unknown'): None,
# Sandstone walls
('Sandstone', 'Internal'): EpcWallDescriptions.sandstone_limestone_internal_insulation.value,
('Sandstone', 'External'): EpcWallDescriptions.sandstone_limestone_external_insulation.value,
('Sandstone', 'Unknown'): None,
('Sandstone', 'AsBuilt'): None,
# Cob walls
('Cob', 'AsBuilt'): None,
}
def map_wall_construction(wall_constuction, wall_insulation, construction_age_band):
if wall_insulation == "AsBuilt":
# Deduce based on wall construction and age band
bands = as_built_map.get(wall_constuction, None)
if bands is None:
raise NotImplementedError(f"Wall construction {wall_constuction} not in as built map")
def map_cavity_wall_insulation(age_band: EpcConstructionAgeBand):
if age_band.start_year() < 1976:
return EpcWallDescriptions.cavity_no_insulation_assumed
# We check if the age band is in insulated or partial insulated, and if neither, we assume uninsulated
if age_band == EpcConstructionAgeBand.from_1976_to_1982:
return EpcWallDescriptions.cavity_partial_insulated_assumed
if age_band in EpcConstructionAgeBand.from_year_onwards(1983):
return EpcWallDescriptions.cavity_insulated_assumed
raise NotImplementedError(f"Age band {age_band} not handled for cavity wall as built insulation mapping")
def map_solid_wall_insulation(age_band: EpcConstructionAgeBand):
if age_band.start_year() < 1976:
return EpcWallDescriptions.solid_brick_no_insulation_assumed
if age_band == EpcConstructionAgeBand.from_1976_to_1982:
return EpcWallDescriptions.solid_brick_partial_insulated_assumed
if age_band in EpcConstructionAgeBand.from_year_onwards(1983):
return EpcWallDescriptions.solid_brick_insulated_assumed
raise NotImplementedError(
f"Age band {age_band.value} not handled for solid wall insulation mapping"
)
def map_timber_frame_wall_insulation(age_band: EpcConstructionAgeBand):
if age_band.start_year() < 1950:
return EpcWallDescriptions.timber_frame_no_insulation_assumed
if age_band.start_year() < 1976:
return EpcWallDescriptions.timber_frame_partial_insulated_assumed
if age_band in EpcConstructionAgeBand.from_year_onwards(1976):
return EpcWallDescriptions.timber_frame_insulated_assumed
raise NotImplementedError(
f"Age band {age_band.value} not handled for timber frame wall insulation mapping"
)
def map_system_build_wall_insulation(age_band: EpcConstructionAgeBand):
if age_band.start_year() < 1976:
return EpcWallDescriptions.system_no_insulation_assumed
if age_band == EpcConstructionAgeBand.from_1976_to_1982:
return EpcWallDescriptions.system_partial_insulated_assumed
if age_band in EpcConstructionAgeBand.from_year_onwards(1983):
return EpcWallDescriptions.system_insulated_assumed
raise NotImplementedError(
f"Age band {age_band.value} not handled for system build wall insulation mapping"
)
def map_granite_wall_insulation(age_band: EpcConstructionAgeBand):
if age_band.start_year() < 1976:
return EpcWallDescriptions.granite_whinstone_no_insulation_assumed
if age_band == EpcConstructionAgeBand.from_1976_to_1982:
return EpcWallDescriptions.granite_whinstone_partial_insulated_assumed
if age_band in EpcConstructionAgeBand.from_year_onwards(1983):
return EpcWallDescriptions.granite_whinestone_insulated_assumed
raise NotImplementedError(
f"Age band {age_band.value} not handled for granite wall insulation mapping"
)
def map_sandstone_wall_insulation(age_band: EpcConstructionAgeBand):
if age_band.start_year() < 1976:
return EpcWallDescriptions.sandstone_limestone_no_insulation_assumed
if age_band == EpcConstructionAgeBand.from_1976_to_1982:
return EpcWallDescriptions.sandstone_limestone_partial_insulated_assumed
if age_band in EpcConstructionAgeBand.from_year_onwards(1983):
return EpcWallDescriptions.sandstone_limestone_insulated_assumed
raise NotImplementedError(
f"Age band {age_band.value} not handled for sandstone wall insulation mapping"
)
def map_cob_wall_insulation(age_band: EpcConstructionAgeBand):
if age_band.start_year() < 1983:
return EpcWallDescriptions.cob_as_built_average
if age_band in EpcConstructionAgeBand.from_year_onwards(1983):
return EpcWallDescriptions.cob_as_built_good
raise NotImplementedError(
f"Age band {age_band.value} not handled for cob wall insulation mapping"
)
AS_BUILT_WALL_CLASSIFIERS = {
"Cavity": map_cavity_wall_insulation,
"Solid Brick": map_solid_wall_insulation,
"Timber Frame": map_timber_frame_wall_insulation,
"System": map_system_build_wall_insulation,
"Granite": map_granite_wall_insulation,
"Sandstone": map_sandstone_wall_insulation,
"Cob": map_cob_wall_insulation,
}
data["landlord_wall_description"] = (
data[["Wall Construction", "Wall Insulation"]]
.apply(tuple, axis=1)
.map(wall_mapping)
)
def fill_as_built(row):
if row.landlord_wall_description is not None:
return row.landlord_wall_description
classifier = AS_BUILT_WALL_CLASSIFIERS.get(row["Wall Construction"])
if classifier is None:
return None
return classifier(row.construction_age_band)
data["landlord_wall_description"] = data.apply(fill_as_built, axis=1)
for _, x in data.iterrows():
if x["construction_age_band"] == "England and Wales: 2012-2021":
de
# Variables we want to map
# 'Org Ref', 'Address 1', 'Address 2', 'Address 3', 'Postcode', 'Type',