def day_clear_sky_index(day_data, **kargs): """ -> float Calcula el índice de claridad del día (radiación global) respecto a la radiación extraterrestre. :param DataFrame day_data: resgistro temporal de la radiación en el día. """ # obtener radiación total en el día data_sum = sum(day_data.values) # lista de timestamps en el registro date_format = '%d-%m-%Y %H:%M' date = day_data.name timestamps = [date + ' ' + h for h in day_data.index] # calcular radiación extraterrestre secs = get_timestep(timestamps, date_format) ext_rad = [ext_irradiation(t, secs, **kargs) for t in timestamps] ext_sum = sum(ext_rad) # retornar cuociente return data_sum / ext_sum if ext_sum != 0.0 else 0.0
def clearsky_variability(database, timestamp, timesteps, side='backward', **kargs): """ -> float Calcula la desviacion estandar del clearsky index dentro del periodo especificado. :param DataFrame database: base de datos que contenga el resgistro temporal de la radiación. :param str timestamp: string que contiene la hora y fecha del comienzo del periodo (UTC). :param int timesteps: cantidad de puntos/timesteps a considerar en el periodo. :param str side: ('backward' or 'forward') define si el timestamp corresponde al comienzo o al fin del periodo. :returns: (float) desviacion estandar del clearsky index en el periodo. """ # ------------------------------------------------------------------------- # obtener indice del timestamp en la base de datos timestamp = validate_date(timestamp) idx = get_date_index(database['Timestamp'], timestamp) assert (side == 'backward') or (side == 'forward') # definir indices del periodo if side == 'backward': data_index = np.arange((idx - timesteps), idx) else: data_index = np.arange(idx, (idx + timesteps)) # obtener radiacion global en el periodo timestamps = database.loc[data_index, 'Timestamp'].values global_rad = database.loc[data_index, 'Global'].values # ------------------------------------------------------------------------- # calcular clearsky index dentro del periodo date_format = '%d-%m-%Y %H:%M' secs = get_timestep(database['Timestamp'], date_format) # calcular radiacion extraterrestre ext_rad = [ext_irradiation(t, secs, **kargs) for t in timestamps] # calcular clearsky index clearsky = np.divide(global_rad, ext_rad, np.zeros_like(global_rad), where=ext_rad != 0.0) clearsky[np.isinf(clearsky)] = 0.0 # obtener desviacion estandar v = np.std(np.diff(clearsky)) return v
def align_radiation(database, clear_sky_days, **kargs ): """ -> DataFrame Ajusta los timestamps en la base de datos tal que el perfil de radiación global se alinie con el de la radiación extraterrestre. :param DataFrame database: base de datos que contiene el registro temporal a procesar. :param list(str) clear_sky_days: lista de días en el dataset con una alto índice de claridad, en formato %d-%m-%Y. :returns: DataFrame con los timestamps modificados. """ date_format = '%d-%m-%Y %H:%M' timestamps = database['Timestamp'].values diff = [] for day in clear_sky_days: # obtener ventana de datos correspondiente ---------------------------- start_index = get_date_index(timestamps, day) next_day = datetime.strptime( validate_date(day), date_format ) next_day = datetime.strftime( next_day + timedelta(days=1), date_format) stop_index = get_date_index( timestamps, next_day ) day_times = timestamps[start_index:stop_index] # alinear máximos ----------------------------------------------------- ext_rad = [ext_irradiance(t, **kargs) for t in day_times] ext_index = ext_rad.index( max(ext_rad) ) try: rad_data = list(database['Global'].values[start_index:stop_index]) except KeyError: rad_data = list(database['Potencia'].values[start_index:stop_index]) rad_index = rad_data.index( max(rad_data) ) # calcular diferencia entre máximos diff.append( ext_index - rad_index ) # calular ajuste en segundos ---------------------------------------------- timestep = get_timestep(database['Timestamp'], date_format) delay = round(sum(diff)/len(diff))*timestep print(delay) # modificar timestamps new_timestamps = [datetime.strftime( datetime.strptime(t, date_format) + timedelta(seconds=delay), date_format ) for t in timestamps] # retornar base de datos modificada db = database db['Timestamp'] = new_timestamps return db
def compact_database(database, factor, use_average=False): """ -> DataFrame Comprime la base de tiempo del set de datos sumando o ponderando los datos correspondientes al nuevo intervalo definido. :param DataFrame database: base de datos que contiene el registro temporal a procesar. :param int factor: factor por el cual se comprimirá la base de datos. :param bool use_average: permite decidir si sumar o ponderar los datos al comprimir el dataframe. """ print('\n' + '-'*80) print('compacting data time series') print('-'*80 + '\n') sleep(0.25) # ------------------------------------------------------------------------- # obtener base de tiempo de dataset date_format = '%d-%m-%Y %H:%M' timestep = get_timestep(database['Timestamp'], date_format) # ------------------------------------------------------------------------- # compactar dataframe colnames = database.columns.values df = pd.DataFrame(0.0, index=range( ceil(len(database.index)/factor) ), columns=colnames) df = df.astype({u'Timestamp': str}) # obtener fecha inicial date_start = datetime.strptime(database.at[0, u'Timestamp'], date_format) next_date = date_start + timedelta(seconds = factor*timestep) df.at[0, u'Timestamp'] = datetime.strftime(date_start, date_format) # peso ponderador weight = 1.0/factor if use_average else 1.0 bar = ProgressBar() for i in bar( database.index ): # obtener fecha date = datetime.strptime(database.at[i, u'Timestamp'], date_format) # si se sale del intervalo actual if date >= next_date: # añadir timestamp al nuevo dataframe df.at[i//factor, 'Timestamp'] = datetime.strftime(next_date, date_format) next_date = next_date + timedelta(seconds = factor*timestep) # sumar valores a fila correspondiente for c in colnames[1:]: df.at[i//factor, c] += database.at[i, c]*weight return df
def correct_daylight_saving(database, start_date, end_date, positive=True): """ -> DataFrame Ajusta los timestamps en la base de datos corrigiendo el periodo de cambio de hora por Daylight Saving Time entre date_start y date_end. :param DataFrame database: base de datos que contiene el registro temporal a procesar. :param str date_start: fecha y hora en la que comienza el DST, %d-%m-%Y %H:%M. :param str date_start: fecha y hora en la que termina el DST, %d-%m-%Y %H:%M. :returns: DataFrame con los timestamps modificados. """ print('\n' + '-'*80) print('correcting daylight saving time') print('-'*80 + '\n') sleep(0.25) # parsing de fechas ------------------------------------------------------- date_format = '%d-%m-%Y %H:%M' start_date = datetime.strptime( validate_date(start_date), date_format ) end_date = datetime.strptime( validate_date(end_date), date_format ) # procesamiento ----------------------------------------------------------- db = database.copy() # obtner timestep timestep = get_timestep(database['Timestamp'], date_format) delta = (3600.0/timestep) delta = delta if positive else -delta # procesar bar = ProgressBar() for i in bar( db.index ): # obtener timestamp timestamp = datetime.strptime(db.at[i, 'Timestamp'], date_format) # si está en el periodo del DST if (timestamp >= start_date) and (timestamp <= end_date): try: db.iloc[i, 1:] = database.iloc[int(i - delta), 1:] except IndexError: continue # retornar base de datos return db #------------------------------------------------------------------------------
def radiance_to_radiation(database): """ Transforma la base de datos de radiancia (W/m2) a de radiación (Wh/m2). :param DataFrame database: base de datos que contiene el registro de irradiancia a procesar. :returns: DataFrame con los datos procesados. """ db = database.copy() # obtener timestep en el dataset date_format = '%d-%m-%Y %H:%M' secs = get_timestep(db['Timestamp'], date_format)/3600 db['Global'] = [ rad*secs for rad in db['Global'].values] db['Diffuse'] = [ rad*secs for rad in db['Diffuse'].values] db['Direct'] = [ rad*secs for rad in db['Direct'].values] return db #------------------------------------------------------------------------------
def reshape_by_day(database, colname, initial_date, final_date): """ -> DataFrame Reordena el registro temporal de la base de datos separando cada día en una columna, siguiendo la base de tiempo indicada. :param DataFrame database: base de datos que contiene el registro temporal a procesar. :param str colname: nombre de la columna que contiene los datos de interés. :param str initial_date: fecha y hora desde la cual considerar los datos. :param str final_date: fecha y hora hasta la cual considerar los datos (sin incluir). :returns: DataFrame de la base de datos procesada """ print('\n' + '-'*80) print('reshaping data time series') print('-'*80 + '\n') sleep(0.25) # inicializar nueva base de datos ----------------------------------------- date_format = '%d-%m-%Y %H:%M' initial_date = datetime.strptime(validate_date(initial_date), date_format) final_date = datetime.strptime(validate_date(final_date), date_format) assert final_date > initial_date # columnas delta_days = final_date - initial_date num_days = delta_days.days dates = [initial_date + timedelta(days=x) for x in range(num_days)] cols = [datetime.strftime(d, '%d-%m-%Y') for d in dates] # filas timestep = get_timestep(database['Timestamp'], date_format) num_hours = int( 24*(3600/timestep) ) initial_hour = datetime.strptime('00:00','%H:%M') hours = [initial_hour + timedelta(seconds=timestep*x) for x in range(num_hours)] rows = [datetime.strftime(h, '%H:%M') for h in hours] # obtener diferencia en segundos first_hour = datetime.strptime(database.at[0,'Timestamp'], date_format) first_hour = datetime.strftime(first_hour, '%H:%M') first_hour = datetime.strptime(first_hour, '%H:%M') closest_hour = min(hours, key=lambda h: abs( first_hour - h )) delta_sec = (closest_hour - first_hour).seconds delta_sec = (delta_sec - 24*3600) if delta_sec > timestep else delta_sec # DataFrame df = pd.DataFrame(0.0, index = rows, columns= cols) # colocar datos de radiación en el nuevo DataFrame bar = ProgressBar() for i in bar( database.index ): try: timestamp = datetime.strptime( database.at[i, 'Timestamp'], date_format) # aplicar corrección de desfase timestamp += timedelta(seconds=delta_sec) if (timestamp >= initial_date) and (timestamp < final_date): date = datetime.strftime(timestamp, '%d-%m-%Y') hour = datetime.strftime(timestamp, '%H:%M') df.at[hour, date] = database.at[i, colname] except (ValueError, TypeError): continue # retornar database return df
def plot_power_irradiance(db_pv, db_solar, start_date, stop_date): """ -> None Plotea el gráfico de potencia PV vs irradiancia global. :param DataFrame db_pv: base de datos que contiene el registro de 'Potencia' fotovoltaica. :param DataFrame db_solar: base de datos que contiene el registro de irradiancia 'Global'. :return: None """ # ------------------------------------------------------------------------- # verificar bases de datos assert ('Timestamp' in db_pv.columns) and ('Sistema' in db_pv.columns) assert ('Timestamp' in db_solar.columns) and ('Global' in db_solar.columns) # verificar que los timesteps se correspondan date_format = '%d-%m-%Y %H:%M' timestep_pv = get_timestep(db_pv[u'Timestamp'], date_format) timestep_solar = get_timestep(db_solar[u'Timestamp'], date_format) assert timestep_solar == timestep_pv # ------------------------------------------------------------------------- # obtener indices start_date = datetime.strptime(validate_date(start_date), date_format) stop_date = datetime.strptime(validate_date(stop_date), date_format) # photovoltaic pv_index = [-1, -1] for i in db_pv.index: date = datetime.strptime(db_pv.at[i, u'Timestamp'], date_format) if (date >= start_date) and (pv_index[0] == -1): pv_index[0] = i if (date >= stop_date) and (pv_index[1] == -1): pv_index[1] = i - 1 # solarimetric solar_index = [-1, -1] for i in db_solar.index: date = datetime.strptime(db_solar.at[i, u'Timestamp'], date_format) if (date >= start_date) and (solar_index[0] == -1): solar_index[0] = i if (date >= stop_date) and (solar_index[1] == -1): solar_index[1] = i - 1 assert (solar_index[1] - solar_index[0]) == (pv_index[1] - pv_index[0]) # ------------------------------------------------------------------------- # obtener datos a plotear global_rad = db_solar['Global'] global_rad = global_rad.values[solar_index[0]:solar_index[1]] pv_power = db_pv['Sistema'] pv_power = pv_power.values[pv_index[0]:pv_index[1]] timestamp = db_solar['Timestamp'].values[solar_index[0]:solar_index[1]] # obtener colores cmap = cm.get_cmap('plasma') colors = [] for t in timestamp: # obtener minuto del día date = datetime.strptime(t, date_format) date_tt = date.timetuple() min_day = date_tt.tm_hour * 60.0 + date_tt.tm_min t_x = 1 - min_day / (24 * 60.0) # asignar color colors.append(cmap(t_x)) # plotear fig = plt.figure() fig.set_size_inches(6, 3) plt.style.use(['seaborn-white', 'seaborn-paper']) matplotlib.rc('font', family='Times New Roman') plt.scatter(global_rad, pv_power, c=colors, s=0.8) plt.xlabel(u'Irradiancia global, W/m2') plt.ylabel(u'Potencia sistema, kW') plt.xlim([0.01, 1200]) plt.tight_layout() return None
def plot_performance_ratio(db_pv, db_solar, start_date, stop_date, **kargs): """ -> None Plotea el gráfico de performance ratio vs claridad a partir de la base de datos entregada. :param DataFrame db_pv: base de datos que contiene el registro de 'Potencia' fotovoltaica. :param DataFrame db_solar: base de datos que contiene el registro de irradiancia 'Global'. :return: None """ # ------------------------------------------------------------------------- # verificar bases de datos assert ('Timestamp' in db_pv.columns) and ('Sistema' in db_pv.columns) assert ('Timestamp' in db_solar.columns) and ('Global' in db_solar.columns) # verificar que los timesteps se correspondan date_format = '%d-%m-%Y %H:%M' timestep_pv = get_timestep(db_pv[u'Timestamp'], date_format) timestep_solar = get_timestep(db_solar[u'Timestamp'], date_format) assert timestep_solar == timestep_pv # ------------------------------------------------------------------------- # obtener indices start_date = datetime.strptime(validate_date(start_date), date_format) stop_date = datetime.strptime(validate_date(stop_date), date_format) # photovoltaic pv_index = [-1, -1] for i in db_pv.index: date = datetime.strptime(db_pv.at[i, u'Timestamp'], date_format) if (date >= start_date) and (pv_index[0] == -1): pv_index[0] = i if (date >= stop_date) and (pv_index[1] == -1): pv_index[1] = i - 1 # solarimetric solar_index = [-1, -1] for i in db_solar.index: date = datetime.strptime(db_solar.at[i, u'Timestamp'], date_format) if (date >= start_date) and (solar_index[0] == -1): solar_index[0] = i if (date >= stop_date) and (solar_index[1] == -1): solar_index[1] = i - 1 assert (solar_index[1] - solar_index[0]) == (pv_index[1] - pv_index[0]) # ------------------------------------------------------------------------- # obtener performance ratio global_rad = db_solar['Global'] global_rad = global_rad.values[solar_index[0]:solar_index[1]] pv_power = db_pv['Sistema'] pv_power = pv_power.values[pv_index[0]:pv_index[1]] plant_factor = 1000.0 / 16.2 performance_ratio = [] for i in range(len(pv_power)): if (global_rad[i] > 2.0): performance_ratio.append(plant_factor * (pv_power[i] / global_rad[i])) else: performance_ratio.append(0.0) performance_ratio = np.array(performance_ratio) # obtener claridad timestamp = db_solar['Timestamp'].values[solar_index[0]:solar_index[1]] ext_rad = [ext_irradiance(t, **kargs) for t in timestamp] claridad = [] for i in range(len(pv_power)): if (ext_rad[i] > 2.0): claridad.append(global_rad[i] / ext_rad[i]) else: claridad.append(0.0) claridad = np.array(claridad) # obtener colores cmap = cm.get_cmap('plasma') colors = [] for t in timestamp: # obtener minuto del día date = datetime.strptime(t, date_format) date_tt = date.timetuple() min_day = date_tt.tm_hour * 60.0 + date_tt.tm_min t_x = 1 - min_day / (24 * 60.0) # asignar color colors.append(cmap(t_x)) # plotear fig = plt.figure() fig.set_size_inches(6, 3) plt.style.use(['seaborn-white', 'seaborn-paper']) matplotlib.rc('font', family='Times New Roman') plt.scatter(claridad, performance_ratio, c=colors, s=0.8) plt.xlabel(u'Índice de claridad') plt.ylabel(u'Performance ratio') plt.xlim([0, 1.0]) plt.ylim([0, 1.0]) return None
def plot_1D_radiation_data(database, colname, start_date, stop_date, extraRad=True, scale_factor=1.0, **kargs): """ -> None Plotea el gráfico temporal 1D de la radiación en el tiempo. Donde el eje X corresponde al tiempo y el eje Y a la intensidad de la radiación. :param DataFrame database: base de datos que contiene el registro de radiación en el tiempo. :param str colname: nombre de la columna a graficar. :param str start_date: fecha desde la cual empezar el plot. :param str stop_date: fecha en que termina el plot. :param bool extraRad: si incluir en el plot la radiación extraterreste. :param float scale_factor: si se desea escalar los datos en algún factor. :return: None """ # obtener limites del plot ------------------------------------------------ tmstmp = database['Timestamp'].values date_format = '%d-%m-%Y %H:%M' start_date = datetime.strptime(validate_date(start_date), date_format) stop_date = datetime.strptime(validate_date(stop_date), date_format) start_index = get_date_index(tmstmp, start_date, nearest=True) stop_index = get_date_index(tmstmp, stop_date, nearest=True) if stop_index <= start_index: print('InputError: las fechas ingresadas no permiten generar un plot.') return None # plotear ----------------------------------------------------------------- Y = database[colname][start_index:stop_index] timestamps = tmstmp[start_index:stop_index] X = range(len(timestamps)) fig = plt.figure() fig.set_size_inches(6, 2.5) plt.style.use(['seaborn-white', 'seaborn-paper']) matplotlib.rc('font', family='Times New Roman') plt.plot(X, Y * scale_factor, c='k', ls='-', lw=0.8, label='Global en plano horizontal') # añadir readiación extraterrestre ---------------------------------------- if extraRad: secs = get_timestep(timestamps, date_format) ext_rad = [ext_irradiance(t, **kargs) for t in timestamps] plt.plot(X, ext_rad, c='k', ls='--', lw=0.8, label='Extraterrestre') # colocar etiquetas en el gráfico ----------------------------------------- plt.axvspan(0, len(timestamps), facecolor='white', alpha=1.0) plt.xlim([0, len(timestamps) - 1]) plt.xticks([]) plt.ylabel('Irradiancia solar, W/m2') plt.legend(loc='upper left') #plt.legend(loc='best') title = tmstmp[start_index] + ' - ' + tmstmp[stop_index] plt.title(title) plt.tight_layout() return None
def forecast_error_evaluation(datasets, output_name, model, data_leap, cluster_labels=[], img_sequence=None, plot_results=True, random_state=0, save_path=None): """ -> DataFrame retorna métricas respecto al error del modelo de forecast en base a su desempeño sobre el dataset entregado. :param list(DataFrame) datasets: lista que contiene los datasets con los atributos correspondientes a los inputs del model de pronóstico. :param str output_name: nombre de la columna que contiene los datos del set Y. :param keras.model model: modelo de forecasting a evaluar. :param int data_leap: define el intervalo de tiempo entre cada pronóstico a realizar. :param list or array_like cluster_labels: contiene la etiqueta que identifica el cluster da cada día en el dataset. :param int img_sequence: (default None) indice del dataset que consiste en una sequencia de imagenes. :param bool plot_results: determina si se muestran los gráficos de la evaluación. :param int random_state: permite definir el random_state en la evaluación. :param str save_path: ubicación del directorio en donde guardar los resultados. :returns: DataFrame """ random.seed(random_state) # obtener lista de días system_ds = datasets[0] date_format = '%d-%m-%Y %H:%M' timestamps = system_ds['Timestamp'].values timestep = get_timestep(system_ds['Timestamp'], date_format) initial_date = datetime.strptime(timestamps[0], date_format) final_date = datetime.strptime(timestamps[-1], date_format) dates = pd.date_range(initial_date, final_date, freq='1D') dates = dates.strftime('%d-%m-%Y') # ------------------------------------------------------------------------- # evaluar modelo print('\n' + '-' * 80) print('cluster evaluation summary') print('-' * 80 + '\n') try: n_input = int(model.input.shape[1]) except AttributeError: n_input = int(model.input[0].shape[1]) n_output = int(model.output.shape[1]) # checkear cluster_labels if cluster_labels is None: cluster_labels = np.zeros([1, dates.size]).flatten() num_labels = np.max(cluster_labels) + 1 num_labels = np.max(cluster_labels) + 1 # inicializar metrics dataframe cols = [] horizons = [ str((h + 1) * timestep / 60.0) + ' min' for h in range(n_output) ] metrics = ['mbe', 'mae', 'rmse', 'fs', 'std', 'skw', 'kts'] for m in metrics: cols += [m + ' ' + h for h in horizons] eval_metrics = pd.DataFrame(index=np.arange(num_labels), columns=cols) # para cada etiqueta resultante del clustering for label in np.arange(num_labels): # obtener fechas correspondientes a la etiqueta cluster_dates = dates[cluster_labels == label] # inicializar datos cluster cluster_data = [] cluster_pred = [] cluster_pers = [] cluster_var = [] # por cada una de las fechas del cluster for date in cluster_dates: # obtener forecasting_times de testing timeleap = timestep * data_leap initial_hour = datetime.strptime(date, '%d-%m-%Y') hours = [ initial_hour + timedelta(seconds=timeleap * i) for i in range(int(24 * 3600 / timeleap)) ] pred_times = [datetime.strftime(h, date_format) for h in hours] # calcular predicción en cada una de las horas for pred_time in pred_times: try: # prediccion del modelo Y_true, Y_pred = forecast_pred(datasets, output_name, model, pred_time, img_sequence=img_sequence, verbose=False) # prediccion del persistence model Y_pers = beauchef_pers_predict(system_ds, pred_time, n_output) # clearsky variability cs_var = clearsky_variability(system_ds, pred_time, n_input) # print progress print('\revaluating cluster ' + str(int(label)) + ': ' + pred_time, end='') except TypeError: continue # agregar datos a cluster data cluster_data.append(Y_true) cluster_pred.append(Y_pred) cluster_pers.append(Y_pers) cluster_var.append(cs_var) # calcular metricas del cluster Y_true = np.concatenate(cluster_data, axis=1) Y_pred = np.concatenate(cluster_pred, axis=1) Y_pers = np.concatenate(cluster_pers, axis=1) cs_var = np.array(cluster_var) # por cada horizonte de pronóstico for i, h in enumerate(horizons): eval_metrics.at[label, 'mbe ' + h] = mean_bias_error( Y_true[i, :], Y_pred[i, :]) eval_metrics.at[label, 'mae ' + h] = mean_absolute_error( Y_true[i, :], Y_pred[i, :]) eval_metrics.at[label, 'rmse ' + h] = np.sqrt( mean_squared_error(Y_true[i, :], Y_pred[i, :])) eval_metrics.at[label, 'fs ' + h] = forecast_skill( Y_true[i, :], Y_pred[i, :], Y_pers[i, :]) eval_metrics.at[label, 'skw ' + h] = skew_error(Y_true[i, :], Y_pred[i, :]) eval_metrics.at[label, 'kts ' + h] = kurtosis_error( Y_true[i, :], Y_pred[i, :]) # plot distribución de error plot_error_dist(Y_true, Y_pred, Y_pers, horizons, bins=40, log=True, range=(-0.5, 0.5)) plot_error_variability(Y_true, Y_pred, cs_var, horizons, s=0.08) return eval_metrics
def forecast_model_evaluation(datasets, output_name, model, data_leap, cluster_labels=None, img_sequence=None, plot_results=True, random_state=0, save_path=None): """ -> DataFrame retorna métricas de evaluación del modelo de forecast entregado en base a su desempeño en el dataset entregado. :param list(DataFrame) datasets: lista que contiene los datasets con los atributos correspondientes a los inputs del model de pronóstico. :param str output_name: nombre de la columna que contiene los datos del set Y. :param keras.model model: modelo de forecasting a evaluar. :param int data_leap: define el intervalo de tiempo entre cada pronóstico a realizar. :param list or array_like cluster_labels: contiene la etiqueta que identifica el cluster da cada día en el dataset. :param int img_sequence: (default None) indice del dataset que consiste en una sequencia de imagenes. :param bool plot_results: determina si se muestran los gráficos de la evaluación. :param int random_state: permite definir el random_state en la evaluación. :param str save_path: ubicación del directorio en donde guardar los resultados. :returns: DataFrame """ random.seed(random_state) # obtener lista de días system_ds = datasets[0] date_format = '%d-%m-%Y %H:%M' timestamps = system_ds['Timestamp'].values timestep = get_timestep(system_ds['Timestamp'], date_format) initial_date = datetime.strptime(timestamps[0], date_format) final_date = datetime.strptime(timestamps[-1], date_format) dates = pd.date_range(initial_date, final_date, freq='1D') dates = dates.strftime('%d-%m-%Y') # ------------------------------------------------------------------------- # evaluar modelo print('\n' + '-' * 80) print('cluster evaluation summary') print('-' * 80 + '\n') n_output = int(model.output.shape[1]) # checkear cluster_labels if cluster_labels is None: cluster_labels = np.zeros([1, dates.size]).flatten() num_labels = np.max(cluster_labels) + 1 num_labels = np.max(cluster_labels) + 1 # inicializar cluster_metrics metrics = ['mbe', 'mae', 'rmse', 'fs', 'std', 'skw', 'kts'] eval_metrics = pd.DataFrame(index=np.arange(num_labels), columns=metrics) # para cada etiqueta resultante del clustering for label in np.arange(num_labels): # obtener fechas correspondientes a la etiqueta cluster_dates = dates[cluster_labels == label] # escoger una fecha de muestra al azar cluster_sample = random.choice(list(cluster_dates)) # inicializar datos cluster cluster_data = [] cluster_pred = [] cluster_pers = [] cluster_time = [] # por cada una de las fechas del cluster for date in cluster_dates: # obtener forecasting_times de testing timeleap = timestep * data_leap initial_hour = datetime.strptime(date, '%d-%m-%Y') hours = [ initial_hour + timedelta(seconds=timeleap * i) for i in range(int(24 * 3600 / timeleap)) ] pred_times = [datetime.strftime(h, date_format) for h in hours] # calcular predicción en cada una de las horas for pred_time in pred_times: try: # prediccion del modelo Y_true, Y_pred = forecast_pred(datasets, output_name, model, pred_time, img_sequence=img_sequence) # prediccion del persistence model Y_pers = beauchef_pers_predict(system_ds, pred_time, n_output) # print progress print('\revaluating cluster ' + str(int(label)) + ': ' + pred_time, end='') except TypeError: continue # agregar datos a cluster data cluster_data.append(Y_true) cluster_pred.append(Y_pred) cluster_pers.append(Y_pers) cluster_time.append([pred_time] * n_output) # calcular metricas del cluster Y_true = np.concatenate(cluster_data, axis=None) Y_pred = np.concatenate(cluster_pred, axis=None) Y_pers = np.concatenate(cluster_pers, axis=None) Y_time = np.concatenate(cluster_time, axis=None) eval_metrics.at[label, 'mbe'] = mean_bias_error(Y_true, Y_pred) eval_metrics.at[label, 'mae'] = mean_absolute_error(Y_true, Y_pred) eval_metrics.at[label, 'rmse'] = np.sqrt(mean_squared_error(Y_true, Y_pred)) eval_metrics.at[label, 'fs'] = forecast_skill(Y_true, Y_pred, Y_pers) eval_metrics.at[label, 'skw'] = skew_error(Y_true, Y_pred) eval_metrics.at[label, 'kts'] = kurtosis_error(Y_true, Y_pred) # plot cluster_sample plot_title = 'cluster ' + str(label) plot_forecast_pred(datasets, output_name, model, cluster_sample, img_sequence=img_sequence, title=plot_title) # plot gráfico estimación plot_forecast_accuracy(Y_true, Y_pred, timestamps=Y_time, title=plot_title, s=0.1) return eval_metrics
def persistence_predict(database, forecast_date, n_output, power_cols, lat=-33.45775, lon=70.66466111, Bs=0.0, Zs=0.0, rho=0.2, verbose=False): """ -> numpy.array(floats) genera un pronóstico de generación fotovoltaica dentro del horizonte especificado a partir del criterio de persistencia y el modelo de Perez. Se supone que tanto el índice de claridad como la fracción difusa del timestamp especificado se mantienen constantes dentro de todo el horizonte. :param DataFrame database: base de datos que contiene el registro temporal del sistema fotovoltaico. :param str forcast_date: fecha y hora en la que realizar el pronóstico %d-%m-%Y %H:%M (UTC). :param int n_output: cantidad de pronósticos a realizar desde el forcast_date especificado. :param str or list(str) power_cols: nombre de las columnas que tienen la potencia de los equipos a pronosticar. :param float lat: latitud del punto geográfico. :param float lon: longitud del punto geográfico en grados oeste [0,360). :param float Bs: (default, 0.0) inclinación de la superficie. ángulo de inclinación respecto al plano horizontal de la superficie sobre la cual calcular la irradiancia (grados). :param float Zs: (default, 0.0) azimut de la superficie. ángulo entre la proyección de la normal de la superficie en el plano horizontal y la dirección hacia el ecuador (grados). :returns: array con las potencias pronosticadas dentro de el horizonte. """ # ------------------------------------------------------------------------- # obtener datos operacionales en el timestamp forecast_date = validate_date(forecast_date) date_index = get_date_index(database['Timestamp'], forecast_date) # checkear que los datos estén contenidos en el database input_index = date_index - 1 end_index = date_index + n_output # checkear ventana de tiempo try: assert input_index >= 0 assert end_index < database.shape[0] except AssertionError: print('InputError: no es posible aplicar el modelo en la ventana' + 'de tiempo especificada') return None # obtener potencia en el timestamp de input power = 0.0 # agregar la potencia de los equipos power_cols = list(power_cols) for equip in power_cols: power += database.at[input_index, equip] # obtener parámetros de irradiación global_rad = database.at[input_index, 'Global'] diffuse_rad = database.at[input_index, 'Diffuse'] # obtener timestep de la base de datos date_format = '%d-%m-%Y %H:%M' data_timestep = get_timestep(database['Timestamp'], date_format) input_timestamp = database.at[input_index, 'Timestamp'] ext_rad = ext_irradiation(input_timestamp, data_timestep, lat=lat, lon=lon) clearsky_index = np.min([1.0, global_rad / ext_rad]) if ext_rad != 0.0 else 1.0 cloudness_index = np.min([1.0, diffuse_rad / global_rad]) if global_rad != 0.0 else 1.0 # ------------------------------------------------------------------------- # modelo de Perez input_timestamp = datetime.strptime(input_timestamp, date_format) rad_fh = np.zeros((1, n_output + 1)).flatten() # incializar matriz de coeficientes file_dir = os.path.dirname( os.path.abspath(inspect.getfile(persistence_predict))) file_path = os.path.join(file_dir, 'perez_coefs.csv') coefs = read_csv_file(file_path) # para cada timestamp dentro del horizonte de pronostico for i in range(n_output + 1): # obtener timestamp de pronostico new_timestamp = input_timestamp + timedelta(seconds=data_timestep * i) new_timestamp = datetime.strftime(new_timestamp, date_format) # radiacion extraterrestre en el plano horizontal gho = ext_irradiation(new_timestamp, data_timestep, lat=lat, lon=lon) # radiacion global en el plano horizontal ghi = gho * clearsky_index # coeficiente de incidencia cos_theta = solar_incidence(new_timestamp, lat=lat, lon=lon, Bs=Bs, Zs=Zs) cos_theta_z = solar_incidence(new_timestamp, lat=lat, lon=lon) rb = cos_theta / cos_theta_z if cos_theta_z != 0.0 else 0.0 # zenith theta_z = acos(cos_theta_z) # estimacion radiaciones dif_i = cloudness_index * ghi dir_i = (ghi - dif_i) * sin(pi / 2 - theta_z) # irradiacion difusa circumsolar a = np.max([0, cos_theta]) b = np.max([cos(85.0 * pi / 180.0), cos_theta_z]) # clearness parameter if dif_i == 0.0: eps = 1.0 else: eps = ((dif_i - dir_i * rb) / dif_i + 5.535e-6 * (theta_z * 180 / pi)**3) / (1 + 5.535e-6 * (theta_z * 180 / pi)**3) # brightness parameter gon = gho / cos_theta_z if cos_theta_z != 0.0 else 0.0 brp = (1 / cos_theta_z) * (dif_i / gon) if (cos_theta_z != 0.0) and ( gon != 0.0) else 0.0 # obtener coeficientes idx = np.searchsorted(coefs['e'], eps, side='right') - 1 f11, f12, f13 = coefs.iloc[idx, 1:4] f21, f22, f23 = coefs.iloc[idx, 4:7] # calcular brightness coeficients F1 = np.max([0, (f11 + f12 * brp + theta_z * f13)]) F2 = (f21 + f22 * brp + theta_z * f23) # corregir angulos de la superfice Bs_rad = Bs * pi / 180 # calcular irradiación total gti = (dir_i * rb + dif_i * (1 - F1) * (1 + cos(Bs_rad)) / 2 + dif_i * F1 * a / b + dif_i * F2 * sin(Bs_rad) + ghi * rho * (1 - cos(Bs_rad)) / 2) rad_fh[i] = gti # calcular estimaciones de potencia forecast_output = np.zeros((1, n_output)).flatten() # si hay información para realizar un pronóstico if not (rad_fh[0] == 0.0 or power == 0.0): for i in range(n_output): forecast_output[i] = (rad_fh[i + 1] / rad_fh[0]) * power # si se desea imprimir información if verbose: # radiacion global global_rad = database['Global'].values[date_index:end_index] # potencia sistema syst_power = np.zeros((1, n_output)).flatten() for equip in power_cols: syst_power = syst_power + database[equip].values[ date_index:end_index] print('forecasted radiation: ' + str(rad_fh)) print('global radiation: ' + str(global_rad) + '\n') print('forecasted power: ' + str(forecast_output)) print('system power: ' + str(syst_power) + '\n') return np.reshape(forecast_output, (n_output, -1))
def goes16_dataset(dir_path, timestamps, size, lat=-33.45775, lon=-70.66466111, adjust_time=0, fix_timestamps=True): """ -> pandas.DataFrame Procesa los archivos netCDF4 contenidos en el directorio especificado contruyendo un DataFrame de imagenes satelitales a partir de la serie de timestamps entregados. :param str or list dir_path: ubicacion de los archivos netCDF4 (.nc) que se desea procesar. :param list or array-like timestamps: serie de timestamps de las imágenes que se desean procesar. :param int size: tamaño de las imagenes resultantes en el procesamiento. :param float lat: latitud central del área de interés. :param float lon: longitud central del área de interés. :param str adjust_time: permite corregir en segundos el timestamp asociado a cada dato. :param bool fix_timestamps: si corregir la serie de timestamps en el dataset. :returns: DataFrame de la base de datos """ print('\n' + '-'*80) print('processing noaa-goes16 data') print('-'*80 + '\n') # obtener lista de archivos netCDF4 a procesar ---------------------------- nc_list = [] # por cada una de las direcciones especificadas if type(dir_path) is str: dir_path = [dir_path] for path in list(dir_path): files_list = os.listdir(path) # por cada archivo en el directorio for f in files_list: file_name, ext = os.path.splitext(f) # si no es un archivo .nc if ext != '.nc': continue # obtener timestamp start_date, end_date = get_key_times(file_name) start_date = datetime.strptime(start_date, '%d-%m-%Y %H:%M:%S') end_date = datetime.strptime(end_date, '%d-%m-%Y %H:%M:%S') nc_list.append( (start_date, end_date, os.path.join(path, f)) ) # ordenar nc_list.sort() # inicializar dataset ----------------------------------------------------- colnames = ['Timestamp', 'Start Time', 'End Time'] + list(np.arange(size*size)) num_data = len(timestamps) db = pd.DataFrame(0.0, index=np.arange(num_data), columns=colnames) db['Timestamp'] = db['Timestamp'].astype(str) db['Start Time'] = db['Start Time'].astype(str) db['End Time'] = db['End Time'].astype(str) # obtener bound indexes --------------------------------------------------- print('getting bounds indexes:', end =" ") bound_indexes = get_bound_indexes(nc_list[0][2], lat, lon, size) print(' done\n') sleep(0.25) # formatear dataset ------------------------------------------------------- date_format = '%d-%m-%Y %H:%M' bar = ProgressBar() for i in bar( db.index ): # obtener timestamp a procesar timestamp = validate_date(timestamps[i]) timestamp = datetime.strptime(timestamp, date_format) # formatear timestamp db.at[i, u'Timestamp'] = datetime.strftime(timestamp, date_format) # obtener imagen satelital correspondiente for j in range( len(nc_list) ): # obtener timestamp de inicio del scaneo data_start, _, data_path = nc_list[j] # si la imagen fue tomada después del timestamp de interés if timestamp < data_start: # la imagen correspondiente es la tomada anteriormente data_start, data_end, data_path = nc_list[j-1] break # agregar timestamps de scaneo db.at[i, 'Start Time'] = datetime.strftime(data_start, date_format) db.at[i, 'End Time'] = datetime.strftime(data_end, date_format) # formatear data data = bound_goes16_data(data_path, bound_indexes) db.at[i, 3:] = data.reshape( (1,-1) )[0] if not(fix_timestamps): return db # ------------------------------------------------------------------------- # añadir timestamps faltantes # obtener timestep del dataset timestep = get_timestep(db['Timestamp'], date_format) time_freq = str(timestep) + 'S' first_date = datetime.strptime( db['Timestamp'].iloc[0], date_format) last_date = datetime.strptime( db['Timestamp'].iloc[-1], date_format) idx = pd.date_range(first_date, last_date, freq=time_freq) db.index = pd.DatetimeIndex( db[u'Timestamp'].values, dayfirst=True ) db = db.reindex( idx, fill_value=0.0 ) db[u'Timestamp'] = idx.strftime(date_format) # resetear index a enteros db = db.reset_index() db.drop('index',axis=1, inplace=True) return db