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
# For Debugging
recommendation_impact_df = []
for property_id in recommendations.keys():
for recs_by_type in recommendations[property_id]:
for rec in recs_by_type:
recommendation_impact_df.append(
{
"property_id": property_id,
"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],
"recommendation_id": rec["recommendation_id"],
"type": rec["type"],
"description": rec["description"],
"sap_points": rec["sap_points"],
"co2_equivalent_savings": rec["co2_equivalent_savings"],
"heat_demand": rec["heat_demand"]
}
)
recommendation_impact_df = pd.DataFrame(recommendation_impact_df)
surveyed_uprns = [
10024087855, 121016117, 121016124,
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 = []
# for property_id in recommendations.keys():
# for recs_by_type in recommendations[property_id]:
# for rec in recs_by_type:
# recommendation_impact_df.append(
# {
# "property_id": property_id,
# "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],
# "recommendation_id": rec["recommendation_id"],
# "type": rec["type"],
# "description": rec["description"],
# "sap_points": rec["sap_points"],
# "co2_equivalent_savings": rec["co2_equivalent_savings"],
# "heat_demand": rec["heat_demand"]
# }
# )
# recommendation_impact_df = pd.DataFrame(recommendation_impact_df)
#
# surveyed_uprns = [
# 10024087855, 121016117, 121016124,
# 10024087902, 121016121, 121016128
# ]
actual_impacts_df = pd.DataFrame(
[
# 10024087855
{"uprn": 10024087855, "type": "internal_wall_insulation", "actual_sap_points": 5},
{"uprn": 10024087855, "type": "draught_proofing", "actual_sap_points": 2},
{"uprn": 10024087855, "type": "low_energy_lighting", "actual_sap_points": 0},
{"uprn": 10024087855, "type": "windows_glazing", "actual_sap_points": 4},
# 121016117
{"uprn": 121016117, "type": "internal_wall_insulation", "actual_sap_points": 6},
{"uprn": 121016117, "type": "draught_proofing", "actual_sap_points": 1},
{"uprn": 121016117, "type": "low_energy_lighting", "actual_sap_points": 1},
{"uprn": 121016117, "type": "windows_glazing", "actual_sap_points": 4},
# 121016124
{"uprn": 121016124, "type": "internal_wall_insulation", "actual_sap_points": 8},
{"uprn": 121016124, "type": "low_energy_lighting", "actual_sap_points": 2},
{"uprn": 121016124, "type": "windows_glazing", "actual_sap_points": 5},
# 10024087902
{"uprn": 10024087902, "type": "room_roof_insulation", "actual_sap_points": 16},
{"uprn": 10024087902, "type": "internal_wall_insulation", "actual_sap_points": 2},
{"uprn": 10024087902, "type": "low_energy_lighting", "actual_sap_points": 0},
# 121016121
{"uprn": 121016121, "type": "internal_wall_insulation", "actual_sap_points": 5},
{"uprn": 121016121, "type": "suspended_floor_insulation", "actual_sap_points": 2},
{"uprn": 121016121, "type": "draught_proofing", "actual_sap_points": 1},
{"uprn": 121016121, "type": "windows_glazing", "actual_sap_points": 3},
# 121016128
{"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},
{"uprn": 121016128, "type": "windows_glazing", "actual_sap_points": 3},
]
)
comparison = recommendation_impact_df.merge(
actual_impacts_df, how="inner", on=["uprn", "type"]
)
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())
property_recs["address"]
# 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"])
# # ]
#
# actual_impacts_df = pd.DataFrame(
# [
# # 10024087855
# {"uprn": 10024087855, "type": "internal_wall_insulation", "actual_sap_points": 5},
# {"uprn": 10024087855, "type": "draught_proofing", "actual_sap_points": 2},
# {"uprn": 10024087855, "type": "low_energy_lighting", "actual_sap_points": 0},
# {"uprn": 10024087855, "type": "windows_glazing", "actual_sap_points": 4},
# # 121016117
# {"uprn": 121016117, "type": "internal_wall_insulation", "actual_sap_points": 6},
# {"uprn": 121016117, "type": "draught_proofing", "actual_sap_points": 1},
# {"uprn": 121016117, "type": "low_energy_lighting", "actual_sap_points": 1},
# {"uprn": 121016117, "type": "windows_glazing", "actual_sap_points": 4},
# # 121016124
# {"uprn": 121016124, "type": "internal_wall_insulation", "actual_sap_points": 8},
# {"uprn": 121016124, "type": "low_energy_lighting", "actual_sap_points": 2},
# {"uprn": 121016124, "type": "windows_glazing", "actual_sap_points": 5},
# # 10024087902
# {"uprn": 10024087902, "type": "room_roof_insulation", "actual_sap_points": 16},
# {"uprn": 10024087902, "type": "internal_wall_insulation", "actual_sap_points": 2},
# {"uprn": 10024087902, "type": "low_energy_lighting", "actual_sap_points": 0},
# # 121016121
# {"uprn": 121016121, "type": "internal_wall_insulation", "actual_sap_points": 5},
# {"uprn": 121016121, "type": "suspended_floor_insulation", "actual_sap_points": 2},
# {"uprn": 121016121, "type": "draught_proofing", "actual_sap_points": 1},
# {"uprn": 121016121, "type": "windows_glazing", "actual_sap_points": 3},
# # 121016128
# {"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},
# {"uprn": 121016128, "type": "windows_glazing", "actual_sap_points": 3},
# ]
# )
#
# comparison = recommendation_impact_df.merge(
# actual_impacts_df, how="inner", on=["uprn", "type"]
# )
#
# 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
# training_data = read_dataframe_from_s3_parquet(

View file

@ -36,7 +36,8 @@ SPECIFIC_MEASURES = [
# Solar
"solar_pv",
# Windows Glazing
"windows",
"double_glazing",
"secondary_glazing",
# Mechanical ventilation
"ventilation",
# Other
@ -62,6 +63,7 @@ MEASURE_MAP = {
"roof_insulation": ["loft_insulation", "flat_roof_insulation", "room_roof_insulation"],
"floor_insulation": ["suspended_floor_insulation", "solid_floor_insulation"],
"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 = {
**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(

View file

@ -1,3 +1,5 @@
import pandas as pd
from backend.Property import Property
from typing import List
from recommendations.Costs import Costs
@ -30,6 +32,37 @@ class LightingRecommendations:
self.material = material[0]
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
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]
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
# all recommendation types
@ -163,9 +170,9 @@ class Recommendations:
property_recommendations.append(self.lighting_recommender.recommendation)
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
self.windows_recommender.recommend(phase=phase)
self.windows_recommender.recommend(phase=phase, measures=measures)
if self.windows_recommender.recommendation:
property_recommendations.append(self.windows_recommender.recommendation)
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
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["carbon"] = min(
property_phase_impact["carbon"], rec["co2_equivalent_savings"]

View file

@ -3,6 +3,7 @@ from typing import List
import numpy as np
from backend.Property import Property
from backend.app.plan.schemas import MEASURE_MAP
from etl.epc_clean.epc_attributes.WindowAttributes import WindowAttributes
from recommendations.Costs import Costs
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")
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.
@ -41,14 +42,26 @@ class WindowsRecommendations:
: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
# 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.
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
if not number_of_windows: