reviewing model performance for vectis - complete

This commit is contained in:
Khalim Conn-Kowlessar 2024-09-28 17:03:41 +01:00
parent 32d702e930
commit fb3fef5a4a
6 changed files with 142 additions and 82 deletions

View file

@ -730,77 +730,80 @@ async def trigger_plan(body: PlanTriggerRequest):
recommendations[property_id] = recommendations_with_impact recommendations[property_id] = recommendations_with_impact
# For Debugging # For Debugging
recommendation_impact_df = [] # recommendation_impact_df = []
for property_id in recommendations.keys(): # for property_id in recommendations.keys():
for recs_by_type in recommendations[property_id]: # for recs_by_type in recommendations[property_id]:
for rec in recs_by_type: # for rec in recs_by_type:
recommendation_impact_df.append( # recommendation_impact_df.append(
{ # {
"property_id": property_id, # "property_id": property_id,
"uprn": [p.uprn for p in input_properties if p.id == property_id][0], # "uprn": [p.uprn for p in input_properties if p.id == property_id][0],
"address": [p.address for p in input_properties if p.id == property_id][0], # "address": [p.address for p in input_properties if p.id == property_id][0],
"recommendation_id": rec["recommendation_id"], # "recommendation_id": rec["recommendation_id"],
"type": rec["type"], # "type": rec["type"],
"description": rec["description"], # "description": rec["description"],
"sap_points": rec["sap_points"], # "sap_points": rec["sap_points"],
"co2_equivalent_savings": rec["co2_equivalent_savings"], # "co2_equivalent_savings": rec["co2_equivalent_savings"],
"heat_demand": rec["heat_demand"] # "heat_demand": rec["heat_demand"]
} # }
) # )
recommendation_impact_df = pd.DataFrame(recommendation_impact_df) # recommendation_impact_df = pd.DataFrame(recommendation_impact_df)
#
surveyed_uprns = [ # surveyed_uprns = [
10024087855, 121016117, 121016124, # 10024087855, 121016117, 121016124,
10024087902, 121016121, 121016128 # 10024087902, 121016121, 121016128
]
recommendation_impact_df = recommendation_impact_df[recommendation_impact_df["uprn"].isin(surveyed_uprns)]
# recommendation_impact_df = recommendation_impact_df[recommendation_impact_df["type"].isin(
# ["windows_glazing", "internal_wall_insulation"])
# ] # ]
# recommendation_impact_df = recommendation_impact_df[recommendation_impact_df["uprn"].isin(surveyed_uprns)]
actual_impacts_df = pd.DataFrame( # # recommendation_impact_df = recommendation_impact_df[recommendation_impact_df["type"].isin(
[ # # ["windows_glazing", "internal_wall_insulation"])
# 10024087855 # # ]
{"uprn": 10024087855, "type": "internal_wall_insulation", "actual_sap_points": 5}, #
{"uprn": 10024087855, "type": "draught_proofing", "actual_sap_points": 2}, # actual_impacts_df = pd.DataFrame(
{"uprn": 10024087855, "type": "low_energy_lighting", "actual_sap_points": 0}, # [
{"uprn": 10024087855, "type": "windows_glazing", "actual_sap_points": 4}, # # 10024087855
# 121016117 # {"uprn": 10024087855, "type": "internal_wall_insulation", "actual_sap_points": 5},
{"uprn": 121016117, "type": "internal_wall_insulation", "actual_sap_points": 6}, # {"uprn": 10024087855, "type": "draught_proofing", "actual_sap_points": 2},
{"uprn": 121016117, "type": "draught_proofing", "actual_sap_points": 1}, # {"uprn": 10024087855, "type": "low_energy_lighting", "actual_sap_points": 0},
{"uprn": 121016117, "type": "low_energy_lighting", "actual_sap_points": 1}, # {"uprn": 10024087855, "type": "windows_glazing", "actual_sap_points": 4},
{"uprn": 121016117, "type": "windows_glazing", "actual_sap_points": 4}, # # 121016117
# 121016124 # {"uprn": 121016117, "type": "internal_wall_insulation", "actual_sap_points": 6},
{"uprn": 121016124, "type": "internal_wall_insulation", "actual_sap_points": 8}, # {"uprn": 121016117, "type": "draught_proofing", "actual_sap_points": 1},
{"uprn": 121016124, "type": "low_energy_lighting", "actual_sap_points": 2}, # {"uprn": 121016117, "type": "low_energy_lighting", "actual_sap_points": 1},
{"uprn": 121016124, "type": "windows_glazing", "actual_sap_points": 5}, # {"uprn": 121016117, "type": "windows_glazing", "actual_sap_points": 4},
# 10024087902 # # 121016124
{"uprn": 10024087902, "type": "room_roof_insulation", "actual_sap_points": 16}, # {"uprn": 121016124, "type": "internal_wall_insulation", "actual_sap_points": 8},
{"uprn": 10024087902, "type": "internal_wall_insulation", "actual_sap_points": 2}, # {"uprn": 121016124, "type": "low_energy_lighting", "actual_sap_points": 2},
{"uprn": 10024087902, "type": "low_energy_lighting", "actual_sap_points": 0}, # {"uprn": 121016124, "type": "windows_glazing", "actual_sap_points": 5},
# 121016121 # # 10024087902
{"uprn": 121016121, "type": "internal_wall_insulation", "actual_sap_points": 5}, # {"uprn": 10024087902, "type": "room_roof_insulation", "actual_sap_points": 16},
{"uprn": 121016121, "type": "suspended_floor_insulation", "actual_sap_points": 2}, # {"uprn": 10024087902, "type": "internal_wall_insulation", "actual_sap_points": 2},
{"uprn": 121016121, "type": "draught_proofing", "actual_sap_points": 1}, # {"uprn": 10024087902, "type": "low_energy_lighting", "actual_sap_points": 0},
{"uprn": 121016121, "type": "windows_glazing", "actual_sap_points": 3}, # # 121016121
# 121016128 # {"uprn": 121016121, "type": "internal_wall_insulation", "actual_sap_points": 5},
{"uprn": 121016128, "type": "internal_wall_insulation", "actual_sap_points": 6}, # {"uprn": 121016121, "type": "suspended_floor_insulation", "actual_sap_points": 2},
{"uprn": 121016128, "type": "suspended_floor_insulation", "actual_sap_points": 1}, # {"uprn": 121016121, "type": "draught_proofing", "actual_sap_points": 1},
{"uprn": 121016128, "type": "draught_proofing", "actual_sap_points": 1}, # {"uprn": 121016121, "type": "windows_glazing", "actual_sap_points": 3},
{"uprn": 121016128, "type": "low_energy_lighting", "actual_sap_points": 1}, # # 121016128
{"uprn": 121016128, "type": "windows_glazing", "actual_sap_points": 3}, # {"uprn": 121016128, "type": "internal_wall_insulation", "actual_sap_points": 6},
] # {"uprn": 121016128, "type": "suspended_floor_insulation", "actual_sap_points": 1},
) # {"uprn": 121016128, "type": "draught_proofing", "actual_sap_points": 1},
# {"uprn": 121016128, "type": "low_energy_lighting", "actual_sap_points": 1},
comparison = recommendation_impact_df.merge( # {"uprn": 121016128, "type": "windows_glazing", "actual_sap_points": 3},
actual_impacts_df, how="inner", on=["uprn", "type"] # ]
) # )
#
property_recs = recommendation_impact_df[recommendation_impact_df["uprn"] == 121016128] # comparison = recommendation_impact_df.merge(
property = [p for p in input_properties if p.uprn == 121016128][0] # actual_impacts_df, how="inner", on=["uprn", "type"]
print(property.data["current-energy-efficiency"]) # )
print(property_recs["sap_points"].sum()) #
property_recs["address"] # print(recommendation_impact_df.groupby(["uprn"])["sap_points"].sum())
# property_recs = recommendation_impact_df[recommendation_impact_df["uprn"] == 121016128]
# property = [p for p in input_properties if p.uprn == 121016128][0]
# print(property.data["current-energy-efficiency"])
# print(property_recs["sap_points"].sum())
# print(property_recs["type"])
# print(float(property.data["current-energy-efficiency"]) + property_recs["sap_points"].sum())
# recommendations[property.id][2][0]["simulation_config"]
# from utils.s3 import read_dataframe_from_s3_parquet # from utils.s3 import read_dataframe_from_s3_parquet
# training_data = read_dataframe_from_s3_parquet( # training_data = read_dataframe_from_s3_parquet(

