import numpy as np import pandas as pd from tqdm import tqdm from pathlib import Path from simulation_system.core.Settings import ( MANDATORY_FIXED_FEATURES, AVERAGE_FIXED_FEATURES, LATEST_FIELD, COMPONENT_FEATURES, RDSAP_RESPONSE, HEAT_DEMAND_RESPONSE, COLUMNS_TO_MERGE_ON, ) from simulation_system.core.DataProcessor import DataProcessor from utils import save_dataframe_to_s3_parquet DATA_DIRECTORY = Path(__file__).parent / "simulation_system" / "data" / "all-domestic-certificates" # TODO: Have a look at temporal features def app(): # Get all the files in the directory # Data glossary: # https://epc.opendatacommunities.org/docs/guidance#glossary # List all subdirectories directories = [entry for entry in DATA_DIRECTORY.iterdir() if entry.is_dir()] dataset = [] cleaning_dataset = [] # 116 # 128048706 # PosixPath('/home/ubuntu/Documents/python/hestia/Model/model_data/simulation_system/data/all-domestic # -certificates/domestic-E09000021-Kingston-upon-Thames') for directory in tqdm(directories): filepath = directory / "certificates.csv" data_processor = DataProcessor(filepath=filepath) df = data_processor.pre_process() cleaning_averages = data_processor.make_cleaning_averages() for uprn, property_data in df.groupby("UPRN", observed=True): # Fixed features - these are property attributes that shouldn't change over time fixed_data = {} # If a property has changed building type, we can ignore the epc rating i.e. this should be 1 unique row if any(property_data[MANDATORY_FIXED_FEATURES].nunique() > 1): continue # Take the latest row for both the LATEST_FEILDS and MANDATORY FIELDS latest_field_data = property_data[LATEST_FIELD].iloc[-1].to_dict() mandatory_field_data = ( property_data[MANDATORY_FIXED_FEATURES].iloc[-1].to_dict() ) # Taking just the last row, which is the percentage change from the latest to previous one only # property_data[AVERAGE_FIXED_FEATURES].fillna(value=0).pct_change().iloc[-1] > 0.1 # Extract the columns that are not all None modified_property_data = DataProcessor.apply_averages_cleaning( data_to_clean=property_data, cleaning_data=cleaning_averages, cols_to_merge_on=COLUMNS_TO_MERGE_ON ) for field in AVERAGE_FIXED_FEATURES: vals = list(modified_property_data[field].dropna().unique()) if len(vals) > 1: # Check the values are too far apart # TODO: we could have multiple values here, why only use the first two? if abs(vals[0] - vals[1]) / vals[0] > 0.1: # Take the more recent value since it's likely to be more accurate vals = [vals[-1]] fixed_data[field] = np.mean(vals) # Combine all fields together fixed_data.update(mandatory_field_data) fixed_data.update(latest_field_data) # We include the lodgement date here as we probably need to factor time into the # model, since EPC standards and rigour have changed over time variable_data = modified_property_data[ COMPONENT_FEATURES + ["LODGEMENT_DATE", RDSAP_RESPONSE, HEAT_DEMAND_RESPONSE] ] # Note: we look at changes between subsequent EPCS, however we could look at other permutations # e.g. first vs second, second vs third and also first vs third property_model_data = [] for idx in range(0, modified_property_data.shape[0] - 1): if idx >= modified_property_data.shape[0] - 1: break starting_record = variable_data.iloc[idx] ending_record = variable_data.iloc[idx + 1] rdsap_change = ( ending_record[RDSAP_RESPONSE] - starting_record[RDSAP_RESPONSE] ) heat_demand_change = ( ending_record[HEAT_DEMAND_RESPONSE] - starting_record[HEAT_DEMAND_RESPONSE] ) # TODO: We need to pre-process the data. For instance, rather than using static for roofs, walls and # floors, we may want to use the U-value. We may also want to handle the (assumed) tags # within descriptions starting_record = starting_record[ COMPONENT_FEATURES + ["LODGEMENT_DATE"] ].add_suffix("_STARTING") ending_record = ending_record[ COMPONENT_FEATURES + ["LODGEMENT_DATE"] ].add_suffix("_ENDING") features = pd.concat([starting_record, ending_record]) property_model_data.append( { "UPRN": uprn, "RDSAP_CHANGE": rdsap_change, "HEAT_DEMAND_CHANGE": heat_demand_change, **fixed_data, **features.to_dict(), } ) dataset.append(property_model_data) cleaning_averages["LOCAL_AUTHORITY"] = df["LOCAL_AUTHORITY"].values[0] cleaning_dataset.append(cleaning_averages) # Store cleaning dataset in s3 as a parquet file cleaning_dataset = pd.concat(cleaning_dataset) save_dataframe_to_s3_parquet( df=cleaning_dataset, bucket_name="retrofit-data-dev", file_key="sap_change_model/cleaning_dataset.parquet", ) output = pd.DataFrame(dataset) output.to_parquet("./dataset.parquet") if __name__ == "__main__": app()