diff --git a/demand_generator/electrical_demand/ElectricalDemand.py b/demand_generator/electrical_demand/ElectricalDemand.py index 3b773e8cd3b97c4ef55e3af0b9bc36c0e129ae24..ef3acb8a330272337075ca3a095a9a22a2a0e0d8 100644 --- a/demand_generator/electrical_demand/ElectricalDemand.py +++ b/demand_generator/electrical_demand/ElectricalDemand.py @@ -163,16 +163,6 @@ class ElectricalDemand: b = datetime.datetime(self.year, self.seasons[p][2], self.seasons[p][3], 23, 59) - # Old Version - # -------------------------------------------------------- - # new_df.update(pd.DataFrame.merge( - # tmp_df[tmp_df['period'] == p[:-1]], time_df[a:b], - # left_on=left_cols, right_on=right_cols, - # how='inner', left_index=True).sort_index().drop( - # ['hour_of_day'], 1)) - # -------------------------------------------------------- - # New Version - # Tested with pandas version 1.3.1 new_df.update(pd.DataFrame.merge( tmp_df[tmp_df['period'] == p[:-1]], time_df[a:b], left_on=left_cols, right_on=right_cols, how='inner').sort_values( diff --git a/input_profile_processor/input_profile_processor.py b/input_profile_processor/input_profile_processor.py index d1aa0cb393b4ea3c021be48b80eb79a13e2e09fc..58137e835a75cbbecea82ca0e8aae754612f14c5 100644 --- a/input_profile_processor/input_profile_processor.py +++ b/input_profile_processor/input_profile_processor.py @@ -33,22 +33,20 @@ def process_input_profiles(input_profile_dict, t_start, t_horizon, t_step): input_profiles = {} for input_profile_name, input_profile_config in input_profile_dict.items(): if input_profile_config[1] == 'generate': - profile = generate_profile(input_profile_config[0], input_profile_config[2:], input_profiles, t_start, - t_horizon, t_step) + profile = generate_profile(input_profile_config[0], input_profile_config[2:], input_profiles, t_start, t_horizon, t_step) elif input_profile_config[1] == 'modify': - profile = modify_profile(input_profile_config[0], input_profile_config[2:], input_profiles, t_start, - t_horizon, t_step) + profile = modify_profile(input_profile_config[0], input_profile_config[2:], input_profiles, t_start, t_horizon, t_step) else: - profile = pd.read_csv(input_profile_config[1], index_col=0) + profile = pd.read_csv(input_profile_config[1], index_col = 0) try: - profile.index = pd.to_datetime(profile.index, format='%d-%m-%Y %H:%M:%S') + profile.index = pd.to_datetime(profile.index, format = '%d-%m-%Y %H:%M:%S') except ValueError: - profile.index = pd.to_datetime(profile.index, format='%Y-%m-%d %H:%M:%S') + profile.index = pd.to_datetime(profile.index, format = '%Y-%m-%d %H:%M:%S') profile = profile.resample(str(t_step) + 'H').mean().interpolate('linear') input_profiles[input_profile_name] = (input_profile_config[0], profile) for input_profile_name, (input_profile_type, input_profile) in input_profiles.items(): - input_profile = input_profile[t_start:t_start + pd.Timedelta(hours=t_horizon * t_step - t_step)] + input_profile = input_profile[t_start:t_start + pd.Timedelta(hours = t_horizon * t_step - t_step)] if input_profile_type == 'irradiance': lambda_1 = 14.122 @@ -56,8 +54,7 @@ def process_input_profiles(input_profile_dict, t_start, t_horizon, t_step): phi = 52.21 psi_f = 0 beta = 30 - input_profile = generate_g_t_series(input_profile, beta, psi_f, phi, lambda_st, lambda_1, t_start, - t_horizon, t_step) + input_profile = generate_g_t_series(input_profile, beta, psi_f, phi, lambda_st, lambda_1, t_start, t_horizon, t_step) input_profile = input_profile.squeeze() input_profile.set_axis(list(range(t_horizon)), inplace = True) @@ -66,14 +63,13 @@ def process_input_profiles(input_profile_dict, t_start, t_horizon, t_step): def generate_profile(profile_type, parameters, input_profiles, t_start, t_horizon, t_step): - t_last = t_start + pd.Timedelta(hours=(t_horizon - 1) * t_step) + t_last = t_start + pd.Timedelta(hours = (t_horizon - 1) * t_step) years = range(t_start.year, t_last.year + 1) if profile_type == 'elec_demand': profiles = [] for year in years: - profile = ElectricalDemand(year).get_profile(parameters[0], 'h0', - True) # True: with smoothing function for household profiles, False: no smoothing function for household profiles + profile = ElectricalDemand(year).get_profile(parameters[0], 'h0', True) # True: with smoothing function for household profiles, False: no smoothing function for household profiles profile = profile.resample(str(t_step) + 'H').mean().interpolate('linear') profiles.append(profile) timeseries = pd.concat(profiles) @@ -132,12 +128,14 @@ def modify_profile(mod_type: str, parameters, input_profiles, t_start, t_horizon # t_horizon: int, number of days that should be created, for now hardcoded to 365 due to external dependencies # parameters[]: array of additional parameters, should contain csv_path, season_offset, model_path, new, save. # For details on the specific parameters look at the documentation of the prophet_based_modification - profile = Modifier.prophet_based_modification(csv_path=parameters[0], - feature_name=parameters[1], - season_offset=parameters[2], - start=t_start, - model_path=parameters[3], - new=parameters[4], mod_period=365, save=parameters[5]) + profile = Modifier.prophet_based_modification(csv_path = parameters[0], + feature_name = parameters[1], + season_offset = parameters[2], + start = t_start, + model_path = parameters[3], + new = parameters[4], + mod_period = 365, + save = parameters[5]) return profile else: raise Exception("Modification for mod type " + str(mod_type) + " is not implemented!") diff --git a/predictor/Predictor.py b/predictor/Predictor.py index dd7bb4bcae2a9765dbe1ada1e0ecf53545c18701..448a0cb9b6d23f2a367f21db4caff906c83daee7 100644 --- a/predictor/Predictor.py +++ b/predictor/Predictor.py @@ -1,5 +1,6 @@ import numpy as np -import copy as cp +import pandas as pd +import math # ---------------------------------------------------------------------------------------------------------------------- @@ -13,84 +14,60 @@ class Predictor: Parameters ---------- - time_series: - The original time series that should be used for predictions. + profile: + The original profile that should be used for predictions. name: str Does not have to be unique, should be used so that the nature of the time series can be identified. method: str Name of the prediction method to be used. The default method is "same_as_last_day". """ - def __init__(self, time_series, type: str, method: str = "same_as_last_day"): - self.profile = time_series + def __init__(self, profile, type: str, method: str, t_step: float): + self.profile = profile self.type = type self.method = method - - def rh_update(self, time_steps_rh, time_steps_fix): - """ - Selects the correct prediction method based on the selected prediction method. - - Parameters - ---------- - time_steps_rh: - Series of time steps covering the entire horizon - time_steps_fix: - Series of time steps covering the fix part of the horizon, i.e. the part that should not be predicted. - - Returns - ------- - The original time series after applying the prediction method. - """ - if self.method == "time_forward": - return time_forward(time_steps_rh, time_steps_fix, cp.deepcopy(self.profile)) + self.t_step = t_step + + def predict(self, time_steps): + if self.method == "perfect_foresight": + return self.profile[time_steps] + elif self.method == "time_forward": + if 0 in time_steps: + print('Requested time forward prediction for time steps that include the first time step! Using original data for the first time step.') + return pd.Series(self.profile[time_steps[0]], index = time_steps) + else: + return pd.Series(self.profile[time_steps[0]] - 1, index = time_steps) elif self.method == "same_as_last_day": - return same_as_last_day(time_steps_rh, time_steps_fix, cp.deepcopy(self.profile)) + time_steps_per_day = int(24 / self.t_step) + if time_steps[0] - time_steps_per_day < 0: + print('Requested same as last day prediction for time steps that include the first day. Using original data for the first day.') + previous_day_data = np.zeros(time_steps_per_day) + previous_day_data[:time_steps_per_day - time_steps[0]] = self.profile[time_steps[0]:time_steps_per_day] + previous_day_data[time_steps_per_day - time_steps[0]:] = self.profile[:time_steps[0]] + else: + previous_day_data = np.array(self.profile[time_steps[0] - len(time_steps_per_day):time_steps[0]]) + days_in_prediction = [(t * time_steps_per_day, (t + 1) * time_steps_per_day) for t in range(math.ceil(len(time_steps) / time_steps_per_day))] + days_in_prediction[-1] = (days_in_prediction[-1][0], time_steps[-1] - time_steps[0] + 1) + + prediction = pd.Series(0.0, index = time_steps) + for start, end in days_in_prediction: + prediction[start:end] = previous_day_data[0:end - start] + return prediction elif self.method == "same_as_last_week": - return same_as_last_week(time_steps_rh, time_steps_fix, cp.deepcopy(self.profile)) - elif self.method == "perfect_foresight": - return perfect_foresight(time_steps_rh, time_steps_fix, cp.deepcopy(self.profile)) + time_steps_per_week = int(7 * 24 / self.t_step) + if time_steps[0] - time_steps_per_week < 0: + print('Requested same as last week prediction for time steps that include the first week. Using original data for the first week.') + previous_week_data = np.zeros(time_steps_per_week) + previous_week_data[:time_steps_per_week - time_steps[0]] = self.profile[time_steps[0]:time_steps_per_week] + previous_week_data[time_steps_per_week - time_steps[0]:] = self.profile[:time_steps[0]] + else: + previous_week_data = np.array(self.profile[time_steps[0] - len(time_steps_per_week):time_steps[0]]) + weeks_in_prediction = [(t * time_steps_per_week, (t + 1) * time_steps_per_week) for t in range(math.ceil(len(time_steps) / time_steps_per_week))] + weeks_in_prediction[-1] = (weeks_in_prediction[-1][0], time_steps[-1] - time_steps[0] + 1) + + prediction = pd.Series(0.0, index = time_steps) + for start, end in weeks_in_prediction: + prediction[start:end] = previous_week_data[0:end - start] + return prediction else: - # Default: Time Forward - return time_forward(time_steps_rh, time_steps_fix, cp.deepcopy(self.profile)) - - def get_profile(self): - """ - Returns a copy of the original time series stored in the predictor. - - Returns - ------- - Copy of the original time series. - """ - return cp.deepcopy(self.profile) - - -def time_forward(time_steps_rh, time_steps_fix, reference): - reference.loc[list(set(time_steps_rh) - set(time_steps_fix))] = reference[time_steps_fix[-1]] - return reference - - -def same_as_last_day(time_steps_rh, time_steps_fix, reference): - # Assumes that one time step is equal to 1h, should this change this code needs to be adjusted - # Get list of time steps that need to be predicted. time_steps_rh \ time_steps_fix - pred_steps = np.array(list(set(time_steps_rh) - set(time_steps_fix))) - # Remove all pred_steps that are below 24, because no data from 24h earlier is present in the data. - pred_steps = pred_steps[np.abs(pred_steps) > 23] - # Replace all pred_steps with its data from 24 hours earlier - reference.loc[pred_steps] = reference[pred_steps - 24].tolist() - return reference - - -def same_as_last_week(time_steps_rh, time_steps_fix, reference): - # Assumes that one time step is equal to 1h, should this change this code needs to be adjusted - # Get list of time steps that need to be predicted. time_steps_rh \ time_steps_fix - pred_steps = np.array(list(set(time_steps_rh) - set(time_steps_fix))) - # Remove all pred_steps that are below 168, because no data from one week earlier is present in the data. - pred_steps = pred_steps[np.abs(pred_steps) > 167] - # Replace all pred_steps with its data from 7*24 hours earlier - reference.loc[pred_steps] = reference[pred_steps - 168].tolist() - return reference - - -def perfect_foresight(time_steps_rh, time_steps_fix, reference): - # Assume that we can perfectly "predict" the future - return reference + return self.profile[time_steps]