View file

@ -36,7 +36,8 @@ SPECIFIC_MEASURES = [
# Solar # Solar
"solar_pv", "solar_pv",
# Windows Glazing # Windows Glazing
"windows", "double_glazing",
"secondary_glazing",
# Mechanical ventilation # Mechanical ventilation
"ventilation", "ventilation",
# Other # Other
@ -62,6 +63,7 @@ MEASURE_MAP = {
"roof_insulation": ["loft_insulation", "flat_roof_insulation", "room_roof_insulation"], "roof_insulation": ["loft_insulation", "flat_roof_insulation", "room_roof_insulation"],
"floor_insulation": ["suspended_floor_insulation", "solid_floor_insulation"], "floor_insulation": ["suspended_floor_insulation", "solid_floor_insulation"],
"heating": ["boiler_upgrade", "high_heat_retention_storage_heater", "air_source_heat_pump"], "heating": ["boiler_upgrade", "high_heat_retention_storage_heater", "air_source_heat_pump"],
"windows": ["double_glazing", "secondary_glazing"],
} }

View file

@ -224,7 +224,9 @@ class FloorRecommendations(Definitions):
simulation_config = { simulation_config = {
**floor_simulation_config, **floor_simulation_config,
"floor_thermal_transmittance_ending": new_u_value, # We don't simulate the impact using this U-value, but rather the average because this
# variable is way too volatile. Will likely be removed from the model
"floor_thermal_transmittance_ending": 0.685593,
} }
self.recommendations.append( self.recommendations.append(

View file

@ -1,3 +1,5 @@
import pandas as pd
from backend.Property import Property from backend.Property import Property
from typing import List from typing import List
from recommendations.Costs import Costs from recommendations.Costs import Costs
@ -30,6 +32,37 @@ class LightingRecommendations:
self.material = material[0] self.material = material[0]
self.recommendation = [] self.recommendation = []
@classmethod
def get_sap_limit(cls, lighting_energy_efficiency: str, lighting_proportion: float):
"""
Lighting seems to be a more straight forward measure to estimate SAP points for, based on the starting
energy efficiency rating.
We seem to have the following brackes based on % of LEDs in outlets
Very poor: 0 - 9%
Poor: 10 - 24%
Average: 25 - 44%
Good: 45 - 69%
Very good: 70 - 100%
:return:
"""
if lighting_energy_efficiency == "Very Good":
return 0
if lighting_energy_efficiency in ["Good", "Average"]:
return cls.SAP_LOWER_LIMIT
# If lighting_energy_efficiency is missing, we'll use the proportion of low energy lighting
if not lighting_energy_efficiency or pd.isnull(lighting_energy_efficiency):
if lighting_proportion >= 0.7:
return 0
if lighting_proportion >= 0.25:
return cls.SAP_LOWER_LIMIT
return cls.SAP_LIMIT
return cls.SAP_LIMIT
@staticmethod @staticmethod
def estimate_lighting_impact(number_of_bulbs: int): def estimate_lighting_impact(number_of_bulbs: int):
""" """

View file

@ -80,6 +80,13 @@ class Recommendations:
inclusions_full = [MEASURE_MAP[x] if x in MEASURE_MAP else x for x in self.inclusions] inclusions_full = [MEASURE_MAP[x] if x in MEASURE_MAP else x for x in self.inclusions]
exclusions_full = [MEASURE_MAP[x] if x in MEASURE_MAP else x for x in self.exclusions] exclusions_full = [MEASURE_MAP[x] if x in MEASURE_MAP else x for x in self.exclusions]
# We need to unlist any lists, but we should check if they're lists first
inclusions_full = [
item for sublist in inclusions_full for item in (sublist if isinstance(sublist, list) else [sublist])
]
exclusions_full = [
item for sublist in exclusions_full for item in (sublist if isinstance(sublist, list) else [sublist])
]
# If inclusions and exclusions are empty, it means that nothing was specified, so we allow # If inclusions and exclusions are empty, it means that nothing was specified, so we allow
# all recommendation types # all recommendation types
@ -163,9 +170,9 @@ class Recommendations:
property_recommendations.append(self.lighting_recommender.recommendation) property_recommendations.append(self.lighting_recommender.recommendation)
phase += 1 phase += 1
if "windows" in measures and "mixed_glazing" not in non_invasive_recommendation_types: if "mixed_glazing" not in non_invasive_recommendation_types:
# If we have a mixed glazing recommendation, we prioritise this over the windows recommendation # If we have a mixed glazing recommendation, we prioritise this over the windows recommendation
self.windows_recommender.recommend(phase=phase) self.windows_recommender.recommend(phase=phase, measures=measures)
if self.windows_recommender.recommendation: if self.windows_recommender.recommendation:
property_recommendations.append(self.windows_recommender.recommendation) property_recommendations.append(self.windows_recommender.recommendation)
phase += 1 phase += 1
@ -538,11 +545,11 @@ class Recommendations:
# For the moment, we cap the number of SAP points that can be achieved by LEDs at 2 # For the moment, we cap the number of SAP points that can be achieved by LEDs at 2
if rec["type"] == "low_energy_lighting": if rec["type"] == "low_energy_lighting":
lighting_sap_limit = LightingRecommendations.get_sap_limit(
property_instance.data["lighting-energy-eff"],
property_instance.lighting["low_energy_proportion"]
)
if property_instance.data["low-energy-lighting"] < 50:
lighting_sap_limit = LightingRecommendations.SAP_LIMIT
else:
lighting_sap_limit = LightingRecommendations.SAP_LOWER_LIMIT
property_phase_impact["sap"] = min(property_phase_impact["sap"], lighting_sap_limit) property_phase_impact["sap"] = min(property_phase_impact["sap"], lighting_sap_limit)
property_phase_impact["carbon"] = min( property_phase_impact["carbon"] = min(
property_phase_impact["carbon"], rec["co2_equivalent_savings"] property_phase_impact["carbon"], rec["co2_equivalent_savings"]

View file

@ -3,6 +3,7 @@ from typing import List
import numpy as np import numpy as np
from backend.Property import Property from backend.Property import Property
from backend.app.plan.schemas import MEASURE_MAP
from etl.epc_clean.epc_attributes.WindowAttributes import WindowAttributes from etl.epc_clean.epc_attributes.WindowAttributes import WindowAttributes
from recommendations.Costs import Costs from recommendations.Costs import Costs
from recommendations.recommendation_utils import override_costs, check_simulation_difference from recommendations.recommendation_utils import override_costs, check_simulation_difference
@ -32,7 +33,7 @@ class WindowsRecommendations:
raise ValueError("There should only be one window glazing material") raise ValueError("There should only be one window glazing material")
self.glazing_material = self.glazing_material[0] self.glazing_material = self.glazing_material[0]
def recommend(self, phase=0): def recommend(self, measures=None, phase=0):
""" """
This method will recommend the best possible glazing options for a property. This method will recommend the best possible glazing options for a property.
@ -41,14 +42,26 @@ class WindowsRecommendations:
:return: :return:
""" """
measures = MEASURE_MAP["windows"] if measures is None else measures
# If we have no windows recs, leave
if not any(x in measures for x in MEASURE_MAP["windows"]):
return
# If the property is in a conservation area or is a listed building, it becomes more difficult to install # If the property is in a conservation area or is a listed building, it becomes more difficult to install
# double glazing. Therefore, we don't recommend it. It is still possible but is not practical as it # double glazing. Therefore, we don't recommend it. It is still possible but is not practical as it
# requires planning permission and might require a more expensive window type, such as timber. # requires planning permission and might require a more expensive window type, such as timber.
number_of_windows = self.property.number_of_windows number_of_windows = self.property.number_of_windows
is_secondary_glazing = self.property.restricted_measures or (
self.property.windows["glazing_type"] == "secondary" if "double_glazing" in measures and "secondary_glazing" not in measures:
) is_secondary_glazing = False
elif "secondary_glazing" in measures and "double_glazing" not in measures:
is_secondary_glazing = True
else:
is_secondary_glazing = self.property.restricted_measures or (
self.property.windows["glazing_type"] == "secondary"
)
windows_area = self.property.windows_area windows_area = self.property.windows_area
if not number_of_windows: if not number_of_windows: