You signed in with another tab or window.Reload to refresh your session.You signed out in another tab or window.Reload to refresh your session.You switched accounts on another tab or window.Reload to refresh your session.Dismiss alert
defload_csv_as_data_x(file_path):""" Lädt eine CSV-Datei und gibt sie als DataFrame zurück. Die relevanten Spaltennamen werden generalisiert: - 'plannedArrival' oder 'plannedDeparture' -> 'planned' - 'changedArrivalTime' oder 'changedDepartureTime' -> 'changed' - 'arrival_delay' oder 'departure_delay' -> 'delay' Zusätzlich werden alle negativen 'delay'-Werte auf 0 gesetzt. :param file_path: Der Pfad zur CSV-Datei, die geladen werden soll. :return: DataFrame mit generalisierten Spaltennamen und angepassten Werten. """try:# CSV-Datei einlesen, unter der Annahme, dass die Spalten durch Semikolon getrennt sinddf=pd.read_csv(file_path,sep=";",header=0)# Spaltenumbenennung basierend auf gemeinsamer Bedeutungrename_mapping= {'plannedArrival':'planned','plannedDeparture':'planned','changedArrivalTime':'changed','changedDepartureTime':'changed','arrival_delay':'delay','departure_delay':'delay' }# Nur Spalten umbenennen, die tatsächlich in der CSV vorhanden sinddf.rename(columns={col:rename_mapping[col]forcolindf.columnsifcolinrename_mapping},inplace=True)# Falls die generalisierte Spalte 'delay' existiert, negative Werte auf 0 setzenif'delay'indf.columns:df['delay']=df['delay'].apply(lambdax:max(x,0))exceptFileNotFoundError:raiseValueError(f"Die Datei unter dem Pfad{file_path} wurde nicht gefunden.")exceptpd.errors.EmptyDataError:raiseValueError("Die CSV-Datei ist leer.")exceptExceptionase:raiseValueError(f"Fehler beim Einlesen der Datei:{e}")returndf
Filtert die Daten entsprechend auf Wunsch nach Bahnhof, Datum oder Woche
deffilter_data_x(data,evas=None,date=None,week_number=None):""" Filtert die Daten basierend auf den gegebenen Parametern. :param data: DataFrame mit den zugrunde liegenden Daten. :param evas: Liste von EVA-Nummern der Bahnhöfe, nach denen gefiltert werden soll (Optional). :param date: Das Datum, nach dem gefiltert werden soll (Optional, im Format 'YYYY-MM-DD'). :param week_number: Die Kalenderwoche, nach der gefiltert werden soll (Optional). :return: Gefilterte Daten, die an die jeweilige Berechnungsfunktion übergeben werden können. """# Überprüfen, ob die Daten vorhanden sindifdata.empty:print("Es sind keine Daten vorhanden.")returnNone# Stelle sicher, dass die EVA-Spalte als String behandelt wirddata['eva']=data['eva'].astype(str)# Wenn evas nicht spezifiziert, berücksichtige alleifevasisnotNone:# Falls nur ein einzelner Wert übergeben wurde, konvertiere ihn in eine Listeifisinstance(evas,str):evas= [evas]# Falls die übergebenen EVAs nicht in den Daten sind, gib eine Warnung ausifnotset(evas).issubset(data['eva'].values):print(f"Es gibt keine Bahnhöfe mit den EVA-Nummern{evas}.")returnNone# Filtere nach den angegebenen EVA-Nummerndata=data[data['eva'].isin(evas)]# Wenn ein Datum angegeben ist, nach Jahr, Monat und Tag filternifdate:data['date']=pd.to_datetime(data['planned']).dt.datedata=data[data['date']==pd.to_datetime(date).date()]# Wenn eine Kalenderwoche angegeben ist, nach dieser filternifweek_number:ifdate:print("Tag und Kalenderwoche können nicht gleichzeitig ausgewählt werden.")returnNonedata['week_number']=pd.to_datetime(data['planned']).dt.isocalendar().weekdata=data[data['week_number']==week_number]returndata
Kapitel: Visualisierungsfunktionen
Erstellt den Balken Graph zu den Daten
defplot_bar_chart_by_category_and_station_x(data,x_column='X',y_column='Y',hue_column='station',title='',x_axis_label='Zugkategorie',y_axis_label='Verspätungsprozentsatz',palette='Set2',y_limit_factor=1.1,text_offset_factor=0.03,show_values=True,fig_size_x=17,fig_size_y=8,int_value=True):""" Erstellt ein Balkendiagramm, das für jede Zugart (x-Achse) Balken für jeden Bahnhof oder Kalenderwoche anzeigt. :param data: DataFrame mit den Daten für das Diagramm. :param x_column: Name der Spalte für die x-Achse (z. B. Zugkategorien). :param y_column: Name der Spalte für die y-Achse (z. B. Verspätungsprozentsatz). :param hue_column: Name der Spalte für die Farbcodierung (z. B. Bahnhöfe oder Kalenderwoche). :param title: Titel des Diagramms. :param x_axis_label: Bezeichnung der x-Achse. :param y_axis_label: Bezeichnung der y-Achse. :param palette: Farbpalette für die Balken (Standard: 'Set2'). :param y_limit_factor: Faktor für die Y-Achsen-Limit (Standard: 1.1). :param text_offset_factor: Faktor zur Berechnung des Abstands für die Textanzeige (Standard: 0.03). :param show_values: Boolean, ob die Werte über den Balken angezeigt werden sollen (Standard: True). :param int_value: Boolean, ob die Zahlen über den Balken als int oder double angezeigt werden (Standardwert: True) """# Überprüfen, ob die Spalte "week_number" existiert, um den Namen der Legende anzupassenif'week_number'indata.columns:hue_column='week_number'# Ändere die Spalte für die Farbcodierunglegend_title='Kalenderwoche'# Setze den Legendentitelelse:legend_title='Bahnhof'# Standardwert, falls es keine "week_number"-Spalte gibt# Maximalwert für die Y-Achse berechneny_limit=data[y_column].max()*y_limit_factor# Abstand für die Zahl über den Balken berechnentext_offset_max_factor=data[y_column].max()*text_offset_factor# Balkendiagramm erstellenplt.figure(figsize=(fig_size_x,fig_size_y))ax=sns.barplot(x=x_column,y=y_column,hue=hue_column,data=data,palette=palette)# Titel und Achsenbeschriftungen setzenplt.title(title)plt.xlabel(x_axis_label)plt.ylabel(y_axis_label)# Werte über den Balken anzeigenifshow_values:forbarinax.patches:yval=bar.get_height()ifyval>0:# Nur Werte > 0 anzeigenifint_value:yval=int(yval)# Ganze Zahl, wenn int_value Trueelse:yval=round(yval,1)# Eine Nachkommastelle, wenn int_value Falseplt.text(bar.get_x()+bar.get_width()/2,yval+text_offset_max_factor,f"{yval}",# Anzeige des Wertsha='center',va='bottom' )# Y-Achse anpassenplt.ylim(0,y_limit)# Legende anpassenplt.legend(title=legend_title)plt.tight_layout()plt.show()
Erstellt einen Liniengraph zu den Zuglinien
defplot_line_chart(data,title='',xlabel='',ylabel='',xsize=10,ysize=4):""" Erstellt einen Liniengraphen für die Daten und stellt sicher, dass die X-Achse korrekt skaliert wird, abhängig davon, ob 'day_of_week' oder 'week_number' verwendet wird. :param data: DataFrame mit Spalten 'X', 'day_of_week'/'week_number', und 'Y'. :param title: Titel des Graphen. :param xlabel: Beschriftung der X-Achse. :param ylabel: Beschriftung der Y-Achse. """ifdataisNoneordata.empty:print("Keine Daten zum Plotten verfügbar.")returnplt.figure(figsize=(xsize,ysize))# X-Achse bestimmenif"day_of_week"indata.columns:x_column="day_of_week"x_ticks= ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]# Umwandlung in numerische Werte für die Reihenfolgeweekdays_order= {day:ifori,dayinenumerate(x_ticks)}data["x_value"]=data[x_column].map(weekdays_order)elif"week_number"indata.columns:x_column="week_number"x_ticks=sorted(data[x_column].unique())# Alle Kalenderwochen als numerische Wertedata["x_value"]=data[x_column]else:print("Keine gültige Spalte für die X-Achse gefunden.")return# Linien für jede Station erstellenforstation,groupindata.groupby("X"):plt.plot(group["x_value"],group["Y"],label=station,marker='o')# Werte über den Punkten anzeigenforx,yinzip(group["x_value"],group["Y"]):plt.text(x,y,f"{y:.2f}",ha="center",va="bottom",fontsize=9)# Plot-Detailsplt.title(title,fontsize=14)plt.xlabel(xlabelifxlabelelsex_column,fontsize=12)plt.ylabel(ylabel,fontsize=12)# Anpassung der X-Ticksifx_column=="day_of_week":plt.xticks(ticks=range(len(x_ticks)),labels=x_ticks,rotation=45)elifx_column=="week_number":plt.xticks(ticks=x_ticks,labels=x_ticks)plt.grid(visible=True,linestyle="--",alpha=0.7)plt.legend(title="Station",fontsize=10,title_fontsize=12)# Plot anzeigenplt.show()
Kapitel: Datenaufbereitung entsprechend der Forschungsfragen
Berechnet je Zugart wie viel Prozent der Züge verpätet sind. Bezieht nur Züge mit ein, deren Verstpätung höher alsdelay_thresholdsind.
defcalculate_percentage_delay_by_category_and_station_x(data,delay_threshold=0,categories=None,combine_all_categories=False):""" Berechnet den Prozentsatz der verspäteten Züge je Zugart und je Bahnhof sowie einen Durchschnittsbalken. :param data: DataFrame mit den zugrunde liegenden Daten. :param delay_threshold: Schwellenwert für die Verspätung in Minuten. :param categories: Optional, Liste spezifischer Zugarten zum Filtern (z. B. ['ICE', 'RE']). :param combine_all_categories: Boolean, ob alle Zugarten zusammen betrachtet werden sollen. Standard: False. :return: DataFrame mit den Spalten: X (Zugkategorie), Y (Verspätungsprozentsatz) und station (Bahnhof). """ifcombine_all_categories:# Alle Zugarten zusammen betrachten (trainCategory ignorieren)delayed_trains=data[data['delay']>delay_threshold]total_trains=data.groupby(['station']).size()delayed_trains_by_category=delayed_trains.groupby(['station']).size()# Prozentsatz der verspäteten Züge je Bahnhofpercentage_delay= (delayed_trains_by_category/total_trains)*100# DataFrame mit den Ergebnissenresult=percentage_delay.reset_index(name='delayPercentage')# Spalte 'X' für Zugkategorie setzen (als "Alle")result['X']='Alle'else:# Optional nach spezifischen Zugarten filternifcategories:data=data[data['trainCategory'].isin(categories)]# Züge filtern, die mehr als 'delay_threshold' Minuten Verspätung habendelayed_trains=data[data['delay']>delay_threshold]# Zugarten getrennt betrachten (falls keine spezifische Kategorie gefiltert wurde)total_trains=data.groupby(['trainCategory','station']).size()delayed_trains_by_category=delayed_trains.groupby(['trainCategory','station']).size()# Prozentsatz der verspäteten Züge je Zugkategorie und Bahnhofpercentage_delay= (delayed_trains_by_category/total_trains)*100# DataFrame mit den Ergebnissenresult=percentage_delay.reset_index(name='delayPercentage')# Umbenennen der Spalten für das gewünschte Formatresult=result.rename(columns={'trainCategory':'X'})# Umbenennen der Spalte für den Prozentsatzresult=result.rename(columns={'delayPercentage':'Y'})# NaN-Werte durch 0 ersetzen und auf 2 Dezimalstellen rundenresult['Y']=result['Y'].fillna(0).apply(lambdax:int(x*100)/100)returnresult
Berechnet die durchschnittliche Verspätung je Zugart in Minuten. Bezieht nur Züge mit ein, deren Verspätung höher alsdelay_threshold sind
defcalculate_average_min_delay_by_category_and_station_x(data,delay_threshold=0,categories=None,combine_all_categories=False):""" Berechnet die durchschnittliche Verspätung in Minuten pro Zugart und Bahnhof. :param data: DataFrame mit den zugrunde liegenden Daten. :param delay_threshold: Minimaler Schwellenwert für die Verspätung (in Minuten), um in die Berechnung einbezogen zu werden. :param categories: Optional, Liste spezifischer Zugarten zum Filtern (z. B. ['ICE', 'RE']). :param combine_all_categories: Boolean, ob alle Zugarten zusammen betrachtet werden sollen. Standard: False. :return: DataFrame mit den Spalten 'X' (Zugart), 'Y' (Durchschnittliche Verspätung in Minuten) und 'station' (Bahnhof). """ifdata.empty:print("Keine Daten für die Berechnung der Verspätung vorhanden.")returnNone# Nur Züge mit einer Verspätung größer als der Schwellenwert berücksichtigendelayed_data=data[data['delay']>delay_threshold]ifdelayed_data.empty:print(f"Keine Züge mit einer Verspätung größer als{delay_threshold} Minuten gefunden.")returnNoneifcombine_all_categories:# Alle Zugarten zusammen betrachten (trainCategory ignorieren)avg_delay_by_station=delayed_data.groupby(['station'])['delay'].mean().reset_index()# Spalte 'X' für Zugkategorie setzen (als "Alle")avg_delay_by_station['X']='Alle'# Umbenennen der Spalten für das gewünschte Formatresult=avg_delay_by_station.rename(columns={'delay':'Y'})else:# Optional nach spezifischen Zugarten filternifcategories:delayed_data=delayed_data[delayed_data['trainCategory'].isin(categories)]# Durchschnittliche Verspätung pro Zugart und Bahnhof berechnenavg_delay_by_category_station=delayed_data.groupby(['trainCategory','station'])['delay'].mean().reset_index()# Ergebnis formatierenavg_delay_by_category_station['X']=avg_delay_by_category_station['trainCategory']avg_delay_by_category_station=avg_delay_by_category_station[['X','station','delay']]# Umbenennen der Spalten für das gewünschte Formatresult=avg_delay_by_category_station.rename(columns={'delay':'Y'})# Runden der durchschnittlichen Verspätung auf 1 Dezimalstelleresult['Y']=result['Y'].round(1)returnresult
Berechnet wie viel Prozent der Verspäteten Züge in eine Bestimmte Verspätungskategorie fallen.
defcalculate_delay_statistics_by_train_type_and_station_x(data,train_type=None):""" Berechnet die Statistik der Verspätungen für bestimmte Zugarten oder für alle Züge, wenn keine Zugart angegeben ist, aus einem bereits gefilterten DataFrame. :param data: DataFrame, das die bereits gefilterten Daten enthält. :param train_type: Liste von Zugarten, für die die Statistik berechnet werden soll. Wenn None, wird für alle Zugarten berechnet. :return: DataFrame mit den Verspätungskategorien (X) und deren prozentualem Anteil (Y) sowie der Stationen-Spalte. """# Wenn Zugarten angegeben sind, nur diese berücksichtigeniftrain_type:data=data[data['trainCategory'].isin(train_type)]# Überprüfen, ob es nach der Filterung noch Daten gibtifdata.empty:print(f"Keine Daten für die Zugart '{train_type}' gefunden."iftrain_typeelse"Keine Daten gefunden.")returnNone# Filtere auf Züge mit einer positiven Verspätungdf_delayed=data[data['delay']>0]# Überprüfen, ob es verspätete Züge gibtifdf_delayed.empty:print(f"Es gibt keine verspäteten Züge.")returnNone# Gesamtanzahl der verspäteten Zügetotal_delays=len(df_delayed)# Berechnung des prozentualen Anteils in jeder Kategorie und nach Stationdelay_stats= []forstationindf_delayed['station'].unique():# Gruppiert nach Stationstation_data=df_delayed[df_delayed['station']==station]stats= {'station':station,'< 10 min': (station_data['delay']<10).sum()/len(station_data)*100,'< 30 min': ((station_data['delay']>=10)& (station_data['delay']<30)).sum()/len(station_data)*100,'< 60 min': ((station_data['delay']>=30)& (station_data['delay']<60)).sum()/len(station_data)*100,'< 120 min': ((station_data['delay']>=60)& (station_data['delay']<120)).sum()/len(station_data)*100,'> 120 min': (station_data['delay']>=120).sum()/len(station_data)*100 }delay_stats.append(stats)# Umwandlung der Statistik in einen DataFramestats_df=pd.DataFrame(delay_stats)# Reshape für die Darstellung der Kategorien in der Spalte 'X'stats_df=pd.melt(stats_df,id_vars=['station'],var_name='X',value_name='Y')# Optional: Y-Werte rundenstats_df['Y']=stats_df['Y'].apply(lambdax:round(x,1))returnstats_df
defcalculate_delay_by_line_and_day(data,line='',output_type="%",delay_threshold=0):""" Berechnet den Prozentsatz oder die durchschnittliche Verspätung einer bestimmten Zuglinie je Wochentag und Bahnhof. """ifdata.empty:print("Keine Daten verfügbar.")returnNone# Nach der gewünschten Linie filternfiltered_data=data[data["Verbindung"]==line].copy()iffiltered_data.empty:print(f"Keine Daten für die Linie{line} gefunden.")returnNone# Verspätung filterndelayed_data=filtered_data[filtered_data["delay"]>delay_threshold].copy()# Wochentag hinzufügen (mit .loc, um die Warnung zu vermeiden)filtered_data.loc[:,"day_of_week"]=pd.to_datetime(filtered_data["planned"]).dt.day_name()delayed_data.loc[:,"day_of_week"]=pd.to_datetime(delayed_data["planned"]).dt.day_name()# Gruppierung nach Bahnhof und Wochentagifoutput_type=="%":total_trains=filtered_data.groupby(["station","day_of_week"]).size()delayed_trains=delayed_data.groupby(["station","day_of_week"]).size()result= (delayed_trains/total_trains)*100elifoutput_type=="min":result=delayed_data.groupby(["station","day_of_week"])["delay"].mean()else:print("Ungültiger output_type. Bitte 'min' oder '%' angeben.")returnNone# Ergebnis in DataFrame umwandelnresult=result.reset_index(name="Y")result=result.rename(columns={"station":"X"})# Format für die Spalte X und Y# Runden der Ergebnisseifoutput_type=="%":result["Y"]=result["Y"].fillna(0).round(2)elifoutput_type=="min":result["Y"]=result["Y"].round(1)# Benutzerdefinierte Sortierung der Wochentageweekdays_order= ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]result["day_of_week"]=pd.Categorical(result["day_of_week"],categories=weekdays_order,ordered=True)result=result.sort_values(by=["X","day_of_week"]).reset_index(drop=True)returnresult
defcalculate_cancellations_by_train_type_and_station(data,train_types=None,# Neuer Parameter für die Liste der Zugartencalculate_percentage=True):""" Berechnet entweder den Prozentsatz oder die absolute Anzahl ausgefallener Züge je Zugart und Bahnhof. Optional kann eine Liste von Zugarten übergeben werden, um nur die Daten für diese Zugarten zu berechnen. Wenn keine Liste übergeben wird, werden alle Zugarten berücksichtigt. :param data: DataFrame mit den zugrunde liegenden Daten. :param train_types: Liste der Zugarten, für die die Berechnungen durchgeführt werden sollen (Optional). :param calculate_percentage: Boolean, ob der Prozentsatz (True) oder die absolute Anzahl (False) berechnet werden soll. :return: DataFrame mit den Spalten: X (Zugart), Y (Wert je nach Auswahl) und station (Bahnhof). """# Filtern der ausgefallenen Zügecancelled_trains=data[data['eventStatus']=='c']# Wenn eine Liste von Zugarten übergeben wurde, filtern wir die Dateniftrain_types:cancelled_trains=cancelled_trains[cancelled_trains['trainCategory'].isin(train_types)]data=data[data['trainCategory'].isin(train_types)]# Berechnung der Züge pro Zugart und Bahnhof, auch für Zugarten ohne Verspätungtotal_trains=data.groupby(['trainCategory','station']).size()# Wenn keine ausgefallenen Züge vorhanden sind, setzen wir sie auf 0cancellations=cancelled_trains.groupby(['trainCategory','station']).size().reindex(total_trains.index,fill_value=0)ifcalculate_percentage:# Berechnung des Prozentsatzespercentage_cancellations= (cancellations/total_trains)*100# Umwandlung in DataFrameresult=percentage_cancellations.reset_index(name='Y')else:# Umwandlung in DataFrameresult=cancellations.reset_index(name='Y')# Hinzufügen der X-Achsen-Spalte (Zugart)result=result.rename(columns={'trainCategory':'X'})# NaN-Werte durch 0 ersetzen und Zahlen auf zwei Dezimalstellen rundenresult['Y']=result['Y'].fillna(0).apply(lambdax:int(x*100)/100ifcalculate_percentageelseint(x))returnresult
defcalculate_cancellations_by_operator_and_station(data,operators=None,# Neuer Parameter: Liste von Operatorencalculate_percentage=True):""" Berechnet entweder den Prozentsatz oder die absolute Anzahl ausgefallener Züge je Operator und Bahnhof. Optional kann eine Liste von Operatoren übergeben werden, um nur die Daten für diese Operatoren zu berechnen. Wenn keine Liste übergeben wird, werden alle Operatoren berücksichtigt. :param data: DataFrame mit den zugrunde liegenden Daten. :param operators: Liste von Operatoren, für die die Berechnungen durchgeführt werden sollen (Optional). :param calculate_percentage: Boolean, ob der Prozentsatz (True) oder die absolute Anzahl (False) berechnet werden soll. :return: DataFrame mit den Spalten: X (Operator), Y (Wert je nach Auswahl) und station (Bahnhof). """# Filtern der ausgefallenen Zügecancelled_trains=data[data['eventStatus']=='c']# Wenn eine Liste von Operatoren übergeben wurde, filtern wir die Datenifoperators:cancelled_trains=cancelled_trains[cancelled_trains['operator'].isin(operators)]data=data[data['operator'].isin(operators)]# Berechnung der Züge pro Operator und Bahnhof, auch für Operatoren ohne Verspätungtotal_trains=data.groupby(['operator','station']).size()# Wenn keine ausgefallenen Züge vorhanden sind, setzen wir sie auf 0cancellations=cancelled_trains.groupby(['operator','station']).size().reindex(total_trains.index,fill_value=0)ifcalculate_percentage:# Berechnung des Prozentsatzespercentage_cancellations= (cancellations/total_trains)*100# Umwandlung in DataFrameresult=percentage_cancellations.reset_index(name='Y')else:# Umwandlung in DataFrameresult=cancellations.reset_index(name='Y')# Hinzufügen der X-Achsen-Spalte (Operator)result=result.rename(columns={'operator':'X'})# NaN-Werte durch 0 ersetzen und Zahlen auf zwei Dezimalstellen rundenresult['Y']=result['Y'].fillna(0).apply(lambdax:int(x*100)/100ifcalculate_percentageelseint(x))returnresult
defcalculate_cancellations_by_line_and_day(data,line='',output_type='absolute',group_by='day_of_week'):""" Berechnet entweder die Ausfallquote oder die absoluten Ausfälle je Gruppierungskriterium (Wochentag/Kalenderwoche) und Bahnhof für eine bestimmte Linie. Parameters: - data: Der DataFrame mit den Zugdaten. - line: Die Linie, für die die Ausfälle berechnet werden sollen. - output_type: Gibt an, ob die Ausgabe die Ausfallquote (%) oder die absolute Zahl der Ausfälle ('absolute') ist. - group_by: Gibt an, ob die Gruppierung nach Wochentagen ('day_of_week') oder Kalenderwochen ('week_number') erfolgen soll. Returns: - DataFrame mit Spalten 'X' (Station), Gruppierungskriterium ('day_of_week'/'week_number'), und 'Y' (Ausfallwerte). """ifdata.empty:print("Keine Daten verfügbar.")returnNone# Nach der gewünschten Linie filternfiltered_data=data[data["Verbindung"]==line].copy()iffiltered_data.empty:print(f"Keine Daten für die Linie{line} gefunden.")returnNone# Füge die Spalte für Wochentage oder Kalenderwochen hinzuifgroup_by=='day_of_week':filtered_data["day_of_week"]=pd.to_datetime(filtered_data["planned"]).dt.day_name()day_order= ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]filtered_data["day_of_week"]=pd.Categorical(filtered_data["day_of_week"],categories=day_order,ordered=True)elifgroup_by=='week_number':filtered_data["week_number"]=pd.to_datetime(filtered_data["planned"]).dt.isocalendar().weekelse:print("Ungültiger group_by-Wert. Bitte 'day_of_week' oder 'week_number' angeben.")returnNone# Nur ausgefallene Züge berücksichtigen (eventStatus == 'c')cancelled_data=filtered_data[filtered_data["eventStatus"]=='c'].copy()# Alle Stationen und Gruppierungswerte sicherstellenall_stations=filtered_data["station"].unique()all_group_values=filtered_data[group_by].dropna().unique()# Ergebnis für alle Kombinationen von Station und Gruppierungswert initialisierenresult=pd.MultiIndex.from_product([all_stations,all_group_values],names=["station",group_by])result=pd.DataFrame(index=result).reset_index()# Gruppierung nach Bahnhof und Gruppierungskriteriumifoutput_type=='absolute':cancelled_trains=cancelled_data.groupby(["station",group_by]).size().reset_index(name="cancelled_trains")# Merge der Gruppen mit den Gruppierungswerten und Stationenresult=result.merge(cancelled_trains,how="left",on=["station",group_by])# Absolute Ausfälle (ersetzen von NaN mit 0)result["Y"]=result["cancelled_trains"].fillna(0).astype(int)result.drop(columns=["cancelled_trains"],inplace=True)elifoutput_type=='%':cancelled_trains=cancelled_data.groupby(["station",group_by]).size().reset_index(name="cancelled_trains")# Berechnung der Ausfallquote in Prozentresult=result.merge(cancelled_trains,how="left",on=["station",group_by])# Ausfallquote in Prozent berechnentotal_trains=len(filtered_data)iftotal_trains>0:result["Y"]= (result["cancelled_trains"]/total_trains)*100else:result["Y"]=0result["Y"]=result["Y"].fillna(0).round(2)result.drop(columns=["cancelled_trains"],inplace=True)else:print("Ungültiger output_type. Bitte 'absolute' oder '%' angeben.")returnNone# Sortieren der Ergebnisse basierend auf dem Gruppierungswertresult=result.sort_values(by=["station",group_by]).reset_index(drop=True)# Umbenennen der 'station' Spalte in 'X'result.rename(columns={"station":"X"},inplace=True)returnresult
defcalculate_cancellations_by_operator_and_week(data,operator=None,# Neuer Parameter für den Operatorcalculate_percentage=True):""" Berechnet entweder den Prozentsatz oder die absolute Anzahl ausgefallener Züge je Operator und Kalenderwoche. Optional kann ein bestimmter Operator übergeben werden, um nur die Daten für diesen Operator zu berechnen. Wenn kein Operator übergeben wird, werden auch Operatoren ohne Verspätungen berücksichtigt. :param data: DataFrame mit den zugrunde liegenden Daten. :param operator: Der Operator, für den die Berechnungen durchgeführt werden sollen (Optional). :param calculate_percentage: Boolean, ob der Prozentsatz (True) oder die absolute Anzahl (False) berechnet werden soll. :return: DataFrame mit den Spalten: X (Operator), Y (Wert je nach Auswahl) und week_number (Kalenderwoche). """# Filtern der ausgefallenen Zügecancelled_trains=data[data['eventStatus']=='c']# Wenn ein bestimmter Operator übergeben wird, filtern wir die Datenifoperator:cancelled_trains=cancelled_trains[cancelled_trains['operator']==operator]data=data[data['operator']==operator]# Berechnung der Züge pro Operator und Kalenderwoche, auch für Operatoren ohne Verspätungtotal_trains=data.groupby(['operator','week_number']).size()# Wenn keine ausgefallenen Züge vorhanden sind, setzen wir sie auf 0cancellations=cancelled_trains.groupby(['operator','week_number']).size().reindex(total_trains.index,fill_value=0)ifcalculate_percentage:# Berechnung des Prozentsatzespercentage_cancellations= (cancellations/total_trains)*100# Umwandlung in DataFrameresult=percentage_cancellations.reset_index(name='Y')else:# Umwandlung in DataFrameresult=cancellations.reset_index(name='Y')# Hinzufügen der X-Achsen-Spalte (Operator)result=result.rename(columns={'operator':'X'})# NaN-Werte durch 0 ersetzen und Zahlen auf zwei Dezimalstellen rundenresult['Y']=result['Y'].fillna(0).apply(lambdax:int(x*100)/100ifcalculate_percentageelseint(x))returnresult
defcalculate_cancellations_by_week(data,calculate_percentage=True):""" Berechnet entweder den Prozentsatz oder die absolute Anzahl ausgefallener Züge je Kalenderwoche, unabhängig von den Betreibern. :param data: DataFrame mit den zugrunde liegenden Daten. :param calculate_percentage: Boolean, ob der Prozentsatz (True) oder die absolute Anzahl (False) berechnet werden soll. :return: DataFrame mit den Spalten: 'X' (Kalenderwoche), 'Y' (Wert je nach Auswahl). """# Filtern der ausgefallenen Zügecancelled_trains=data[data['eventStatus']=='c']# Berechnung der Züge pro Wochetotal_trains_per_week=data.groupby(['week_number']).size()# Berechnung der Ausfälle pro Wochecancellations_per_week=cancelled_trains.groupby(['week_number']).size().reindex(total_trains_per_week.index,fill_value=0)ifcalculate_percentage:# Berechnung des Prozentsatzespercentage_cancellations= (cancellations_per_week/total_trains_per_week)*100# Umwandlung in DataFrameresult=percentage_cancellations.reset_index(name='Y')else:# Umwandlung in DataFrameresult=cancellations_per_week.reset_index(name='Y')# Hinzufügen der X-Achsen-Spalte (Kalenderwoche)result=result.rename(columns={'week_number':'X'})# NaN-Werte durch 0 ersetzen und Zahlen auf zwei Dezimalstellen rundenresult['Y']=result['Y'].fillna(0).apply(lambdax:int(x*100)/100ifcalculate_percentageelseint(x))returnresult
defcalculate_cancellation_statistics_by_train_type_and_station(data,train_types=None):""" Berechnet die Statistik der Zugausfälle nach Zugart und Station basierend auf der cancellation_delay. :param data: DataFrame mit den Spalten 'eventStatus', 'cancellation_delay', 'station', und 'trainCategory'. :param train_type: Liste von Zugarten, für die die Statistik berechnet werden soll. Wenn None, wird für alle Zugarten berechnet. :return: DataFrame mit den Kategorien (X), den Stationen und dem prozentualen Anteil (Y). """# Filter für ausgefallene Zügecancelled_trains=data[data['eventStatus']=='c']# Filtern nach Zugarten, falls train_type angegeben istiftrain_types:cancelled_trains=cancelled_trains[cancelled_trains['trainCategory'].isin(train_types)]# Überprüfen, ob es nach der Filterung noch Daten gibtifcancelled_trains.empty:print(f"Keine ausgefallenen Züge für die Zugart '{train_types}' gefunden."iftrain_typeselse"Keine ausgefallenen Züge gefunden.")returnNone# Kategorien für cancellation_delay definierencategories= {'> 120 min vorher':cancelled_trains['cancellation_delay']<=-120,'> 60 min vorher': (cancelled_trains['cancellation_delay']>-120)& (cancelled_trains['cancellation_delay']<=-60),'> 30 min vorher': (cancelled_trains['cancellation_delay']>-60)& (cancelled_trains['cancellation_delay']<=-30),'> 0 min vorher': (cancelled_trains['cancellation_delay']>-30)& (cancelled_trains['cancellation_delay']<=0),'< 30 min nachher': (cancelled_trains['cancellation_delay']>0)& (cancelled_trains['cancellation_delay']<=30),'< 60 min nachher': (cancelled_trains['cancellation_delay']>30)& (cancelled_trains['cancellation_delay']<=60),'> 60 min nachher':cancelled_trains['cancellation_delay']>60, }# Berechnung des Anteils der Kategorien pro Stationcancellation_stats= []forstationincancelled_trains['station'].unique():station_data=cancelled_trains[cancelled_trains['station']==station]stats= {'station':station}total_cancellations=len(station_data)forcategory,conditionincategories.items():stats[category]=condition[station_data.index].sum()/total_cancellations*100cancellation_stats.append(stats)# Umwandlung der Statistik in einen DataFramestats_df=pd.DataFrame(cancellation_stats)# Reshape für die Darstellung der Kategorien in der Spalte 'X'stats_df=pd.melt(stats_df,id_vars=['station'],var_name='X',value_name='Y')# Optional: Y-Werte rundenstats_df['Y']=stats_df['Y'].apply(lambdax:round(x,1))returnstats_df
defplot_cancellations_by_week(data,x_column='X',y_column='Y',title='Ausfälle pro Kalenderwoche',x_axis_label='Kalenderwoche',y_axis_label='Verspätungsprozentsatz',palette='Set2',fig_size_x=10,fig_size_y=6):""" Erstellt ein Balkendiagramm, das die Ausfälle pro Kalenderwoche zeigt. :param data: DataFrame mit den Daten für das Diagramm (Spalten: 'X' und 'Y'). :param x_column: Name der Spalte für die x-Achse (z. B. Kalenderwoche). :param y_column: Name der Spalte für die y-Achse (z. B. Verspätungsprozentsatz). :param title: Titel des Diagramms. :param x_axis_label: Bezeichnung der x-Achse. :param y_axis_label: Bezeichnung der y-Achse. :param palette: Farbpalette für die Balken (Standard: 'Set2'). :param fig_size_x: Breite der Figur. :param fig_size_y: Höhe der Figur. """# Balkendiagramm erstellenplt.figure(figsize=(fig_size_x,fig_size_y))ax=sns.barplot(x=x_column,y=y_column,data=data,palette=palette)# Titel und Achsenbeschriftungen setzenplt.title(title)plt.xlabel(x_axis_label)plt.ylabel(y_axis_label)# Werte über den Balken anzeigenforbarinax.patches:yval=bar.get_height()ifyval>0:# Nur Werte > 0 anzeigenplt.text(bar.get_x()+bar.get_width()/2,yval+0.1,f"{yval:.2f}",# Anzeige des Wertsha='center',va='bottom' )# Layout anpassen und Diagramm anzeigenplt.tight_layout()plt.show()
Kapitel: Reorder-Funktionen
defreorder_calculate_percentage_delay_by_category_and_station_x(df,füller='-'):""" Diese Funktion nimmt ein DataFrame mit den Spalten 'Zugart', 'Station' und 'Y' und strukturiert es in eine Pivot-Tabelle um. Jede Zugart wird zur Zeile, jede Station zur Spalte, und der Wert 'Y' wird als Inhalt verwendet. Leere Zellen werden mit dem angegebenen Füllwert gefüllt. Args: df (pd.DataFrame): Das Eingabedaten-Frame mit den Spalten 'Zugart', 'Station' und 'Y'. füller: Der Wert, mit dem leere Zellen gefüllt werden sollen (Standard: '-'). Returns: pd.DataFrame: Das umstrukturierte DataFrame. """df=df.rename(columns={'X':'Zugart'})df=df.rename(columns={'station':'Banhöfe'})# Pivot-Tabelle erstellen, mit 'Zugart' als Index, 'Station' als Spalten und 'Y' als Wertepivot_df=df.pivot_table(index='Zugart',columns='Banhöfe',values='Y',aggfunc='first')# Leere Zellen mit dem Füllwert ersetzenpivot_df=pivot_df.fillna(füller)returnpivot_df
importpandasaspddefreorder_calculate_delay_statistics_by_train_type_and_station_x(df,fueller="-"):""" Diese Funktion nimmt ein DataFrame und wandelt es in eine Tabelle um, bei der 'X' (Zugart) als Zeilenüberschrift dient. Die Werte von 'station' werden zu den Spaltenüberschriften und die entsprechenden 'Y'-Werte werden in den Zellen abgebildet. Leere Zellen werden mit dem angegebenen Füllwert gefüllt (Standard ist "-"). Die Reihenfolge der 'X'-Spalte wird explizit auf '< 10 min', '< 30 min', '< 60 min', '< 120 min', '> 120 min' festgelegt. Args: df (pd.DataFrame): Das DataFrame mit den zu transformierenden Daten. fueller (str): Der Wert, der für leere Zellen verwendet wird (optional, Standard ist "-"). Returns: pd.DataFrame: Das umstrukturierte DataFrame. """df=df.rename(columns={'X':'Kategorie'})df=df.rename(columns={'station':'Banhöfe'})# Definiere eine benutzerdefinierte Reihenfolge für die 'X'-Spaltecategorie_order= ['< 10 min','< 30 min','< 60 min','< 120 min','> 120 min']# 'X'-Spalte in eine kategorielle Variable mit definierter Reihenfolge umwandelndf['Kategorie']=pd.Categorical(df['Kategorie'],categories=categorie_order,ordered=True)# Pivot-Tabelle erstellen, wobei 'X' (Zugart) zu den Zeilen und 'station' zu den Spalten wirddf_pivot=df.pivot_table(index='Kategorie',columns='Banhöfe',values='Y',aggfunc='first')# Alle leeren Zellen mit dem Füllwert ersetzendf_pivot=df_pivot.fillna(fueller)# Die Zeilen und Spalten sortierendf_pivot=df_pivot.sort_index(axis=1).sort_index(axis=0)returndf_pivot
defreorder_calculate_cancellations_by_operator_and_station(df,füller='-'):""" Diese Funktion nimmt ein DataFrame mit den Spalten 'Zugart', 'Station' und 'Y' und strukturiert es in eine Pivot-Tabelle um. Jede Zugart wird zur Zeile, jede Station zur Spalte, und der Wert 'Y' wird als Inhalt verwendet. Leere Zellen werden mit dem angegebenen Füllwert gefüllt. Args: df (pd.DataFrame): Das Eingabedaten-Frame mit den Spalten 'Zugart', 'Station' und 'Y'. füller: Der Wert, mit dem leere Zellen gefüllt werden sollen (Standard: '-'). Returns: pd.DataFrame: Das umstrukturierte DataFrame. """df=df.rename(columns={'X':'Betreiber'})df=df.rename(columns={'station':'Banhöfe'})# Pivot-Tabelle erstellen, mit 'Zugart' als Index, 'Station' als Spalten und 'Y' als Wertepivot_df=df.pivot_table(index='Betreiber',columns='Banhöfe',values='Y',aggfunc='first')# Leere Zellen mit dem Füllwert ersetzenpivot_df=pivot_df.fillna(füller)returnpivot_df
defreorder_calculate_delay_by_line_and_day(df,filler='-'):""" Diese Funktion nimmt ein DataFrame mit Bahnhöfen, Wochentagen und Werten, und strukturiert es so um, dass die Bahnhöfe als Spaltenüberschriften und die Wochentage als Zeilenüberschriften erscheinen. Args: df (DataFrame): Das Eingabedatenframe mit den Spalten 'X', 'day_of_week' und 'Y'. filler: Der Wert, der in leere Zellen (NaN-Werte) eingefügt werden soll. Standardwert ist '-'. Returns: DataFrame: Ein umstrukturiertes DataFrame mit Füllwerten. """# Pivot das DataFrame, so dass die Wochentage in den Zeilen und die Bahnhöfe in den Spalten erscheinendf_umstrukturiert=df.pivot(index='day_of_week',columns='X',values='Y')# Optional: Die Spaltenüberschriften umbenennen, falls erforderlichdf_umstrukturiert.columns.name=None# Entfernt den Namen der Spalte (Bahnhöfe)# Leere Zellen (NaN) mit dem Füllwert ersetzendf_umstrukturiert=df_umstrukturiert.fillna(filler)# Das Ergebnis zurückgebenreturndf_umstrukturiert
defreorder_calculate_cancellation_statistics_by_train_type_and_station(df):# Definieren der gewünschten Reihenfolge der Kategoriencategories_order= ['> 120 min vorher','> 60 min vorher','> 30 min vorher','> 0 min vorher','< 30 min nachher','< 60 min nachher','> 60 min nachher']# Umwandeln der 'X'-Spalte in einen Categorical-Typ mit der definierten Reihenfolgedf['X']=pd.Categorical(df['X'],categories=categories_order,ordered=True)# Pivotieren der Tabelle, um die Stationen als Spalten und Kategorien als Zeilen zu erhaltendf_pivot=df.pivot_table(index='X',columns='station',values='Y',aggfunc='first')# Ersetzen von NaN-Werten mit "-"df_pivot=df_pivot.fillna("-")# Zurücksetzen des Indexdf_pivot.reset_index(inplace=True)returndf_pivot
Kapitel: CSV Erstellung
defcreate_csv(df,dateiname):""" Diese Funktion nimmt ein DataFrame und speichert es als CSV-Datei mit Semikolon als Separator und Komma als Dezimaltrennzeichen. Args: df (pd.DataFrame): Das DataFrame mit den zu exportierenden Daten. dateiname (str): Der Name der CSV-Datei, die erstellt werden soll (inkl. Pfad). """# Zahlen mit Komma als Dezimaltrennzeichen und Semikolon als Separator speicherndf.to_csv(dateiname,sep=";",index=True,decimal=",")
defersetze_char_in_csv(dateipfad,alter_char,neuer_char):""" Diese Funktion ersetzt einen bestimmten Charakter in einer CSV-Datei durch einen anderen. Args: dateipfad (str): Der Pfad zur CSV-Datei. alter_char (str): Der zu ersetzende Charakter. neuer_char (str): Der Charakter, durch den der alte ersetzt werden soll. """# CSV-Datei einlesendf=pd.read_csv(dateipfad,sep=';',engine='python')# Durch alle Zellen im DataFrame iterieren und den Charakter ersetzendf=df.applymap(lambdax:str(x).replace(alter_char,neuer_char)ifisinstance(x,str)elsex)# CSV-Datei mit den ersetzten Werten speicherndf.to_csv(dateipfad,sep=';',index=False,decimal=",")
Kapitel: Datenauswahl
# 1. CSV-Daten ladenarrival_data=load_csv_as_data_x('db-data/ar_superquery.csv')depature_data=load_csv_as_data_x('db-data/dp_superquery.csv')# 2. Daten filtern (optional)arrival_filtered_data=filter_data_x(data=arrival_data,evas=None,# Bahnhof nach EVA-Nummer (Optional, None wenn nicht benötigt, z.B. 8000244, 8000250 oder 8000134)date=None,# Datum im Format 'YYYY-MM-DD' (Optional, None wenn nicht benötigt)week_number=None# Kalenderwoche (Optional, None wenn nicht benötigt))depature_filtered_data=filter_data_x(data=depature_data,evas=None,# Bahnhof nach EVA-Nummer (Optional, None wenn nicht benötigt, z.B. 8000244, 8000250 oder 8000134)date=None,# Datum im Format 'YYYY-MM-DD' (Optional, None wenn nicht benötigt)week_number=None# Kalenderwoche (Optional, None wenn nicht benötigt))
Kapitel: Visualisierung
# 3. Durchschnittliche Verspätung in Minuten nach Zugart und Bahnhof berechnenaverage_min_delay_data=calculate_average_min_delay_by_category_and_station_x(data=arrival_filtered_data,delay_threshold=5,# Schwellenwert in Minutencategories=None,combine_all_categories=False)#print(average_min_delay_data)#format_and_save_to_csv(average_min_delay_data, ["Zugart", "Bahnhof", "Durchschnittliche Verspätung (in min)"], "Arrival_average_min_delay_by_category_and_station.csv")
# 3. arrival_percentage_delay_by_categorie_more_than_5.pngpercentage_delay_data=calculate_percentage_delay_by_category_and_station_x(data=arrival_filtered_data,delay_threshold=5,# Schwellenwert in Minutencategories=["IC","ICE","RB","RE","S","TGV","VIA","HLB","N","NJ","FLX","ECE"],combine_all_categories=False)plot_bar_chart_by_category_and_station_x(data=percentage_delay_data,title='Prozentuale Verspätung ankommender Züge je Zugart pro Bahnhof (mehr als 5 Minuten)',x_axis_label='Zugart',y_axis_label='Anteil verpäteter Züge (%)',fig_size_x=12,fig_size_y=6,int_value=True,# Bei Prozentualen Ausfällen muss es hier "False" sein )#create_csv_calculate_percentage_delay_by_category_and_station_x(percentage_delay_data, "X.csv")reorder_percentage_delay_data=reorder_calculate_percentage_delay_by_category_and_station_x(percentage_delay_data,"-")print(reorder_percentage_delay_data)create_csv(reorder_percentage_delay_data,"out/01_Prozentuale Verspätung ankommender Züge je Zugart pro Bahnhof.csv")ersetze_char_in_csv("out/01_Prozentuale Verspätung ankommender Züge je Zugart pro Bahnhof.csv",".",",")
# 3. depature_percentage_delay_by_categorie_more_than_5.pngpercentage_delay_data=calculate_percentage_delay_by_category_and_station_x(data=depature_filtered_data,delay_threshold=5,# Schwellenwert in Minutencategories=["IC","ICE","RB","RE","S","TGV","VIA","HLB","N","NJ","FLX","ECE"],combine_all_categories=False)plot_bar_chart_by_category_and_station_x(data=percentage_delay_data,title='Prozentuale Verspätung abfahrender Züge je Zugart pro Bahnhof (mehr als 5 Minuten)',x_axis_label='Zugart',y_axis_label='Anteil verpäteter Züge (%)',fig_size_x=12,fig_size_y=6,int_value=True,# Bei Prozentualen Ausfällen muss es hier "False" sein )#create_csv_calculate_percentage_delay_by_category_and_station_x(percentage_delay_data, "X.csv")reorder_percentage_delay_data=reorder_calculate_percentage_delay_by_category_and_station_x(percentage_delay_data,"-")print(reorder_percentage_delay_data)create_csv(reorder_percentage_delay_data,"out/02_Prozentuale Verspätung abfahrender Züge je Zugart pro Bahnhof.csv")ersetze_char_in_csv("out/02_Prozentuale Verspätung abfahrender Züge je Zugart pro Bahnhof.csv",".",",")
# 3. Flex Ticketpercentage_delay_data=calculate_percentage_delay_by_category_and_station_x(data=depature_filtered_data,delay_threshold=25,# Schwellenwert in Minutencategories=["IC","ICE","RB","RE","S","TGV","VIA","HLB","N","NJ","FLX","ECE"],combine_all_categories=False)plot_bar_chart_by_category_and_station_x(data=percentage_delay_data,title='Wahrscheinlichkeit zur Zugaufhebung',x_axis_label='Zugart',y_axis_label='Wahrscheinlichkeit (%)',fig_size_x=9,fig_size_y=4,int_value=True,# Bei Prozentualen Ausfällen muss es hier "False" sein )reorder_percentage_delay_data=reorder_calculate_percentage_delay_by_category_and_station_x(percentage_delay_data,"-")print(reorder_percentage_delay_data)create_csv(reorder_percentage_delay_data,"out/03_Wahrscheinlichkeit zur Zugaufhebung.csv")ersetze_char_in_csv("out/03_Wahrscheinlichkeit zur Zugaufhebung.csv",".",",")
# 3. Verspätungsstatistik für eine Zugart berechnenar_train_delay_data=calculate_delay_statistics_by_train_type_and_station_x(data=arrival_filtered_data,train_type=["IC","ICE","RB","RE","S","TGV","VIA","HLB","N","NJ","FLX","ECE"]# Zugart (optional))plot_bar_chart_by_category_and_station_x(data=ar_train_delay_data,title='Verspätungskategorien aller ankommenden Züge pro Bahnhof',x_axis_label='Verspätungskategorie',y_axis_label='Anteil verpäteter Züge (%)',fig_size_x=12,fig_size_y=4,int_value=False,# Bei Prozentualen Ausfällen muss es hier "False" sein )dp_train_delay_data=calculate_delay_statistics_by_train_type_and_station_x(data=depature_filtered_data,train_type=["IC","ICE","RB","RE","S","TGV","VIA","HLB","N","NJ","FLX","ECE"]# Zugart (optional))plot_bar_chart_by_category_and_station_x(data=dp_train_delay_data,title='Verspätungskategorien aller abfahrenden Züge pro Bahnhof',x_axis_label='Verspätungskategorie',y_axis_label='Anteil verpäteter Züge (%)',fig_size_x=12,fig_size_y=4,int_value=False,# Bei Prozentualen Ausfällen muss es hier "False" sein )ar_train_delay_data=reorder_calculate_delay_statistics_by_train_type_and_station_x(ar_train_delay_data,"-")dp_train_delay_data=reorder_calculate_delay_statistics_by_train_type_and_station_x(dp_train_delay_data,"-")create_csv(ar_train_delay_data,"out/04_Verspätungskategorien aller ankommenden Züge pro Bahnhof.csv")ersetze_char_in_csv("out/04_Verspätungskategorien aller ankommenden Züge pro Bahnhof.csv",".",",")create_csv(dp_train_delay_data,"out/05_Verspätungskategorien aller abfahrenden Züge pro Bahnhof.csv")ersetze_char_in_csv("out/05_Verspätungskategorien aller abfahrenden Züge pro Bahnhof.csv",".",",")
/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/1920162383.py:27: FutureWarning: The default value of observed=False is deprecated and will change to observed=True in a future version of pandas. Specify observed=False to silence this warning and retain the current behavior df_pivot = df.pivot_table(index='Kategorie', columns='Banhöfe', values='Y', aggfunc='first')/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/1920162383.py:27: FutureWarning: The default value of observed=False is deprecated and will change to observed=True in a future version of pandas. Specify observed=False to silence this warning and retain the current behavior df_pivot = df.pivot_table(index='Kategorie', columns='Banhöfe', values='Y', aggfunc='first')/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/3162237569.py:14: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead. df = df.applymap(lambda x: str(x).replace(alter_char, neuer_char) if isinstance(x, str) else x)/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/3162237569.py:14: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead. df = df.applymap(lambda x: str(x).replace(alter_char, neuer_char) if isinstance(x, str) else x)
# depature_cancellation_by_categorieresult=calculate_cancellations_by_train_type_and_station(data=depature_filtered_data,train_types=["IC","ICE","RB","RE","S","TGV","VIA","HLB","N","NJ","FLX","ECE"],calculate_percentage=True)plot_bar_chart_by_category_and_station_x(data=result,title='Anteil ausgefallener Züge (%)',x_axis_label='Zugart',y_axis_label='Ausfallanteil (%)',fig_size_x=10,fig_size_y=5,int_value=True,# Bei Prozentualen Ausfällen muss es hier "False" sein )result=reorder_calculate_percentage_delay_by_category_and_station_x(result,"-")create_csv(result,"out/06_Anteil ausgefallener Züge (%).csv")ersetze_char_in_csv("out/06_Anteil ausgefallener Züge (%).csv",".",",")
/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/3162237569.py:14: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead. df = df.applymap(lambda x: str(x).replace(alter_char, neuer_char) if isinstance(x, str) else x)
# arrival datacancellation_data_by_station=calculate_cancellations_by_operator_and_station(depature_filtered_data,operators=["DB Fernverkehr AG","DB Regio AG","Flixtrain","Hessische Landesbahn","VIAS GmbH"],# z. B. "DB Fernverkehr AG"calculate_percentage=True)plot_bar_chart_by_category_and_station_x(data=cancellation_data_by_station,title='Ausfälle pro Betreiber',x_axis_label='Betreiber',y_axis_label='Wahrscheinlichkeit eines Zugausfalls (%)',fig_size_x=8,fig_size_y=4,int_value=False,# Bei Prozentualen Ausfällen muss es hier "False" sein )cancellation_data_by_station=reorder_calculate_cancellations_by_operator_and_station(cancellation_data_by_station,"-")create_csv(cancellation_data_by_station,"out/07_Ausfälle pro Betreiber (%).csv")ersetze_char_in_csv("out/07_Ausfälle pro Betreiber (%).csv",".",",")
/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/3162237569.py:14: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead. df = df.applymap(lambda x: str(x).replace(alter_char, neuer_char) if isinstance(x, str) else x)
cancellation_data_by_operator_and_week=calculate_cancellations_by_operator_and_week(data=arrival_filtered_data,operator=None,calculate_percentage=False)cancellation_data_by_week=calculate_cancellations_by_week(data=arrival_filtered_data,calculate_percentage=False)plot_data=None# 4. Balkendiagramm erstellenifplot_dataisNone:print("")elifplot_dataisnotcancellation_data_by_week:plot_bar_chart_by_category_and_station_x(data=plot_data,title='Prozentuale Verspätung je Zugart pro Bahnhof (mehr als 5 Minuten)',x_axis_label='Zugart',y_axis_label='Anteil verpäteter Züge (%)',fig_size_x=12,fig_size_y=6,int_value=True,# Bei Prozentualen Ausfällen muss es hier "False" sein )elifplot_dataiscancellation_data_by_week:plot_cancellations_by_week(data=plot_data,title='Ausfälle je Kalenderwoche',x_axis_label='Kalenderwoche',y_axis_label='Anzahl Ausfälle',fig_size_x=6,fig_size_y=5 )
# wichtig: Depature Zügeline="RE1"delay_by_line_and_day=calculate_delay_by_line_and_day(data=depature_filtered_data,line=line,output_type="min",delay_threshold=0)plot_line_chart(delay_by_line_and_day,title="Durchschnittliche Verspätung des RE1 (in Minuten)",xlabel="Wochentag",ylabel="Minuten")delay_by_line_and_day=reorder_calculate_delay_by_line_and_day(delay_by_line_and_day)create_csv(delay_by_line_and_day,"out/08_Durchschnittliche Verspätung des RE1 (in Minuten).csv")ersetze_char_in_csv("out/08_Durchschnittliche Verspätung des RE1 (in Minuten).csv",".",",")
/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/3162237569.py:14: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead. df = df.applymap(lambda x: str(x).replace(alter_char, neuer_char) if isinstance(x, str) else x)
line="RE1"cancelldation_by_line_and_day=calculate_cancellations_by_line_and_day(depature_filtered_data,line,"absolute","day_of_week")#plot_line_chart(cancelldation_by_line_and_day, title="Ausfälle des RE1", xlabel="Wochentag", ylabel="Anzahl")
/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/3418611152.py:49: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning. cancelled_trains = cancelled_data.groupby(["station", group_by]).size().reset_index(name="cancelled_trains")
# depature_cancellation_time_by_categorie_short-distanceresult=calculate_cancellation_statistics_by_train_type_and_station(data=depature_filtered_data,train_types=["RB","RE","S","VIA","HLB","N"])plot_bar_chart_by_category_and_station_x(data=result,title='Zeitpunkt der Ausfallbekanntgabe für Nahverkehrszüge nach Kategorien',x_axis_label='Kategorie',y_axis_label='Ausfallanteil (%)',fig_size_x=13,fig_size_y=5,int_value=False,# Bei Prozentualen Ausfällen muss es hier "False" sein )# Tabelle sortierensorted_df=reorder_calculate_cancellation_statistics_by_train_type_and_station(result)create_csv(sorted_df,"out/09_Zeitpunkt der Ausfallbekanntgabe für Nahverkehrszüge.csv")ersetze_char_in_csv("out/09_Zeitpunkt der Ausfallbekanntgabe für Nahverkehrszüge.csv",".",",")
/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/1657369058.py:10: FutureWarning: The default value of observed=False is deprecated and will change to observed=True in a future version of pandas. Specify observed=False to silence this warning and retain the current behavior df_pivot = df.pivot_table(index='X', columns='station', values='Y', aggfunc='first')/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/3162237569.py:14: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead. df = df.applymap(lambda x: str(x).replace(alter_char, neuer_char) if isinstance(x, str) else x)
# depature_cancellation_time_by_categorie_long-distanceresult=calculate_cancellation_statistics_by_train_type_and_station(data=depature_filtered_data,train_types=["IC","ICE","TGV","NJ","FLX","ECE"])plot_bar_chart_by_category_and_station_x(data=result,title='Zeitpunkt der Ausfallbekanntgabe für Fernverkehrszüge nach Kategorien',x_axis_label='Kategorie',y_axis_label='Ausfallanteil (%)',fig_size_x=13,fig_size_y=5,int_value=False,# Bei Prozentualen Ausfällen muss es hier "False" sein )# Tabelle sortierensorted_df=reorder_calculate_cancellation_statistics_by_train_type_and_station(result)create_csv(sorted_df,"out/10_Zeitpunkt der Ausfallbekanntgabe für Fernverkehrszüge.csv")ersetze_char_in_csv("out/10_Zeitpunkt der Ausfallbekanntgabe für Fernverkehrszüge.csv",".",",")
/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/1657369058.py:10: FutureWarning: The default value of observed=False is deprecated and will change to observed=True in a future version of pandas. Specify observed=False to silence this warning and retain the current behavior df_pivot = df.pivot_table(index='X', columns='station', values='Y', aggfunc='first')/var/folders/43/l3hjvmnj5_7_dlqh7dw925s80000gn/T/ipykernel_24530/3162237569.py:14: FutureWarning: DataFrame.applymap has been deprecated. Use DataFrame.map instead. df = df.applymap(lambda x: str(x).replace(alter_char, neuer_char) if isinstance(x, str) else x)
# Directory containing the CSV filescsv_directory='./out'# Replace with your CSV directory pathoutput_excel_file='./out/output.xlsx'# Name of the output Excel file# Create a new Excel writer objectwithpd.ExcelWriter(output_excel_file,engine='openpyxl')aswriter:# Loop through all files in the directorydir=os.listdir(csv_directory)dir.sort()forfile_nameindir:# Check if the file is a CSViffile_name.endswith('.csv'):file_path=os.path.join(csv_directory,file_name)# Read the CSV file into a DataFramedf=pd.read_csv(file_path,sep=';',decimal=',')print(df)# Use the file name (without extension) as the sheet namesheet_name=file_name# Write the DataFrame to a new sheet in the Excel filedf.to_excel(writer,sheet_name=sheet_name,index=False)print(f"All CSV files have been combined into{output_excel_file}")
Zugart Frankfurt(Main)Hbf Mainz Hbf Mannheim Hbf Trier Hbf Wiesbaden Hbf0 ECE 25,0 37,5 20,0 - -1 FLX - 33,33 - - -2 HLB 44,37 81,81 - - 35,543 IC 57,69 52,94 33,33 - -4 ICE 48,74 55,46 31,71 - 36,085 N 49,29 44,96 65,88 - -6 NJ 71,42 60,0 66,66 - -7 RB 30,94 46,93 20,0 23,63 42,858 RE 48,73 53,49 32,64 28,52 -9 S 52,63 41,66 45,55 - 35,1810 TGV 50,0 - 37,03 - -11 VIA 48,78 - - - 43,58 Zugart Frankfurt(Main)Hbf Mainz Hbf Mannheim Hbf Trier Hbf Wiesbaden Hbf0 ECE 0,0 37,5 11,11 - -1 FLX - 0,0 - - -2 HLB 9,3 84,0 - - 27,393 IC 12,0 47,36 50,0 - -4 ICE 38,84 47,56 26,98 - 37,55 N 27,9 55,03 16,09 - -6 NJ 23,07 60,0 53,33 - -7 RB 16,41 29,95 0,0 17,46 0,08 RE 19,26 42,4 22,04 30,58 -9 S 32,35 32,04 26,75 - 24,0310 TGV 0,0 - 32,25 - -11 VIA 25,88 - - - 28,77 Zugart Frankfurt(Main)Hbf Mainz Hbf Mannheim Hbf Trier Hbf Wiesbaden Hbf0 ECE 0,0 0,0 0,0 - -1 FLX - 0,0 - - -2 HLB 0,0 12,0 - - 2,733 IC 2,0 36,84 0,0 - -4 ICE 12,75 15,46 7,31 - 8,535 N 1,16 12,08 0,0 - -6 NJ 15,38 50,0 26,66 - -7 RB 2,84 1,68 0,0 1,03 0,08 RE 3,19 9,8 2,44 3,8 -9 S 8,82 3,34 3,76 - 2,1410 TGV 0,0 - 6,45 - -11 VIA 3,19 - - - 4,91 Kategorie Frankfurt(Main)Hbf Mainz Hbf Mannheim Hbf Trier Hbf \0 < 10 min 52.9 63.1 70.1 78.8 1 < 30 min 33.3 26.8 22.9 17.7 2 < 60 min 9.6 8.2 5.3 3.0 3 < 120 min 3.5 1.6 1.4 0.6 4 > 120 min 0.7 0.3 0.2 0.0 Wiesbaden Hbf 0 70.5 1 24.9 2 3.8 3 0.7 4 0.2 Kategorie Frankfurt(Main)Hbf Mainz Hbf Mannheim Hbf Trier Hbf \0 < 10 min 73.6 68.1 77.5 79.1 1 < 30 min 18.1 22.8 16.3 18.2 2 < 60 min 5.8 7.6 4.5 2.6 3 < 120 min 2.1 1.3 1.4 0.1 4 > 120 min 0.3 0.2 0.2 0.0 Wiesbaden Hbf 0 74.7 1 19.8 2 4.3 3 0.9 4 0.3 Zugart Frankfurt(Main)Hbf Mainz Hbf Mannheim Hbf Trier Hbf Wiesbaden Hbf0 ECE 0,0 12,5 0,0 - -1 FLX - 0,0 - - -2 HLB 8,26 0,0 - - 4,13 IC 17,0 5,26 16,66 - -4 ICE 5,75 9,74 5,35 - 7,925 N 18,6 3,35 6,89 - -6 NJ 0,0 6,66 0,0 - -7 RB 16,19 9,28 25,0 3,53 0,08 RE 4,04 4,65 3,26 3,32 -9 S 35,29 5,8 3,64 - 12,8710 TGV 100,0 - 0,0 - -11 VIA 12,76 - - - 38,24 Betreiber Frankfurt(Main)Hbf Mainz Hbf Mannheim Hbf Trier Hbf \0 DB Fernverkehr AG 6,33 9,6 5,42 - 1 DB Regio AG 9,69 5,8 3,95 3,41 2 Flixtrain - 0,0 - - 3 Hessische Landesbahn 8,26 0,0 - - 4 VIAS GmbH 12,76 - - - Wiesbaden Hbf 0 7,92 1 12,82 2 - 3 4,1 4 38,24 day_of_week Mannheim Hbf Trier Hbf0 Monday 7.8 7.21 Tuesday 3.1 7.52 Wednesday 5.4 5.83 Thursday 4.6 6.34 Friday 3.0 8.75 Saturday 3.8 3.76 Sunday 6.4 12.3 Unnamed: 0 X Frankfurt(Main)Hbf Mainz Hbf Mannheim Hbf \0 0 > 120 min vorher 62.6 29.1 31.1 1 1 > 60 min vorher 6.4 8.9 24.4 2 2 > 30 min vorher 10.3 11.4 8.9 3 3 > 0 min vorher 12.8 25.3 22.2 4 4 < 30 min nachher 5.9 13.9 11.1 5 5 < 60 min nachher 1.5 8.9 2.2 6 6 > 60 min nachher 0.5 2.5 0.0 Trier Hbf Wiesbaden Hbf 0 39.5 71.7 1 7.9 4.8 2 10.5 5.5 3 21.1 13.1 4 18.4 3.4 5 0.0 1.4 6 2.6 0.0 Unnamed: 0 X Frankfurt(Main)Hbf Mainz Hbf Mannheim Hbf \0 0 > 120 min vorher 66.1 50.0 81.1 1 1 > 60 min vorher 13.7 21.0 3.8 2 2 > 30 min vorher 8.1 17.7 9.4 3 3 > 0 min vorher 6.5 4.8 0.0 4 4 < 30 min nachher 3.2 3.2 3.8 5 5 < 60 min nachher 1.6 1.6 1.9 6 6 > 60 min nachher 0.8 1.6 0.0 Wiesbaden Hbf 0 65.4 1 11.5 2 11.5 3 3.8 4 0.0 5 3.8 6 3.8 All CSV files have been combined into ./out/output.xlsx/Users/lucakrawczyk/GitHub-Repos/bahn-mining/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py:99: UserWarning: Title is more than 31 characters. Some applications may not be able to read the file warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")/Users/lucakrawczyk/GitHub-Repos/bahn-mining/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py:99: UserWarning: Title is more than 31 characters. Some applications may not be able to read the file warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")/Users/lucakrawczyk/GitHub-Repos/bahn-mining/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py:99: UserWarning: Title is more than 31 characters. Some applications may not be able to read the file warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")/Users/lucakrawczyk/GitHub-Repos/bahn-mining/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py:99: UserWarning: Title is more than 31 characters. Some applications may not be able to read the file warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")/Users/lucakrawczyk/GitHub-Repos/bahn-mining/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py:99: UserWarning: Title is more than 31 characters. Some applications may not be able to read the file warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")/Users/lucakrawczyk/GitHub-Repos/bahn-mining/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py:99: UserWarning: Title is more than 31 characters. Some applications may not be able to read the file warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")/Users/lucakrawczyk/GitHub-Repos/bahn-mining/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py:99: UserWarning: Title is more than 31 characters. Some applications may not be able to read the file warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")/Users/lucakrawczyk/GitHub-Repos/bahn-mining/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py:99: UserWarning: Title is more than 31 characters. Some applications may not be able to read the file warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")/Users/lucakrawczyk/GitHub-Repos/bahn-mining/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py:99: UserWarning: Title is more than 31 characters. Some applications may not be able to read the file warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")/Users/lucakrawczyk/GitHub-Repos/bahn-mining/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py:99: UserWarning: Title is more than 31 characters. Some applications may not be able to read the file warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")
About
Project to gather information and statistics about the Deutsche Bahn.