diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index 2067d796..50b8a837 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -196,7 +196,6 @@ async def trigger_plan(body: PlanTriggerRequest): ) model_api = ModelApi(portfolio_id=body.portfolio_id, timestamp=created_at) - # model_api.MODEL_PREFIXES = ['sap_change_predictions', 'carbon_change_predictions'] all_predictions = { "sap_change_predictions": pd.DataFrame(), @@ -219,9 +218,6 @@ async def trigger_plan(body: PlanTriggerRequest): for key, scored in predictions_dict.items(): all_predictions[key] = pd.concat([all_predictions[key], scored]) - # TODO: TEMP - # all_predictions["heat_demand_predictions"] = all_predictions["sap_change_predictions"].copy() - # Insert the predictions into the recommendations and run the optimiser # TODO: If a recommendation has a negative impact on SAP, we should remove it - this seems to have become a # possibility with heating system diff --git a/backend/ml_models/AnnualBillSavings.py b/backend/ml_models/AnnualBillSavings.py index 99fae4db..4a433a7f 100644 --- a/backend/ml_models/AnnualBillSavings.py +++ b/backend/ml_models/AnnualBillSavings.py @@ -10,13 +10,13 @@ class AnnualBillSavings: AVERAGE_ELECTRICITY_CONSUMPTION = 2700 AVERAGE_GAS_CONSUMPTION = 11500 - # Latest price cap figures from Ofgem are for January 2024 - # https://www.ofgem.gov.uk/publications/changes-energy-price-cap-1-january-2024 - ELECTRICITY_PRICE_CAP = 0.29 - GAS_PRICE_CAP = 0.07 + # Latest price cap figures from Ofgem are for April 2024 + # https://www.ofgem.gov.uk/publications/new-energy-price-cap-level-april-june-2024-starts-today + ELECTRICITY_PRICE_CAP = 0.245 + GAS_PRICE_CAP = 0.0604 # This is a weighted mean of the price caps, using the consumption figures above as weights - PRICE_FACTOR = 0.11183098591549295 + PRICE_FACTOR = 0.09549999999999999 EPC_BANDS = ["G", "F", "E", "D", "C", "B", "A"] diff --git a/etl/customers/gla_croydon_demo/asset_list.py b/etl/customers/gla_croydon_demo/asset_list.py index 01220d0a..a0475807 100644 --- a/etl/customers/gla_croydon_demo/asset_list.py +++ b/etl/customers/gla_croydon_demo/asset_list.py @@ -35,20 +35,20 @@ def app(): # 79% D, 19% E, 1% F, 0.2% G - it probably makes the most sense to focus on E and D properties epc_data["CURRENT_ENERGY_RATING"].value_counts(normalize=True) - # For the purpose of the sample, take the properties have surveys done in the last 2 years - # This gives us 1167 remaining properties - two_years_ago = pd.Timestamp.now() - pd.DateOffset(days=int(2.5 * 365)) - epc_data = epc_data[epc_data["LODGEMENT_DATE"] >= two_years_ago] + # For the purpose of the sample, take the properties have surveys done in the last 3 years + # This gives us 1351 remaining properties + three_years_ago = pd.Timestamp.now() - pd.DateOffset(days=int(3 * 365)) + epc_data = epc_data[epc_data["LODGEMENT_DATE"] >= three_years_ago] # Archetype 1: defined below: # 1) House # 2) Unfilled cavity # 3) A roof that could be insulated (flat or pitched with no more than 50mm insulation) - # 4) EPC E - # 12 properties + # 4) EPC E or D + # 24 properties archetype_1_sample = epc_data[ epc_data["PROPERTY_TYPE"].isin(["House"]) & - (epc_data["CURRENT_ENERGY_RATING"] == "E") & + (epc_data["CURRENT_ENERGY_RATING"].isin(["D", "E"])) & epc_data["WALLS_DESCRIPTION"].isin(["Cavity wall, as built, no insulation (assumed)"]) & epc_data["ROOF_DESCRIPTION"].isin( [ @@ -69,10 +69,10 @@ def app(): # 2) Unfilled cavity # 3) Another property above # 4) EPC E - # 14 properties here + # 57 properties here archetype_2_sample = epc_data[ epc_data["PROPERTY_TYPE"].isin(["Flat"]) & - (epc_data["CURRENT_ENERGY_RATING"] == "E") & + (epc_data["CURRENT_ENERGY_RATING"].isin(["E", "D"])) & epc_data["WALLS_DESCRIPTION"].isin(["Cavity wall, as built, no insulation (assumed)"]) & epc_data["ROOF_DESCRIPTION"].isin( [ @@ -88,11 +88,18 @@ def app(): # 2) Solid brick wall # 3) House # 4) Pitched roof with no insulation - # Just 1 property (more expensive to retrofit) + # Just 7 properties (more expensive to retrofit) archetype_3_sample = epc_data[ epc_data["PROPERTY_TYPE"].isin(["House"]) & - (epc_data["CURRENT_ENERGY_RATING"] == "F") & - epc_data["ROOF_DESCRIPTION"].isin(["Pitched, no insulation"]) + (epc_data["CURRENT_ENERGY_RATING"].isin(["F", "G"])) & + epc_data["ROOF_DESCRIPTION"].isin( + [ + "Pitched, no insulation", + "Pitched, limited insulation (assumed)", + "Pitched, 100 mm loft insulation", + "Pitched, no insulation (assumed)", + ] + ) ] archetype_3_sample_asset_list = archetype_3_sample[["UPRN", "ADDRESS1", "POSTCODE"]].copy() archetype_3_sample_asset_list["ARCHETYPE"] = "Archetype 3" @@ -101,15 +108,18 @@ def app(): # 1) Maisonette # 2) Empty cavity # 3) EPC E - # 14 properties here + # 16 properties here archetype_4_sample = epc_data[ epc_data["PROPERTY_TYPE"].isin(["Maisonette"]) & - epc_data["WALLS_DESCRIPTION"].isin(["Cavity wall, as built, no insulation (assumed)"]) + epc_data["WALLS_DESCRIPTION"].isin( + ["Cavity wall, as built, no insulation (assumed)"] + ) ] + archetype_4_sample_asset_list = archetype_4_sample[["UPRN", "ADDRESS1", "POSTCODE"]].copy() archetype_4_sample_asset_list["ARCHETYPE"] = "Archetype 4" - # 41 total properties + # 104 total properties asset_list = pd.concat( [ archetype_1_sample_asset_list, diff --git a/etl/customers/gla_croydon_demo/slides.py b/etl/customers/gla_croydon_demo/slides.py index 5954f604..ebca7dc3 100644 --- a/etl/customers/gla_croydon_demo/slides.py +++ b/etl/customers/gla_croydon_demo/slides.py @@ -38,7 +38,50 @@ def app(): asset_list = read_csv_from_s3( "retrofit-plan-inputs-dev", f"{USER_ID}/{portfolio_id}/inputs.csv" ) + asset_list = pd.DataFrame(asset_list) # Get the properties for the portfolio properties = get_properties_with_default_recommendations(session, portfolio_id) properties_df = pd.DataFrame(properties) + + # We now pull the data for the property details + property_details = get_property_details_by_portfolio_id(session, portfolio_id) + property_details_df = pd.DataFrame(property_details) + # Merge on uprn + property_details_df = property_details_df.merge( + properties_df[["uprn", "id"]].rename(columns={"id": "property_id"}), + on="property_id" + ) + + plans = get_plan_by_portfolio_id(session, portfolio_id) + plans_df = pd.DataFrame(plans) + + # Unnest the recommendations. Each recommendation is a list of dictionaries + recommendations_exploded = properties_df["recommendations"].explode().tolist() + recommendations_df = pd.DataFrame([r for r in recommendations_exploded if not pd.isnull(r)]) + # Add uprn on + recommendations_df = recommendations_df.merge( + properties_df[["uprn", "id"]].rename(columns={"id": "property_id"}), + how="left", + on="property_id" + ) + + # Summary information by each archetype + archetype_1 = asset_list[asset_list["archetype"] == "Archetype 1"] + + recommendations_arch_1_summary = create_recommendations_summary( + recommendations_df[recommendations_df["uprn"].astype(str).isin(archetype_1["uprn"].values)], + properties_df[properties_df["uprn"].astype(str).isin(archetype_1["uprn"].values)], + SAP_TARGET_1 + ) + + # Take the mean, median and maximum of each value + arch_1_recommendation_means = recommendations_arch_1_summary.mean() + + arch_1_property_details = property_details_df[ + property_details_df["uprn"].astype(str).isin(archetype_1["uprn"].values) + ] + + arch_1_property_details_means = arch_1_property_details.mean() + + arch_1_recommendation_means["total_bill_savings"] / arch_1_property_details_means["adjusted_energy_consumption"]