Source code for rws_py.residual_analyse

# -*- coding: utf-8 -*-
"""
Analyse the rsidual loads from different countries statistically

with respect to the correlation of periods with positive and negative residual loads.
sum: integral (Energy)
mean: measure for Power
count: period (in h)
and further values are also calculated like the 25%, 75% etc.

Context: SPP2403 Cooperation Atakan / Bertsch

Created on Thu Dec  5 10:00:37 2024

@author: atakan

"""

import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sbn
from scipy import signal

from rws_py import DATA_DIR, RESULTS_DIR
plt.close("all")

# Beispiel-Daten (ersetze durch deine eigene Datenserie)
# data_0 = pd.DataFrame(
#     {'Residuallast': [5, 1, 2, -2, -1, -3, 5, 2, -4, -6, 8, 1, 6, 7, 8]})
test = False
dir_name = RESULTS_DIR
f_name_base = dir_name/"res_store"

[docs] def analyze_residual(data, column, verbose=False): """ Analyze Residual load data for periods of negative residuals followed by positive with respect to time (length), integral(sum)=energy and the mean values of each period. Parameters ---------- data : pd.DataFrame to be analyzed. column : string the column with the data to be analyzed. verbose : Boolean, optional if you want some printing. The default is False. Raises ------ KeyError DESCRIPTION. Returns ------- results : dictionary with three entries, as follows -"" df_period : pd.DataFrame all periods ith the folowing one of opposite sign . df_pos_neg : pd.DataFrame positive residual load periods with the folowing one of opposite sign . df_neg_pos : pd.DataFrame negative residual load periods with the folowing one of opposite sign . """ # Vorzeichenserien erstellen data['sign'] = np.sign(data[column]) # Finden Sie die Perioden positiver und negativer Zahlen data['block'] = (data['sign'].diff() != 0).cumsum() # Statistik für jede Periode erstellen df_period = data.groupby('block')[column].agg( ['count', 'mean', 'std', 'sum', 'min', 'max']).reset_index() df_period['25%'] = data.groupby('block')[column].quantile(0.25).values df_period['50%'] = data.groupby('block')[column].quantile(0.5).values df_period['75%'] = data.groupby('block')[column].quantile(0.75).values df_period['positive'] = df_period['sum'] > 0 # Listen für Positive-Negative und Negative-Positive Kombinationen pos_neg_list = [] neg_pos_list = [] # Iteriere durch die Perioden und kombiniere sie for i in range(len(df_period)): current_row = df_period.iloc[i] if current_row['positive']: # Positive-Negative Kombination if i + 1 < len(df_period) and not df_period.iloc[i + 1]['positive']: next_row = df_period.iloc[i + 1] combined = pd.concat([current_row.add_suffix( '_pos'), next_row.add_suffix('_neg')]) else: zero_row = pd.Series( 0, index=current_row.index).add_suffix('_neg') combined = pd.concat( [current_row.add_suffix('_pos'), zero_row]) pos_neg_list.append(combined) else: # Negative-Positive Kombination if i + 1 < len(df_period) and df_period.iloc[i + 1]['positive']: next_row = df_period.iloc[i + 1] combined = pd.concat([current_row.add_suffix( '_neg'), next_row.add_suffix('_pos')]) else: zero_row = pd.Series( 0, index=current_row.index).add_suffix('_pos') combined = pd.concat( [current_row.add_suffix('_neg'), zero_row]) neg_pos_list.append(combined) # DataFrames erstellen df_pos_neg = pd.DataFrame(pos_neg_list).reset_index(drop=True) df_neg_pos = pd.DataFrame(neg_pos_list).reset_index(drop=True) # Ergebnisse anzeigen if verbose: print("Positive-Negative Perioden (fehlende negative Werte mit Nullen aufgefüllt):") print(df_pos_neg) print( "\nNegative-Positive Perioden (fehlende positive Werte mit Nullen aufgefüllt):") print(df_neg_pos) return df_period, df_pos_neg, df_neg_pos
[docs] def storage(power, actual_capacity, full_capacity, efficiency=1, time_step=1, sp_names=None): """ Simple storage model without power restriction but max. capacity. Parameters ---------- power : Float power entering the storage (=positive) or wanted(=negative), if possible with the actual state of charge. actual_capacity : Float what is the actual charge (energy). full_capacity : Float what is the full capacity. efficiency : Float, optional discharging efficiency. The default is 1. time_step : Float, optional time step relative to the next value in hours. The default is 1. sp_names : list of strings, optional the names of the three values calculated. The default is None. Returns ------- result_dict : Dictionary - "act_st_cap" actual loading/energy after the time step - "power_st_in": the real power entering/exiting the storage - "SOC": state of charge. """ if power >= 0: # Laden if actual_capacity < full_capacity - power*time_step: pass elif actual_capacity < full_capacity: power = (full_capacity - actual_capacity)/time_step else: power = 0 actual_capacity += time_step*power else: # Entladen power_with_efficiency = power / efficiency # Berücksichtige Effizienz bei der Entladeleistung if actual_capacity > np.abs(time_step*power_with_efficiency): pass elif actual_capacity > 0: power = -actual_capacity*efficiency/time_step # Anpassen der Leistung basierend auf verfügbarer Kapazität else: power = 0 actual_capacity += time_step * power_with_efficiency # Sicherheitscheck für numerische Ungenauigkeiten actual_capacity = max(0, min(actual_capacity, full_capacity)) soc = actual_capacity/full_capacity if not sp_names: sp_names = ["act_st_cap", "power_st_in", "SOC"] result_dict = dict(zip(sp_names, [actual_capacity, power, soc])) return result_dict
[docs] def create_filenames(value, base=None): exponent = int(np.floor(np.log10(value))) # Berechne den Exponenten base_value = value / (10 ** exponent) # Basiswert berechnen # Runden die Basis auf 3 signifikante Stellen und ohne Dezimalstellen # Multiplizieren und umwandeln in Ganze Zahl zur Eliminierung der Dezimalstellen rounded_value = round(base_value, 3) # Entferne Punkte durch Umwandlung in integer und formatiere den Namen filename = f"{int(rounded_value * 1000)}E{exponent - 3}" return base+filename
[docs] def eval_df_pos_neg(df, col, time=1, verbose=False): positive_residuals = df[df[col] > 0][col] positive_mean = positive_residuals.mean() positive_sum = positive_residuals.sum() positive_count = positive_residuals.count() # Berechnung für negative Residualwerte negative_residuals = df[df[col] < 0][col] negative_mean = negative_residuals.mean() negative_sum = negative_residuals.sum() negative_count = negative_residuals.count() if verbose: # Ausgabe der Ergebnisse print(f"""Positive Residuals: Mittelwert: { positive_mean:.2e}, Summe: {positive_sum:.2e}""") print(f"""Negative Residuals: Mittelwert: { negative_mean:.2e}, Summe: {negative_sum:.2e}""") print(f"""summe Residuals: Mittelwert: { positive_mean+negative_mean:.2e}, Summe: {positive_sum+negative_sum:.2e}""") return np.array([positive_mean, positive_sum, positive_count, negative_mean, negative_sum, negative_count])
[docs] def calc_capacity_dependence(in_ser, m_cap, EFF_STOR=0.75, make_df=False, verbose = False, file_name = 'cap_res-', title = None): """ Calculate a storage (dis-)charging as a function of residual loads Parameters ---------- in_ser : pd.Series hourly Residual loads (neg->entering). m_cap : array of floats storage capacities for which the behaviour is calculated. EFF_STOR : Float, optional discharging efficiency of the storage. The default is 0.75. make_df : Boolean, optional shall all results be returned as pd.DataFrame or as some numpy.arrays. The default is False. verbose : Boolean, optional if printing is wanted. The default is False. file_name : string, optional place to store the results, the name will be extended. The default is 'cap_res-'. Returns ------- Dataframe assessment as function of the residual loads time series. or numpy arrays if make_df=False, cap_dep_res, st_state, la, cap_change. """ cap_dep_res = [] st_state = [] cap_change = [] la = [] in_df = pd.DataFrame(in_ser) in_df.columns = ["residual"] for mca in m_cap: cap = 0 # Initiale Speicherkapazität fn_act = create_filenames(mca, file_name) results = [] for po in in_df["residual"]: st_res = storage(-po, cap, mca, efficiency=EFF_STOR) results.append(st_res) cap = st_res["act_st_cap"] results = pd.DataFrame(results) #results.set_index('row_num', inplace=True) in_df.set_index(results.index, inplace=True) actual_df = pd.concat([in_df, results], axis=1) actual_df["row_num"] = actual_df.index actual_df["pow_rest"] = actual_df["residual"] + actual_df["power_st_in"] # Berechne die Differenzen und weitere Statistiken actual_df['change_act_st_cap'] = actual_df['act_st_cap'].diff() fi_, ax_ = plt.subplots(3) sbn.lineplot(data=actual_df, x="row_num", y="SOC", ax=ax_[0], color='b') sbn.lineplot(data=actual_df, x="row_num", y="pow_rest", ax=ax_[1], color='b') sbn.lineplot(data=actual_df, x="row_num", y="residual", ax=ax_[2], color='k') ax_[0].set_title(title+f", Cap: {mca:2.2e}") fi_.savefig(fn_act+".jpg") soc_counts = actual_df['SOC'].value_counts() zero_count = soc_counts.get(0, 0) # Anzahl der 0en one_count = soc_counts.get(1, 0) # Anzahl der 1en # Berechnung der Differenzen zwischen aufeinanderfolgenden Werten actual_df['previous_SOC'] = actual_df['SOC'].shift( 1) # Vorheriger Wert # Bedingung für Wechsel von 0 auf einen höheren Wert from_0_to_higher = ((actual_df['previous_SOC'] == 0) & ( actual_df['SOC'] > 0)).sum() # Bedingung für Wechsel von 1 auf einen niedrigeren Wert from_1_to_lower = ((actual_df['previous_SOC'] == 1) & ( actual_df['SOC'] < 1)).sum() # Ausgabe der Ergebnisse if verbose: print(f"Max. storage capacity: {mca}") print(f"""Anzahl der Werte 0 in 'SOC': { zero_count} und 1 in 'SOC': {one_count}""") print(f"""Anzahl der Wechsel von 0 auf einen höheren Wert: { from_0_to_higher}; von 1 auf einen niedrigeren Wert: { from_1_to_lower}""") st_state.append(np.array([zero_count, one_count, from_0_to_higher, from_1_to_lower])) # Zugrunde liegende Werte zur LA Auswertung time_pos_res_remain = actual_df[actual_df['pow_rest'] > 0].count()['pow_rest'] time_neg_res_remain = actual_df[actual_df['pow_rest'] < 0].count()['pow_rest'] time_no_res_remain = actual_df[actual_df['pow_rest'] == 0].count()['pow_rest'] la_pos = 1 - (time_pos_res_remain / actual_df["pow_rest"].count()) la_neg = 1 - (time_neg_res_remain / actual_df["pow_rest"].count()) la_eq = (time_no_res_remain / actual_df["pow_rest"].count()) la.append(np.array([la_pos, la_neg, la_eq])) cap_change.append(eval_df_pos_neg(actual_df, "change_act_st_cap")) # Ergebnisse sammeln pos_neg_stat = eval_df_pos_neg(actual_df, "pow_rest") cap_dep_res.append(np.array([*pos_neg_stat, actual_df["act_st_cap"].iloc[-1]])) actual_df.to_csv(fn_act+".csv", sep=";") cap_dep_res = np.array(cap_dep_res) st_state = np.array(st_state) la = np.array(la) cap_change= np.array(cap_change) # Rückgabe je nach Bedarf if make_df: cd_names=['load_pos_mean', 'load_pos_sum', 'load_pos_count', 'load_neg_mean', 'load_neg_sum', 'load_neg_count', "final_storedCharge"] st_names =["empty", "full", "0->up", "1->down"] la_names= ["LA+", "LA-", "eq_ratio"] cap_change_names=['load_diff_pos_mean', 'load_diff_pos_sum', 'load_diff_pos_count', 'load_diff_neg_mean', 'load_diff_neg_sum', 'load_diff_neg_count'] col_names = [ *cd_names, *st_names, *la_names, *cap_change_names] combined_df = pd.DataFrame(np.hstack((cap_dep_res, st_state, la, cap_change)), columns=col_names) combined_df["storage capacity"] = m_cap combined_df["Eff_dis"] = EFF_STOR combined_df.to_csv(file_name+"_cap_dependence.csv",sep=";") return combined_df else: return cap_dep_res, st_state, la, cap_change
[docs] def esm_residual_to_df(country=None, file_in=None, col_name="residual-no-storage", first_day=None, save_csv=True, verbose=False, get_countries=False, normalize=True): """ Reads ESM residual load data from a CSV or Excel file, extracts the data for a specific country, sets the date as the index, and optionally normalizes the data. Parameters: ----------- country : str, optional The country for which the residual load data should be extracted. Defaults to "DE0 Tot" if not specified. file_in : str, optional The filename (without extension) containing the residual load data. If None, defaults to '2024_Bertsch_residual_Load'. col_name : str, optional The name of the column to store the extracted residual load data. Default is "residual-no-storage". first_day : str, optional Not used in the current function. save_csv : bool, optional If True, saves the extracted and transposed data to a CSV file for future use. Default is True. verbose : bool, optional If True, prints the first few rows of the dataset and the index for debugging purposes. Default is False. get_countries : bool, optional If True, returns a list of available country names from the dataset instead of extracting data. Default is False. normalize : bool, optional If True, normalizes the extracted residual load data by computing the absolute mean of the negative values (=> positive!). The original values are then divided by this value. Default is True. Returns: -------- pd.DataFrame or list - If `get_countries` is True: Returns a list of available country names. - Otherwise: Returns a DataFrame with extracted residual load data for the specified country. Raises: ------- ValueError If the specified file cannot be loaded. FileNotFoundError If neither the CSV nor the Excel file is found. Notes: ------ - The function first attempts to read data from a CSV file. If the CSV is missing, it tries to read from an Excel file. - If an Excel file is used, the dataset is transposed, and a datetime index is generated starting from '2050-01-01 00:00:00' with hourly frequency. - Normalization is performed only if the normalization factor (difference between mean positive and mean negative values) is nonzero. """ COL_N_ORIGINAL = "residual-original" if file_in is None: file_in = '2024_Bertsch_residual_Load' # **Pfad zum data-Ordner setzen** base_dir = os.path.dirname(os.path.abspath(__file__)) # Ordner von residual_analyse.py data_dir = os.path.join(base_dir, "data") # Wechselt eine Ebene hoch und geht in "data" file_csv = os.path.join(data_dir, file_in + ".csv") file_xlsx = os.path.join(data_dir, file_in + ".xlsx") df_transponiert = None # Initialisierung try: df_transponiert = pd.read_csv(file_csv, index_col=0) except: try: df = pd.read_excel(file_xlsx) df_transponiert = df.set_index('node').T start_date = '2050-01-01 00:00:00' date_range = pd.date_range(start=start_date, periods=8760, freq='h') df_transponiert.index = date_range if save_csv: df_transponiert.to_csv(file_csv) except FileNotFoundError: print(f"Fehler: Datei '{file_csv}' oder '{file_xlsx}' nicht gefunden.") except Exception as e: print(f"Ein unerwarteter Fehler ist aufgetreten: {e}") if df_transponiert is None: raise ValueError(f"Konnte Datei '{file_csv}' oder '{file_xlsx}' nicht laden!") if verbose: print(df_transponiert.head()) print(df_transponiert.index) if get_countries: return list(df_transponiert.columns) if country is None: country = "DE0 Tot" actual_values = df_transponiert[country].values actual_country_df = pd.DataFrame(actual_values, columns=[col_name]) actual_country_df[COL_N_ORIGINAL] = actual_country_df[col_name] actual_country_df.index = pd.to_datetime(df_transponiert.index) if normalize: actual_values = actual_country_df[COL_N_ORIGINAL].values hours = (actual_country_df.index[-1] - actual_country_df.index[0]).total_seconds() / 3600 # Compute mean of the negative values separately # this is, what could be stored on average per year norm_factor = abs(actual_values[actual_values < 0].sum()) \ / hours actual_country_df.loc[:, 'residual-not-normalized'] = actual_country_df[col_name] if norm_factor != 0: # Avoid division by zero actual_country_df[col_name] = actual_country_df[col_name]/ norm_factor else: pass # Keep original if no variation return actual_country_df
if __name__ == "__main__": test_countries = esm_residual_to_df(get_countries =True) test_df = esm_residual_to_df() EFF_STOR = .75 m_cap = np.linspace(1e6, 20e6, 15) if test: verbose = False df = pd.read_excel('2024_Bertsch_residual_Load.xlsx') # Lade die Excel-Datei mit pandas # output_folder = create_output_folder() # Transponiere die Tabelle df_transponiert = df.set_index('node').T df_transponiert['time_diff_seconds'] = np.zeros( len(df_transponiert)) + 3600 df_transponiert['time'] = np.arange(0, len(df_transponiert) * 3600., 3600) countries = ["DE0 Tot", "IT0 0", "GB5 0", "ES0 0", "IE4 0"] spalten = ["act_st_cap", "power_st_in", "SOC"] actual_df = df_transponiert["DE0 Tot"].values actual_df = pd.DataFrame(actual_df, columns=["residual"]) # Beispielaufruf der neuen Funktion results_df = calc_capacity_dependence(actual_df, m_cap, EFF_STOR, make_df=True, file_name=f_name_base) # Wenn du die NumPy-Arrays willtest cap_dep_res, st_state, la, cap_change = calc_capacity_dependence(actual_df, m_cap, EFF_STOR, make_df=False, file_name=f_name_base) fi_n, (ax_n, ax_s)= plt.subplots(2) fi_n2, (ax_c, ax_e)= plt.subplots(2) # Energy integral ax_n.plot(m_cap, cap_dep_res[:, 1], "k", label="pos res") # integral of remaining negative residuals ax_n.plot(m_cap, -cap_dep_res[:, 4], "b:", label="neg res") ax_n.plot(m_cap, cap_dep_res[:, -1], "bv", label = "stored finally") # am Ende im Speicher ax_n.plot(m_cap, cap_dep_res[:, 1] + \ cap_dep_res[:, 4], "kv-", label="sum res") ax_n.plot(m_cap, cap_dep_res[:, 1]+cap_dep_res[:, 4]+cap_dep_res[:, -1], "ro", label="sum") ax_n.legend(loc='upper left', bbox_to_anchor=(1, 1)) ax_n.set_xlabel("storage capacity") ax_n.set_ylabel("integral energy") # Storage loads ax_s.plot(m_cap, st_state[:, 0], "k", label="empty") ax_s.plot(m_cap, st_state[:, 1]/20, "b", label="full/20") ax_s.plot(m_cap, st_state[:, 2], "k", label="0->up") ax_s.plot(m_cap, st_state[:, 3], "k", label="1->down") ax_s.legend(loc='upper left', bbox_to_anchor=(1, 1)) ax_s.set_xlabel("storage capacity") ax_s.set_ylabel("number of states") # Raum für die Legende schaffen fi_n.tight_layout(rect=[0, 0, 0.85, 1]) fi_n.savefig(f_name_base+"-number_cap-plot.png") # Storage capacity change ax_c.plot(m_cap, cap_change[:, 1], "k", label="charging") ax_c.plot(m_cap, -cap_change[:, 4], "b", label="discharging") ax_e.plot(m_cap, cap_change[:, 2], "k", label="number chg") ax_e.plot(m_cap, cap_change[:, 5], "b:", label="number disc") ax_c.legend(loc='upper left', bbox_to_anchor=(1, 1)) ax_e.legend(loc='upper left', bbox_to_anchor=(1, 1)) ax_c.set_xlabel("storage capacity") ax_c.set_ylabel("integral charging") # Raum für die Legende schaffen fi_n2.tight_layout(rect=[0, 0, 0.85, 1]) fi_n2.savefig(f_name_base+"-integral-charge_cap-plot.png") # Number of full charging equivalents fi_n3, (ax_g, ax_h)= plt.subplots(2) equiv_no_charging = cap_change[:, 1] / m_cap equiv_no_discharging = cap_change[:, 4] / m_cap ax_g.plot(m_cap, equiv_no_charging, "k", label="charging") ax_g.plot(m_cap, equiv_no_discharging, "b:", label="discharging") ax_g.set_xlabel("storage capacity") ax_g.set_ylabel("no eq. full cyc. / y") ax_g.legend(loc='upper left', bbox_to_anchor=(1, 1)) e2p_mean_charge = m_cap/cap_change[:, 0] e2p_mean_discharge = m_cap/cap_change[:, 3] ax_h.plot(m_cap, e2p_mean_charge, "k", label="charging") ax_h.plot(m_cap, e2p_mean_discharge, "b:", label="discharging") ax_h.set_xlabel("storage capacity") ax_h.set_ylabel("E2P / h") ax_h.legend(loc='upper left', bbox_to_anchor=(1, 1)) # Raum für die Legende schaffen fi_n3.tight_layout(rect=[0, 0, 0.85, 1]) fi_n3.savefig(f_name_base+"-charge_discharge-plot.png") fig_e, ax_p =plt.subplots(1) ax_p.plot(m_cap, la[:,0], "k", label="LA_pos") ax_p.plot(m_cap, la[:,1], "b", label="LA_neg") ax_p.plot(m_cap, la[:,2], "r:", label="ratio_eq") ax_p.legend(loc='upper left', bbox_to_anchor=(1, 1)) ax_p.set_title("Level of Autononomy (LA)") ax_p.set_xlabel("storage capacity") ax_p.set_ylabel("LA") # Raum für die Legende schaffen fig_e.tight_layout(rect=[0, 0, 0.85, 1]) fig_e.savefig(f_name_base+"-la-plot.png") #----------------------- #----------------------- aa =storage(-1, 2 , 5, .5,.1666,) print (aa) pd.set_option('display.max_columns', None) # a, b, c = analyze_residual(data_0, 'Residuallast') df = pd.read_excel(DATA_DIR/'2024_Bertsch_residual_Load.xlsx') # Lade die Excel-Datei mit pandas # output_folder = create_output_folder() # Transponiere die Tabelle df_transponiert = df.set_index('node').T df_transponiert['time_diff_seconds'] = np.zeros( len(df_transponiert)) + 3600 df_transponiert['time']= np.arange(0, len(df_transponiert) * 3600., 3600) countries= ["DE0 Tot"]# , "IT0 0", "GB5 0", "ES0 0", "IE4 0"] sum_units= 1/1000 EFF_STOR = .75 m_cap = np.linspace(1e6, 20e6, 15) for what in countries: fnam =f_name_base+what[:3] res_max = df_transponiert[what].max() m_cap = np.linspace(res_max/20, res_max*20, 7) results_df = calc_capacity_dependence(df_transponiert[what], m_cap, EFF_STOR, make_df=True, file_name=f_name_base+"-"+what[:3]+"-", title = what[:3]) fig_e, ax_p = plt.subplots() line_labels = ['LA+', 'LA-', 'eq_ratio'] colors = ['black', 'blue', 'green'] # Beispiel Farben können angepasst werden for label, color in zip(line_labels, colors): sbn.lineplot(data=results_df, x='storage capacity', y=label, ax=ax_p, label=label, color=color) # Anpassen der Legende ax_p.legend(loc='upper left', bbox_to_anchor=(1, 1)) ax_p.set_title("Level of Autonomy (LA), "+what[:3]) ax_p.set_xlabel("Storage Capacity") ax_p.set_ylabel("LA") # Raum für die Legende schaffen fig_e.tight_layout(rect=[0, 0, 0.85, 1]) # Speichern des Plots fig_e.savefig("la-plot.png") plt.show() all_periods, pos_neg, neg_pos = analyze_residual(df_transponiert, what) neg_pos[['sum_pos', 'sum_neg']] = neg_pos[[ 'sum_pos', 'sum_neg']] * sum_units sbn.scatterplot(data=neg_pos, x='count_neg', y='count_pos', size = 'sum_pos', hue = 'sum_neg') # , style='mean_pos') plt.legend(loc='upper left', bbox_to_anchor=(1, 1)) # Optional: Ändere die Größe des Plots, um Platz für die Legende zu schaffen plt.tight_layout() plt.title(what) plt.savefig(fnam+"sum_count.jpg") plt.figure() sbn.scatterplot(data=neg_pos, x='sum_neg', y='sum_pos', size = 'count_pos', hue = 'count_neg') # , style='mean_pos') plt.legend(loc='upper left', bbox_to_anchor=(1, 1)) # Optional: Ändere die Größe des Plots, um Platz für die Legende zu schaffen plt.tight_layout() plt.title(what) plt.savefig(fnam+"_count_sum.jpg") fi, ax= plt.subplots(2) print( f"country:{what}, No.-periods: {pos_neg['count_pos'].count()}") act_corr= signal.correlate( df_transponiert[what], df_transponiert[what]) # f, Pxx_den = signal.periodogram(data[dat_act[1][2]], fs =1/600) # ax4.semilogy(f, Pxx_den) ax[0].plot(act_corr) # positive_values = df_transponiert[what][df_transponiert[what] > 0] # Falls die Indizes wichtig sind, erstelle einen Array nur mit den positiven Werten # Setze negative Werte auf 0 df_transponiert[what+"mod"] = np.where( df_transponiert[what] < 0, 0, df_transponiert[what]) # Erstelle ein NumPy-Array aus der Spalte modified_array = df_transponiert[what+"mod"].to_numpy() # Berechne die Kreuzkorrelation der modifizierten Werte # , mode='full') corr = signal.correlate(modified_array, modified_array) # Optional: Wenn du auch die Lag-Werte berechnen möchtest lags = signal.correlation_lags( len(modified_array), len(modified_array), mode ='full') # Ausgabe der Korrelation und der Lags lc = len(corr) corr_n = corr[int((lc-1)/2):] ax[1].plot(corr_n[:200]/np.abs(corr_n).max()) ax[0].set_title("Autocorrelation, "+what[:3]) fi.savefig(fnam+"_correl.jpg") neg_pos.to_csv(fnam+"neg_pos.csv",sep=';') pos_neg.to_csv(fnam+"pos_neg.csv",sep=';') all_periods.to_csv(fnam+"all_periods.csv",sep=';') neg_sum = np.where( df_transponiert[what] > 0, 0, df_transponiert[what]).sum() pos_sum = np.where( df_transponiert[what] < 0, 0, df_transponiert[what]).sum() print(f"{what}, pos/neg residuals: {-pos_sum/neg_sum:.2f}")