Temperaturstatistiken für Europa

Dieser Datensatz enthält Temperaturstatistiken für Europa, die sich auf die tägliche 2-Meter-Lufttemperatur (Mittelwert, Minimum und Maximum) für ganze Jahre sowie für die Winter- (DJF: Dezember-Januar-Februar) und die Sommersaison (JJA: Juni-Juli-August) konzentrieren. Die Statistiken, die aus den um Verzerrungen bereinigten EURO-CORDEX-Daten abgeleitet wurden, umfassen einen geglätteten 30-Jahres-Durchschnitt von 1971 bis 2100, was zu einer Zeitreihe von 1986 bis 2085 führt, einschließlich der Mittelwerte des Modellensembles und der zugehörigen Standardabweichungen. Diese Temperaturperzentile sind im öffentlichen Gesundheitswesen und in der Epidemiologie von entscheidender Bedeutung für die Bewertung von Gesundheitsrisiken und -auswirkungen und ermöglichen Vergleiche zwischen verschiedenen Regionen unter verschiedenen Klimawandelszenarien.

Schnellnavigation:

Information on Dataset:


Laden Sie bitte über den unten stehenden Link eine Kopie dieses Notebooks herunter, um es lokal auf Ihrem System auszuprobieren:

Öffnen Sie es nach dem Download in Jupyter Notebook und beginnen Sie mit der schrittweisen Ausführung des Codes.


1. Festlegen der Pfade und Arbeitsverzeichnisse

import os

''' ---- Verzeichnisse hier angeben ---- '''
download_folder = r".\data\sis-temperature-statistics\download"
working_folder = r".\data\sis-temperature-statistics\working"
geotiff_folder = r".\data\sis-temperature-statistics\geotiff"
csv_folder = r".\data\sis-temperature-statistics\csv"
output_folder = r".\data\sis-temperature-statistics\output"
''' ----- Ende der Angaben ---- '''

os.makedirs(download_folder, exist_ok=True)
os.makedirs(working_folder, exist_ok=True)
os.makedirs(geotiff_folder, exist_ok=True)
os.makedirs(csv_folder, exist_ok=True)
os.makedirs(output_folder, exist_ok=True)

2. Herunterladen und Entpacken des Datensatzes

2.1 Authentifizierung

import cdsapi

def main():
    # API-Key für die Authentifizierung
    api_key = "fdae60fd-35d4-436f-825c-c63fedab94a4"
    api_url = "https://cds.climate.copernicus.eu/api"

    # Erstellung des CDS-API-Clients
    client = cdsapi.Client(url=api_url, key=api_key)
    return client

2.2 Definieren Sie die „request“ und laden Sie den Datensatz herunter

Definieren Sie zusätzliche Anfragefelder, um sicherzustellen, dass die Anfrage innerhalb der Dateigrößenbeschränkung bleibt. Bei der Arbeit mit Geodaten oder APIs, die Karten- oder Satellitenbilder zurückgeben, kann die Begrenzung des geografischen Interessengebiets verhindern, dass Anfragen zu groß werden und die Datei- oder Verarbeitungsgrenzen überschreiten. Begrenzungsrahmen (Bounding Boxes) werden verwendet, um das geografische Gebiet für solche Anfragen festzulegen.

Die untenstehenden Koordinaten wurden mit dem Tool BBox Extractor ermittelt.

BBox Extractor ist ein webbasiertes Tool, das Benutzern hilft, interaktiv Begrenzungsrahmen-Koordinaten im WGS84-Format (Breite/Länge) auszuwählen und zu generieren. Dies ist besonders nützlich für APIs oder Datensätze, die eine Eingabe eines geografischen Gebiets erfordern

# Definieren der Begrenzungsrahmen-Koordinaten (WGS84-Format)
# Das Koordinatenformat lautet: [Norden, Westen, Süden, Osten]
bbox_wgs84_deutschland = [56.0, 5.8, 47.2, 15.0]
bbox_wgs84_konstanz = [47.9, 8.9, 47.6, 9.3]
# Alternativ können Sie ein Shapefile für eine präzise geografische Filterung verwenden
import geopandas as gpd
import math

# Beispiel: Shapefile von Konstanz laden (WGS84-Projektion)
de_shapefile = r"./shapefiles/de_boundary.shp"
de_gdf = gpd.read_file(de_shapefile)

# Extrahieren Sie den Begrenzungsrahmen des Shapefiles
de_bounds = de_gdf.total_bounds

# Passen Sie den Begrenzungsrahmen an und puffern Sie ihn, um einen etwas größeren
de_bounds_adjusted = [(math.floor(de_bounds[0]* 10)/10)-0.1,
                      (math.floor(de_bounds[1]* 10)/10)-0.1,
                      (math.ceil(de_bounds[2]* 10)/10)+0.1,
                      (math.ceil(de_bounds[3]* 10)/10)+0.1]

# Ordnen Sie die Koordinaten in das Format: [Nord, West, Süd, Ost] um.
bbox_de_bounds_adjusted = [de_bounds_adjusted[3], de_bounds_adjusted[0],
                           de_bounds_adjusted[1], de_bounds_adjusted[2]]
# Definieren Sie verfügbare Optionen für Zeiträume und Temperaturvariablen
period_list = ["year", "summer", "winter"]
variable_list = ["average_temperature", "minimum_temperature", "maximum_temperature"]

# Wählen Sie den Zeitraum und die Variable für die Verarbeitung aus; ändern Sie diese nach Bedarf
# Standardmäßig auf "Jahr" und "Durchschnittstemperatur" eingestellt
selected_period = period_list[0]  # Standard: "year"
selected_variable = variable_list[0]  # Standard: "average_temperature"

# Ausgewählte Variable und Periode anzeigen
print(f"Ausgewählte Variable: {selected_variable}\nAusgewählter Zeitraum: {selected_period}")

# Definieren Sie Statistiken basierend auf der ausgewählten Variable
# Fügen Sie für "average_temperature" zusätzliche Statistiken ein; verwenden Sie andernfalls nur "time_average"
statistic = (
    ['time_average', '10th_percentile', '90th_percentile']
    if selected_variable == "average_temperature"
    else ['time_average']
)
Ausgewählte Variable: average_temperature
Ausgewählter Zeitraum: year
# Definition des Datensatzes und der Request-Parameter
dataset = "sis-temperature-statistics"
request = {
    "variable": selected_variable,
    "period": selected_period,
    "statistic": statistic,
    "experiment": [
        "rcp4_5",
        "rcp8_5"
    ],
    "ensemble_statistic": [
        "ensemble_members_average",
        "ensemble_members_standard_deviation"
    ],
    "area": bbox_de_bounds_adjusted
}
# Führen Sie es aus, um den Datensatz herunterzuladen:
def main_retrieve():
    dataset_filename = f"{dataset}_{selected_period}_{selected_variable}.zip"
    dataset_filepath = os.path.join(download_folder, dataset_filename)
    
    # Den Datensatz nur herunterladen, wenn er noch nicht heruntergeladen wurde
    if not os.path.isfile(dataset_filepath):
        # Rufen Sie den CDS-Client nur auf, wenn der Datensatz noch nicht heruntergeladen wurde.
        client = main()
        # Den Datensatz mit den definierten Anforderungsparametern herunterladen
        client.retrieve(dataset, request, dataset_filepath)
    else:
        print("Datensatz bereits heruntergeladen.")
        
if __name__ == "__main__":
    main_retrieve()
Datensatz bereits heruntergeladen.

2.3 Extrahieren Sie die ZIP-Dateien in Ordner

import zipfile

# Definieren Sie den Ordner, in dem die extrahierten Dateien gespeichert werden
extract_folder = os.path.join(working_folder, selected_period)
os.makedirs(extract_folder, exist_ok=True)

# Entpacken der ZIP-Datei
try:
    if not os.listdir(extract_folder):
        for variable in variable_list:
            # Konstruieren Sie den Dateinamen für die ZIP-Datei basierend auf dem ausgewählten Zeitraum und der Variable
            dataset_filename = f"{dataset}_{selected_period}_{variable}.zip"
            dataset_filepath = os.path.join(download_folder, dataset_filename)
            
            # Versuchen Sie, die ZIP-Datei zu öffnen und zu extrahieren
            with zipfile.ZipFile(dataset_filepath, 'r') as zip_ref:
                zip_ref.extractall(extract_folder)
                print(f"Dateien erfolgreich extrahiert nach: {extract_folder}")
    else:
        print("Ordner ist nicht leer. Entpacken überspringen.")
except FileNotFoundError:
    print(f"Fehler: Die Datei {dataset_filepath} wurde nicht gefunden.")
except zipfile.BadZipFile:
    print(f"Fehler: Die Datei {dataset_filepath} ist keine gültige ZIP-Datei.")
except Exception as e:
    print(f"Ein unerwarteter Fehler ist aufgetreten: {e}")
Ordner ist nicht leer. Entpacken überspringen.

3. Untersuchen der Metadaten der NetCDF4-Datei

⚠️ Wichtig Nachdem Sie die Datensätze heruntergeladen haben, aktualisieren Sie selected_period im Codeblock unten, um mit der Analyse, Datenextraktion und Visualisierung fortzufahren. Dieser Schritt ist entscheidend, um mit der Analyse, Datenextraktion und Visualisierung fortzufahren.

# Legen Sie den ausgewählten Zeitraum für die Analyse fest (0: Jahr, 1: Sommer, 2: Winter)
selected_period = period_list[0]  # Ändern Sie diesen Index, um den Zeitraum zu ändern
print(f"Ausgewählter Zeitraum: {selected_period}")

# Definieren Sie den Pfad des extrahierten Ordners basierend auf dem ausgewählten Zeitraum
extract_folder = os.path.join(working_folder, selected_period)
os.makedirs(extract_folder, exist_ok=True)
Ausgewählter Zeitraum: year

3.1 Erstellen eines DataFrame mit verfügbaren NetCDF-Dateien

import re
import pandas as pd
import netCDF4 as nc
import numpy as np

def meta(filename):
    # Überprüfen, ob der Dateiname dem erwarteten Muster entspricht
    match = re.search(
        r"(?P<ds_statistic>mean|p10|p90)_(?P<ds_variable>Tmean|Tmax|Tmin)_(?P<ds_period>Yearly|Winter|Summer)_(?P<rcp>rcp\d{2})_(?P<rcp_statistic>mean|stdev)_v(?P<version>\d+\.\d+)\.",
        filename
    )
    
    # Fehler ausgeben, wenn der Dateiname nicht dem erwarteten Schema entspricht
    if not match:
        raise ValueError("Der angegebene Dateiname entspricht nicht dem erwarteten Namensschema")
    
    # Funktion zum Extrahieren des Variablennamens aus der NetCDF-Datei
    filepath = os.path.join(extract_folder, filename)
    def get_nc_variable():
        with nc.Dataset(filepath, 'r') as nc_dataset:
            variable_names = list(nc_dataset.variables.keys())

            primary_variable_index = 0  # Diesen Index bei Bedarf anpassen
            if primary_variable_index >= len(variable_names):
                raise IndexError("Der Index der Hauptvariable liegt außerhalb des gültigen Bereichs für die NetCDF-Variablen.")
            
            primary_variable = variable_names[primary_variable_index]
            primary_variable_shape = np.shape(nc_dataset[primary_variable])

            return primary_variable, primary_variable_shape
    
    # Metadaten als Dictionary zurückgeben
    return dict(
        filename=filename,
        path=os.path.join(extract_folder, filename),
        ds_period=match.group('ds_period'),
        ds_variable=match.group('ds_variable'),
        ds_statistic=match.group('ds_statistic'),
        variable_name=get_nc_variable()[0],
        variable_shape=get_nc_variable()[1],
        rcp=match.group('rcp'),
        rcp_statistic=match.group('rcp_statistic'),
    )

# Metadaten für alle NetCDF-Dateien im Ordner extrahieren
# Das Dictionary 'nc_files' enthält alle relevanten Metadaten der verfügbaren NetCDF4-Dateien
# Dieses Dictionary wird später verwendet, um die Dateien in GeoTiff zu konvertieren
nc_files = [meta(f) for f in os.listdir(extract_folder) if f.endswith('.nc')]
nc_files = sorted(nc_files, key=lambda x: (x['ds_variable'], x['ds_statistic']))

df_nc_files = pd.DataFrame.from_dict(nc_files)

# Pandas-Anzeigeoptionen anpassen
pd.options.display.max_colwidth = 30

# DataFrame anzeigen, ohne die Spalte 'path' darzustellen
df_nc_files.loc[:, df_nc_files.columns != 'path']
filename ds_period ds_variable ds_statistic variable_name variable_shape rcp rcp_statistic
0 mean_Tmax_Yearly_rcp45_mea... Yearly Tmax mean mean_Tmax_Yearly (100, 82, 95) rcp45 mean
1 mean_Tmax_Yearly_rcp45_std... Yearly Tmax mean mean_Tmax_Yearly (100, 82, 95) rcp45 stdev
2 mean_Tmax_Yearly_rcp85_mea... Yearly Tmax mean mean_Tmax_Yearly (100, 82, 95) rcp85 mean
3 mean_Tmax_Yearly_rcp85_std... Yearly Tmax mean mean_Tmax_Yearly (100, 82, 95) rcp85 stdev
4 mean_Tmean_Yearly_rcp45_me... Yearly Tmean mean mean_Tmean_Yearly (100, 82, 95) rcp45 mean
5 mean_Tmean_Yearly_rcp45_st... Yearly Tmean mean mean_Tmean_Yearly (100, 82, 95) rcp45 stdev
6 mean_Tmean_Yearly_rcp85_me... Yearly Tmean mean mean_Tmean_Yearly (100, 82, 95) rcp85 mean
7 mean_Tmean_Yearly_rcp85_st... Yearly Tmean mean mean_Tmean_Yearly (100, 82, 95) rcp85 stdev
8 p10_Tmean_Yearly_rcp45_mea... Yearly Tmean p10 p10_Tmean_Yearly (100, 82, 95) rcp45 mean
9 p10_Tmean_Yearly_rcp45_std... Yearly Tmean p10 p10_Tmean_Yearly (100, 82, 95) rcp45 stdev
10 p10_Tmean_Yearly_rcp85_mea... Yearly Tmean p10 p10_Tmean_Yearly (100, 82, 95) rcp85 mean
11 p10_Tmean_Yearly_rcp85_std... Yearly Tmean p10 p10_Tmean_Yearly (100, 82, 95) rcp85 stdev
12 p90_Tmean_Yearly_rcp45_mea... Yearly Tmean p90 p90_Tmean_Yearly (100, 82, 95) rcp45 mean
13 p90_Tmean_Yearly_rcp45_std... Yearly Tmean p90 p90_Tmean_Yearly (100, 82, 95) rcp45 stdev
14 p90_Tmean_Yearly_rcp85_mea... Yearly Tmean p90 p90_Tmean_Yearly (100, 82, 95) rcp85 mean
15 p90_Tmean_Yearly_rcp85_std... Yearly Tmean p90 p90_Tmean_Yearly (100, 82, 95) rcp85 stdev
16 mean_Tmin_Yearly_rcp45_mea... Yearly Tmin mean mean_Tmin_Yearly (100, 82, 95) rcp45 mean
17 mean_Tmin_Yearly_rcp45_std... Yearly Tmin mean mean_Tmin_Yearly (100, 82, 95) rcp45 stdev
18 mean_Tmin_Yearly_rcp85_mea... Yearly Tmin mean mean_Tmin_Yearly (100, 82, 95) rcp85 mean
19 mean_Tmin_Yearly_rcp85_std... Yearly Tmin mean mean_Tmin_Yearly (100, 82, 95) rcp85 stdev

3.2 Übersicht über die Struktur der heruntergeladenen Daten

Das folgende Diagramm veranschaulicht die Struktur der SIS-Temperaturstatistik-Daten, die in vier hierarchische Ebenen unterteilt sind:

  1. Zeitraum (Period): Gibt die Zeiteinheit der Daten an, die eine der folgenden sein kann:

    • Jahr

    • Sommer

    • Winter

  2. Variable: Die Daten enthalten drei Temperaturvariablen:

    • Durchschnittstemperatur

    • Mindesttemperatur

    • Maximaltemperatur

  3. Statistik (Statistic): Für jede Variable werden folgende Statistiken berechnet:

    • Zeitlicher Durchschnitt

    • 10. Perzentil (nur für Durchschnittstemperatur)

    • 90. Perzentil (nur für Durchschnittstemperatur)

  4. Experiment: Die Daten werden unter zwei Klimaszenarien bereitgestellt:

    • rcp45

    • rcp85

    • Für jedes Szenario liegen die Daten in zwei Formen vor:

      • rcp45_mean, rcp45_std

      • rcp85_mean, rcp85_std

Die Datenstruktur folgt einer hierarchischen Abfolge von Zeitraum (Period) zu Variable, dann zu Statistik (Statistic) und schließlich zu Experiment. Diese Hierarchie zeigt, wie die Temperaturdaten organisiert, berechnet und analysiert werden.

3.3 Einzigartige Variablennamen und verfügbare Variablen ausgeben

# Variable definieren, um bereits verarbeitete Variablennamen zu speichern und Duplikate zu vermeiden  
seen_variables = set()  

# Alle Variablen in jeder NetCDF-Datei auflisten  
for i, nc_file in enumerate(nc_files):  
    variable_name = nc_file['variable_name']  
    
    # Überspringen, wenn die Variable bereits verarbeitet wurde  
    if variable_name in seen_variables:  
        continue  

    # NetCDF-Datei im Lesemodus öffnen  
    with nc.Dataset(nc_file['path'], mode='r') as nc_dataset:  
        # Alle Variablen im aktuellen Datensatz auflisten  
        variables_list = list(nc_dataset.variables.keys())  
        
        # Details der Datei und ihrer Variablen ausgeben  
        print(f"{i + 1:<2} {variable_name:<18}: Verfügbare Variablen: {variables_list}")  
    
    # Diese Variable als verarbeitet markieren  
    seen_variables.add(variable_name)  

# Variable definieren, um bereits verarbeitete Variablennamen zu speichern und Duplikate zu vermeiden  
seen_variables = set()  
1  mean_Tmax_Yearly  : Verfügbare Variablen: ['mean_Tmax_Yearly', 'height', 'lat', 'lon', 'time']
5  mean_Tmean_Yearly : Verfügbare Variablen: ['mean_Tmean_Yearly', 'height', 'lat', 'lon', 'time']
9  p10_Tmean_Yearly  : Verfügbare Variablen: ['p10_Tmean_Yearly', 'quantile', 'lat', 'lon', 'time']
13 p90_Tmean_Yearly  : Verfügbare Variablen: ['p90_Tmean_Yearly', 'quantile', 'lat', 'lon', 'time']
17 mean_Tmin_Yearly  : Verfügbare Variablen: ['mean_Tmin_Yearly', 'height', 'lat', 'lon', 'time']
# Alle Variableninformationen in jeder NetCDF-Datei auflisten  
for i, nc_file in enumerate(nc_files):  
    variable_name = nc_file['variable_name']  
    
    # Überspringen, wenn die Variable bereits verarbeitet wurde  
    if variable_name in seen_variables:  
        continue  
    
    # NetCDF-Datei im Lesemodus öffnen  
    with nc.Dataset(nc_file['path'], mode='r') as nc_dataset:  
        # Primärvariable-Daten abrufen  
        variable_data = nc_dataset[variable_name]  
        
        # Zusammenfassung der Primärvariable erstellen  
        summary = {  
            "Variablenname": variable_name,  
            "Datentyp": variable_data.dtype,  
            "Form": variable_data.shape,  
            "Variableninfo": f"{variable_data.dimensions}",  
            "Einheiten": getattr(variable_data, "units", "N/A"),  
            "Langer Name": getattr(variable_data, "long_name", "N/A"),  
        }  
        
        # Datensatz-Zusammenfassung als DataFrame zur besseren Visualisierung anzeigen  
        nc_summary = pd.DataFrame(list(summary.items()), columns=['Beschreibung', 'Bemerkungen'])  
        print(f"{i + 1}. Zusammenfassung der Variable '{variable_name}':")  
        display(nc_summary)  
    
    # Variablenname zur Liste der bereits verarbeiteten Variablen hinzufügen  
    seen_variables.add(variable_name)  
    
    # Ausgabe begrenzen  
    output_limit = 2  
    if len(seen_variables) >= output_limit:  
        print(f".... (Ausgabe auf die ersten {output_limit} Variablen gekürzt)")  
        break  
1. Zusammenfassung der Variable 'mean_Tmax_Yearly':
Beschreibung Bemerkungen
0 Variablenname mean_Tmax_Yearly
1 Datentyp float32
2 Form (100, 82, 95)
3 Variableninfo ('time', 'lat', 'lon')
4 Einheiten degrees C
5 Langer Name Ensemble members average o...
5. Zusammenfassung der Variable 'mean_Tmean_Yearly':
Beschreibung Bemerkungen
0 Variablenname mean_Tmean_Yearly
1 Datentyp float32
2 Form (100, 82, 95)
3 Variableninfo ('time', 'lat', 'lon')
4 Einheiten degrees C
5 Langer Name Ensemble members average o...
.... (Ausgabe auf die ersten 2 Variablen gekürzt)

4. Exportieren der NetCDF4-Dateien im CSV-Format

4.1 Definieren eine Funktion zum Konvertieren von NetCDF-Daten in einen Pandas-DataFrame

import xarray as xr

# Funktion zur Konvertierung von NetCDF-Daten in ein Pandas DataFrame
def netcdf_to_dataframe(nc_file, bounding_box=None):

    with xr.open_dataset(nc_file['path']) as nc_dataset:
        # Zugriff auf die Variablendaten aus dem Datensatz
        variable_data = nc_dataset[nc_file['variable_name']]
        
        # Sicherstellen, dass die Namen für Breiten- und Längengrad korrekt sind
        latitude_name = 'latitude' if 'latitude' in nc_dataset.coords else 'lat'
        longitude_name = 'longitude' if 'longitude' in nc_dataset.coords else 'lon'
        
        # Falls eine Begrenzungsbox angegeben ist, die Daten filtern
        if bounding_box:
            filtered_data = variable_data.where(
                (nc_dataset[latitude_name] >= bounding_box[1]) & (nc_dataset[latitude_name] <= bounding_box[3]) &
                (nc_dataset[longitude_name] >= bounding_box[0]) & (nc_dataset[longitude_name] <= bounding_box[2]),
                drop=True
            )
        else:
            filtered_data = variable_data

        # Umwandlung des xarray-Datensatzes in ein Pandas DataFrame
        df = filtered_data.to_dataframe().reset_index().set_index(['time', latitude_name, longitude_name])

        # Spaltennamen für die Werte umbenennen (optional)
        variable_column_name = f"{nc_file['ds_period']}_{nc_file['ds_variable']}_{nc_file['ds_statistic']}_{nc_file['rcp']}_{nc_file['rcp_statistic']}"
        df.rename(columns={nc_file['variable_name']: variable_column_name}, inplace=True)

        # Entfernen nicht benötigter Spalten (variiert je nach Datensatz)
        if 'height' in df.columns:
            df = df.drop(columns=['height'])
        elif 'quantile' in df.columns:
            df = df.drop(columns=['quantile'])

        return df

4.2 Erstellen ein zusammengeführtes DataFrame und exportieren als CSV

import textwrap
from tqdm import tqdm

# Definieren den CSV-Dateinamen und den Dateipfad für die Ausgabe
csv_filename = f"{dataset}-{nc_files[0]['ds_period']}.csv.zip"
csv_filepath = os.path.join(csv_folder, csv_filename)

# Exportieren das DataFrame als CSV, falls es noch nicht existiert
if not os.path.isfile(csv_filepath):
    dataframes = [netcdf_to_dataframe(nc_file) for nc_file in tqdm(nc_files)]
    df_merged = pd.concat(dataframes, axis=1).dropna().reset_index()
    df_merged.to_csv(csv_filepath, sep=",", encoding='utf8', compression='zip', index=False)
else:
    print(f"Datei existiert bereits unter {csv_filepath}.\nExport wird übersprungen.")
    print("Lesen bestehende CSV-Datei ein...")
    df_merged = pd.read_csv(csv_filepath)

def wrap_column_names(df, width):
    wrapped_columns = {col: " ".join(textwrap.wrap(col, width)) for col in df.columns}
    return df.rename(columns=wrapped_columns)

# Ändere die Pandas-Anzeigeoptionen
pd.options.display.max_colwidth = 20
pd.options.display.float_format = '{:,.2f}'.format

# Zeige das DataFrame an
df_wrapped = wrap_column_names(df_merged, width=11)
df_wrapped = df_wrapped.set_index(['time', 'lat', 'lon'])
df_wrapped
Datei existiert bereits unter .\data\sis-temperature-statistics\csv\sis-temperature-statistics-Yearly.csv.zip.
Export wird übersprungen.
Lesen bestehende CSV-Datei ein...
Yearly_Tmax _mean_rcp45 _mean Yearly_Tmax _mean_rcp45 _stdev Yearly_Tmax _mean_rcp85 _mean Yearly_Tmax _mean_rcp85 _stdev Yearly_Tmea n_mean_rcp4 5_mean Yearly_Tmea n_mean_rcp4 5_stdev Yearly_Tmea n_mean_rcp8 5_mean Yearly_Tmea n_mean_rcp8 5_stdev Yearly_Tmea n_p10_rcp45 _mean Yearly_Tmea n_p10_rcp45 _stdev Yearly_Tmea n_p10_rcp85 _mean Yearly_Tmea n_p10_rcp85 _stdev Yearly_Tmea n_p90_rcp45 _mean Yearly_Tmea n_p90_rcp45 _stdev Yearly_Tmea n_p90_rcp85 _mean Yearly_Tmea n_p90_rcp85 _stdev Yearly_Tmin _mean_rcp45 _mean Yearly_Tmin _mean_rcp45 _stdev Yearly_Tmin _mean_rcp85 _mean Yearly_Tmin _mean_rcp85 _stdev
time lat lon
1986-01-01 47.10 5.70 15.12 0.15 15.12 0.15 10.77 0.24 10.77 0.22 1.01 0.26 1.01 0.24 20.24 0.34 20.24 0.32 6.60 0.39 6.60 0.37
5.80 14.91 0.11 14.91 0.10 10.57 0.23 10.57 0.22 0.82 0.25 0.82 0.24 20.07 0.33 20.07 0.32 6.42 0.41 6.42 0.40
5.90 14.44 0.14 14.44 0.13 10.20 0.16 10.20 0.15 0.55 0.21 0.55 0.20 19.66 0.26 19.66 0.25 6.14 0.31 6.14 0.29
6.00 13.97 0.21 13.97 0.20 9.74 0.22 9.74 0.21 0.19 0.27 0.19 0.26 19.15 0.25 19.15 0.24 5.69 0.33 5.69 0.31
6.10 13.42 0.22 13.42 0.21 9.23 0.21 9.23 0.20 -0.30 0.23 -0.30 0.23 18.67 0.27 18.67 0.25 5.20 0.38 5.20 0.37
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2085-01-01 55.20 14.70 13.95 0.47 15.22 0.37 9.91 0.55 11.21 0.44 0.22 0.90 1.68 0.81 19.61 0.53 20.84 0.42 6.01 0.88 7.35 0.79
14.80 14.12 0.47 15.39 0.35 9.74 0.54 11.06 0.44 -0.22 0.96 1.26 0.88 19.66 0.52 20.89 0.42 5.53 0.93 6.90 0.86
14.90 14.12 0.49 15.39 0.37 9.60 0.50 10.93 0.40 -0.46 0.86 1.03 0.78 19.61 0.52 20.86 0.45 5.25 0.80 6.65 0.73
15.00 14.06 0.46 15.33 0.33 9.67 0.49 10.99 0.39 -0.30 0.77 1.19 0.69 19.65 0.54 20.90 0.46 5.44 0.69 6.83 0.61
15.10 14.13 0.52 15.39 0.42 9.97 0.46 11.27 0.34 0.20 0.69 1.67 0.57 19.78 0.53 21.03 0.45 5.97 0.67 7.32 0.58

729100 rows × 20 columns

4.3 Nach Gebiet filtern, DataFrame erstellen und als zusammengeführte CSV-Datei exportieren

# Zusätzliche Anforderungsfelder definieren, um sicherzustellen, dass die Anfrage innerhalb der Dateigrößenbegrenzung bleibt.
# Diese Koordinaten wurden mit dem BBox Extractor Tool erhalten:
# https://str-ucture.github.io/bbox-extractor/

# Begrenzungsbox für die Region Konstanz (WGS84-Projektion):
bbox_wgs84_konstanz = [47.9, 8.9, 47.6, 9.3]  # Format: [Norden, Westen, Süden, Osten]
bbox_wgs84_konstanz_standard = [9.0, 47.6, 9.3, 47.8]  # Standardformat: [Westen, Süden, Osten, Norden]
# CSV-Dateiname und Pfad für die Ausgabe definieren
csv_filename = f"{dataset}-subset-{nc_files[0]['ds_period']}.csv.zip"
csv_filepath = os.path.join(csv_folder, csv_filename)

# Exportiere das DataFrame als CSV, falls es noch nicht existiert
if not os.path.isfile(csv_filepath):
    dataframes = [netcdf_to_dataframe(nc_file, bounding_box=bbox_wgs84_konstanz_standard) for nc_file in tqdm(nc_files)]
    df_merged_subset = pd.concat(dataframes, axis=1).dropna().reset_index()
    df_merged_subset.to_csv(csv_filepath, sep=",", encoding='utf8', compression='zip', index=False)
else:
    print(f"Datei existiert bereits unter {csv_filepath}.\nÜberspringen den Export.")
    print("Lesen bestehende CSV-Datei ein...")
    df_merged_subset = pd.read_csv(csv_filepath)

# Funktion zum Umbruch der Spaltennamen für bessere Lesbarkeit
def wrap_column_names(df, width):
    wrapped_columns = {col: " ".join(textwrap.wrap(col, width)) for col in df.columns}
    return df.rename(columns=wrapped_columns)

# Ändere die Pandas-Anzeigeoptionen
pd.options.display.max_colwidth = 20
pd.options.display.float_format = '{:,.2f}'.format

# Zeige das DataFrame an
df_wrapped_subset = wrap_column_names(df_merged_subset, width=11)
df_wrapped_subset = df_wrapped_subset.set_index(['time', 'lat', 'lon'])
df_wrapped_subset
Datei existiert bereits unter .\data\sis-temperature-statistics\csv\sis-temperature-statistics-subset-Yearly.csv.zip.
Überspringen den Export.
Lesen bestehende CSV-Datei ein...
Yearly_Tmax _mean_rcp45 _mean Yearly_Tmax _mean_rcp45 _stdev Yearly_Tmax _mean_rcp85 _mean Yearly_Tmax _mean_rcp85 _stdev Yearly_Tmea n_mean_rcp4 5_mean Yearly_Tmea n_mean_rcp4 5_stdev Yearly_Tmea n_mean_rcp8 5_mean Yearly_Tmea n_mean_rcp8 5_stdev Yearly_Tmea n_p10_rcp45 _mean Yearly_Tmea n_p10_rcp45 _stdev Yearly_Tmea n_p10_rcp85 _mean Yearly_Tmea n_p10_rcp85 _stdev Yearly_Tmea n_p90_rcp45 _mean Yearly_Tmea n_p90_rcp45 _stdev Yearly_Tmea n_p90_rcp85 _mean Yearly_Tmea n_p90_rcp85 _stdev Yearly_Tmin _mean_rcp45 _mean Yearly_Tmin _mean_rcp45 _stdev Yearly_Tmin _mean_rcp85 _mean Yearly_Tmin _mean_rcp85 _stdev
time lat lon
1986-01-01 47.60 9.00 14.33 0.37 14.33 0.33 9.97 0.20 9.97 0.17 -0.13 0.35 -0.13 0.34 20.15 0.31 20.15 0.27 5.74 0.21 5.74 0.21
9.10 14.45 0.36 14.45 0.32 10.09 0.19 10.09 0.16 -0.04 0.32 -0.04 0.30 20.32 0.32 20.32 0.27 5.86 0.20 5.86 0.19
9.20 14.47 0.29 14.47 0.25 10.18 0.20 10.18 0.18 0.11 0.32 0.11 0.31 20.35 0.27 20.35 0.22 5.98 0.24 5.98 0.24
47.70 9.00 14.46 0.25 14.46 0.23 10.04 0.13 10.04 0.13 -0.13 0.35 -0.13 0.33 20.28 0.24 20.28 0.21 5.75 0.17 5.75 0.18
9.10 14.61 0.28 14.61 0.25 10.16 0.14 10.16 0.12 -0.13 0.31 -0.13 0.29 20.52 0.29 20.52 0.24 5.85 0.17 5.85 0.16
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2085-01-01 47.70 9.10 16.74 0.45 18.44 0.59 12.29 0.33 13.99 0.43 2.18 0.41 3.83 0.47 22.78 0.62 24.90 1.03 8.02 0.24 9.71 0.28
9.20 16.79 0.44 18.49 0.57 12.38 0.33 14.08 0.43 2.24 0.39 3.91 0.44 22.89 0.64 25.00 1.05 8.14 0.25 9.84 0.29
47.80 9.00 15.87 0.41 17.58 0.54 11.78 0.43 13.49 0.47 1.97 0.41 3.62 0.43 21.86 0.62 23.96 0.97 7.83 0.52 9.54 0.51
9.10 16.07 0.51 17.78 0.58 11.78 0.40 13.48 0.43 1.85 0.42 3.50 0.48 22.08 0.57 24.17 0.90 7.65 0.36 9.35 0.36
9.20 16.04 0.53 17.74 0.58 11.72 0.38 13.41 0.42 1.78 0.44 3.44 0.51 22.05 0.52 24.12 0.83 7.55 0.29 9.25 0.30

900 rows × 20 columns

5. Exportieren der NetCDF4-Datei nach GeoTIFF

5.1 Definieren eine Funktion zum Exportieren der NetCDF4-Datei als GeoTIFF-Datei(en)

import numpy as np
from rasterio.transform import from_origin
import rasterio

from tqdm.notebook import tqdm

def main_export_geotiff(
    nc_file,
    bounding_box=None,
    start_year=None,
    end_year=None,
    merged=None,
    output_directory=None):
    
    """
    Parameter:
        nc_file (dict): Ein Dictionary mit den Schlüsseln 'path' (Dateipfad), 'variable', 'rcp' und 'statistic'.
        bounding_box (list): [lon_min, lat_min, lon_max, lat_max] (optional).
        start_year (int): Startjahr für das Dataset (optional).
        end_year (int): Endjahr für das Dataset (optional).
        merged (bool): Gibt an, ob ein zusammengeführtes GeoTIFF oder einzelne GeoTIFFs erstellt werden sollen (optional).
        output_directory (str): Verzeichnis zum Speichern der Ausgabe-GeoTIFF-Dateien (optional).
    """
    
    # Öffnet die NetCDF-Datei
    with nc.Dataset(nc_file['path'], 'r') as nc_dataset:
        lon = nc_dataset['lon'][:]
        lat = nc_dataset['lat'][:]
                    
        # Falls eine Begrenzungsbox angegeben wurde, filtere die Daten entsprechend
        if bounding_box:
            lon_min, lat_min, lon_max, lat_max = bounding_box
            
            indices_lat = np.where((lat >= lat_min) & (lat <= lat_max))[0]
            indices_lon = np.where((lon >= lon_min) & (lon <= lon_max))[0]
            start_lat, end_lat = indices_lat[0], indices_lat[-1] + 1
            start_lon, end_lon = indices_lon[0], indices_lon[-1] + 1
        else:
            start_lat, end_lat = 0, len(lat)
            start_lon, end_lon = 0, len(lon)
        
        lat = lat[start_lat:end_lat]
        lon = lon[start_lon:end_lon]
            
        # Extrahiere die Zeitvariable und konvertiere sie in lesbare Datumsangaben
        time_var = nc_dataset.variables['time']
        time_units = time_var.units
        time_calendar = getattr(time_var, "calendar", "standard")
        cftime = nc.num2date(time_var[:], units=time_units, calendar=time_calendar)
        
        # Berechnet die räumliche Auflösung und die Rastertransformation
        dx = abs(lon[1] - lon[0])
        dy = abs(lat[1] - lat[0])
        transform = from_origin(lon.min() - dx / 2, lat.min() - dy / 2, dx, -dy)
            
        # Bestimmt den verfügbaren Zeitraum
        min_year = cftime[0].year
        max_year = cftime[-1].year

        if start_year and end_year:
            # Passen Sie start_year und end_year basierend auf dem verfügbaren Zeitraum an
            if start_year < min_year:
                print(f"Das angegebene Startjahr {start_year} liegt vor dem Datensatzbereich.")
                print(f"Das Startjahr wird auf {min_year} angepasst.")
            if end_year > max_year:
                print(f"Das angegebene Endjahr {end_year} liegt nach dem Datensatzbereich.")
                print(f"Das Endjahr wird auf {max_year} angepasst.")
                
            start_year = max(start_year, min_year)
            end_year = min(end_year, max_year)

            dt_full_start = start_year
            dt_full_end = end_year
        else:
            # Standardmäßig wird der gesamte Datensatz verwendet
            dt_full_start = min_year
            dt_full_end = max_year
            start_year = min_year
            end_year = max_year
            
        # Findet die Indizes, die dem angegebenen Jahresbereich entsprechen
        start_index = next(i for i, dt in enumerate(cftime) if dt.year == start_year)
        end_index = next(i for i, dt in enumerate(cftime) if dt.year == end_year) + 1 
        
        # Extrahiere Temperatur-/Variablen-Daten
        variable_data = nc_dataset.variables[nc_file['variable_name']]
        variable_data_subset = variable_data[start_index:end_index,
                                            start_lat:end_lat,
                                            start_lon:end_lon]
        
        # Erstellt den Namen des Unterverzeichnisses basierend auf nc_file
        nc_filename = f"{nc_file['ds_statistic']}_{nc_file['ds_variable']}_{nc_file['ds_period']}_{nc_file['rcp']}_{nc_file['rcp_statistic']}"
        if f"{nc_file['ds_variable']}" == "Tmax":
            period_sub_directory = "Maximale Temperatur"
        elif f"{nc_file['ds_variable']}" == "Tmean":
            period_sub_directory = "Durchschnittstemperatur"
        elif f"{nc_file['ds_variable']}" == "Tmin":
            period_sub_directory = "Minimale Temperatur"
            
        if merged:
            # Erstellt ein zusammengeführtes GeoTIFF mit allen Zeitscheiben als separate Bänder
            if output_directory:
                save_directory_path = output_directory
            else:
                subset_directory_path = os.path.join(geotiff_folder, f"{nc_file['ds_period']}-zusammengeführt")
                os.makedirs(subset_directory_path, exist_ok=True)
                save_directory_path = os.path.join(subset_directory_path, period_sub_directory)
                os.makedirs(save_directory_path, exist_ok=True)

            output_filename = f"{nc_filename}-zusammengeführt-{dt_full_start}-{dt_full_end}.tif"
            output_filepath = os.path.join(save_directory_path, output_filename)

            # Erstellt eine GeoTIFF-Datei mit mehreren Bändern für jede Zeitscheibe
            with rasterio.open(
                output_filepath,
                "w",
                driver="GTiff",
                dtype=str(variable_data_subset.dtype),
                width=variable_data_subset.shape[2],
                height=variable_data_subset.shape[1],
                count=variable_data_subset.shape[0],
                crs="EPSG:4326",
                nodata=-9999,
                transform=transform,        
            ) as dst:
                for year_index in tqdm(range(variable_data_subset.shape[0]),
                                       desc=f"Exportiere zusammengeführte GeoTIFF-Datei von {start_year} bis {end_year}"):
                    band_data = variable_data_subset[year_index,:,:]
                    dt = cftime[start_index + year_index]
                    band_desc = f"{dt.year:04d}-{dt.month:02d}-{dt.day:02d}"
                    
                    # Schreibe jede Jahresscheibe als Band
                    dst.write(band_data, year_index + 1)
                    dst.set_band_description(year_index + 1, band_desc)
                    
        else:
            if output_directory:
                save_directory_path = output_directory
            else:
                subset_directory_path = os.path.join(geotiff_folder, f"{nc_file['ds_period']}-einzeln")
                os.makedirs(subset_directory_path, exist_ok=True)
                save_directory_path = os.path.join(subset_directory_path, period_sub_directory)
                os.makedirs(save_directory_path, exist_ok=True)
            
            for year_index in tqdm(range(variable_data_subset.shape[0]),
                                desc="Exportiere einzelne GeoTIFF-Dateien"):
                # Bestimmt das Datum für die aktuelle Zeitscheibe
                dt = cftime[start_index + year_index]
                dt_full = f"{dt.year:04d}-{dt.month:02d}-{dt.day:02d}"

                # Definiert den Speicherort der Ausgabe-GeoTIFF-Datei            
                output_filename = f"{nc_filename}_{dt_full}.tif"
                output_filepath = os.path.join(save_directory_path, output_filename)

                # Exportiert die aktuelle Zeitscheibe als GeoTIFF
                with rasterio.open(
                    output_filepath,
                    "w",
                    driver="GTiff",
                    dtype=str(variable_data_subset.dtype),
                    width=variable_data_subset.shape[2],
                    height=variable_data_subset.shape[1],
                    count=1,
                    crs="EPSG:4326",
                    nodata=-9999,
                    transform=transform,
                ) as dst:
                    year_precipitation_data = variable_data_subset[year_index, :, :]
                    dst.write(year_precipitation_data, 1)
                    dst.set_band_description(1, f"{dt.year:04d}-{dt.month:02d}-{dt.day:02d}")

Die Funktion main_export_geotiff kann eine einzelne NetCDF-Datei als Eingabe verarbeiten und sie als GeoTIFF exportieren, wobei verschiedene Anpassungsoptionen zur Verfügung stehen.

  • bounding_box (list): [lon_min, lat_min, lon_max, lat_max] (optional).

  • start_year (int): Startjahr für den Datensatz (optional).

  • end_year (int): Endjahr für den Datensatz (optional).

  • merged (bool): Legt fest, ob ein zusammengeführtes GeoTIFF oder einzelne GeoTIFFs erstellt werden sollen (optional).

  • output_directory (str): Verzeichnis zum Speichern der exportierten GeoTIFF-Dateien (optional).

Beispielanwendungen sind in der untenstehenden Zelle aufgeführt:

# 1. Anwendungsfall 1:
# main_export_geotiff(nc_file=nc_files[0],
#                     bounding_box = [9.0, 47.6, 9.3, 47.8],
#                     start_year=2000,
#                     end_year=2005,
#                     merged=True,
#                     output_directory=r"C:/Users/DELL/Desktop/Geotiff")

# Erklärung:
# Die Funktion exportiert die erste NetCDF-Datei aus dem nc_files-Dictionary.
# Die Begrenzungsbox (Bounding Box) ist auf die Region Konstanz festgelegt.
# Das Startjahr ist auf 2000 und das Endjahr auf 2005 gesetzt.
# Der Parameter merged ist auf True gesetzt, sodass die NetCDF-Datei nicht als einzelne GeoTIFF-Dateien exportiert wird.
# Hinweis: Jede NetCDF-Datei wird als separate GeoTIFF-Datei exportiert, wobei alle Bänder in einer einzigen Datei zusammengeführt werden.
# Das Ausgabeverzeichnis ist auf "C:/Users/DELL/Desktop/Geotiff" gesetzt.

# 2. Anwendungsfall 2:
# main_export_geotiff(nc_file=nc_files[0])

# Erklärung:
# Die Funktion exportiert die erste NetCDF-Datei aus dem nc_files-Dictionary.
# Keine Begrenzungsbox angegeben → der gesamte Datenbereich wird exportiert.
# Das Startjahr wird auf das erste verfügbare Jahr im Datensatz gesetzt.
# Das Endjahr wird auf das letzte verfügbare Jahr im Datensatz gesetzt.
# Der Parameter merged ist nicht explizit gesetzt, daher wird der Standardwert True verwendet → Es wird eine zusammengeführte GeoTIFF-Datei erstellt.
# Kein Ausgabeverzeichnis definiert → Die GeoTIFF-Dateien werden im Standardverzeichnis "geotiff_folder" gespeichert.

5.2 Ausgewählte NetCDF4-Datei(en) in GeoTIFF-Datei(en) exportieren

if __name__ == "__main__":
    # Exportiert alle NetCDF-Dateien aus nc_files als kombiniert GeoTIFF-Dateien (Fall: erste 5 Dateien)
    for nc_file in nc_files[:5]: # Entfernen Sie "[:5]", um alle Dateien zu exportieren
        main_export_geotiff(nc_file=nc_file,
                            bounding_box=None,
                            merged=True)
        
    # # Exportiert alle NetCDF-Dateien aus nc_files als einzelne GeoTIFF-Dateien,
    # # wobei jedes Band als separate GeoTIFF-Datei gespeichert wird
    # for nc_file in nc_files:
    #     main_export_geotiff(nc_file=nc_file,
    #                         merged=False)
if __name__ == "__main__":
    # Zusätzlicher Fall (Erweiterte Filterung)
    temp_folder = os.path.join(geotiff_folder, "_temp_folder")
    os.makedirs(temp_folder, exist_ok=True)
    
    main_export_geotiff(nc_file=nc_files[0],
                        bounding_box=bbox_wgs84_konstanz_standard,
                        start_year=2000,
                        end_year=2020,
                        merged=True,
                        output_directory=temp_folder)  # Zusammengeführt, nach Datum gefiltert und nach Gebiet gefiltert

6. Analyse und Visualisierung Optionen

6.1 Vorbereitung der Daten für die Visualisierung

# Daten für die Region Konstanz filtern (WGS84-Format)
lon_min, lat_min, lon_max, lat_max = bbox_wgs84_konstanz_standard

# DataFrame mit einer Abfrage filtern
filtered_df = (
    df_merged.query(
        "@lat_min <= lat <= @lat_max and @lon_min <= lon <= @lon_max"
    )
    .reset_index()
    .set_index("time")
)

# DataFrame anzeigen
df_wrapped = wrap_column_names(filtered_df, width=11)
df_wrapped.head()
index lat lon Yearly_Tmax _mean_rcp45 _mean Yearly_Tmax _mean_rcp45 _stdev Yearly_Tmax _mean_rcp85 _mean Yearly_Tmax _mean_rcp85 _stdev Yearly_Tmea n_mean_rcp4 5_mean Yearly_Tmea n_mean_rcp4 5_stdev Yearly_Tmea n_mean_rcp8 5_mean ... Yearly_Tmea n_p10_rcp85 _mean Yearly_Tmea n_p10_rcp85 _stdev Yearly_Tmea n_p90_rcp45 _mean Yearly_Tmea n_p90_rcp45 _stdev Yearly_Tmea n_p90_rcp85 _mean Yearly_Tmea n_p90_rcp85 _stdev Yearly_Tmin _mean_rcp45 _mean Yearly_Tmin _mean_rcp45 _stdev Yearly_Tmin _mean_rcp85 _mean Yearly_Tmin _mean_rcp85 _stdev
time
1986-01-01 508 47.60 9.00 14.33 0.37 14.33 0.33 9.97 0.20 9.97 ... -0.13 0.34 20.15 0.31 20.15 0.27 5.74 0.21 5.74 0.21
1986-01-01 509 47.60 9.10 14.45 0.36 14.45 0.32 10.09 0.19 10.09 ... -0.04 0.30 20.32 0.32 20.32 0.27 5.86 0.20 5.86 0.19
1986-01-01 510 47.60 9.20 14.47 0.29 14.47 0.25 10.18 0.20 10.18 ... 0.11 0.31 20.35 0.27 20.35 0.22 5.98 0.24 5.98 0.24
1986-01-01 603 47.70 9.00 14.46 0.25 14.46 0.23 10.04 0.13 10.04 ... -0.13 0.33 20.28 0.24 20.28 0.21 5.75 0.17 5.75 0.18
1986-01-01 604 47.70 9.10 14.61 0.28 14.61 0.25 10.16 0.14 10.16 ... -0.13 0.29 20.52 0.29 20.52 0.24 5.85 0.17 5.85 0.16

5 rows × 23 columns

# Gruppierung nach dem 'time'-Index und Berechnung des Durchschnitts für jede Gruppe
filtered_df_average = filtered_df.groupby(level='time').mean()
filtered_df_average = filtered_df_average.drop(columns=['lat', 'lon'])

# DataFrame anzeigen
df_wrapped = wrap_column_names(filtered_df_average, width=11)
df_wrapped.head()
index Yearly_Tmax _mean_rcp45 _mean Yearly_Tmax _mean_rcp45 _stdev Yearly_Tmax _mean_rcp85 _mean Yearly_Tmax _mean_rcp85 _stdev Yearly_Tmea n_mean_rcp4 5_mean Yearly_Tmea n_mean_rcp4 5_stdev Yearly_Tmea n_mean_rcp8 5_mean Yearly_Tmea n_mean_rcp8 5_stdev Yearly_Tmea n_p10_rcp45 _mean ... Yearly_Tmea n_p10_rcp85 _mean Yearly_Tmea n_p10_rcp85 _stdev Yearly_Tmea n_p90_rcp45 _mean Yearly_Tmea n_p90_rcp45 _stdev Yearly_Tmea n_p90_rcp85 _mean Yearly_Tmea n_p90_rcp85 _stdev Yearly_Tmin _mean_rcp45 _mean Yearly_Tmin _mean_rcp45 _stdev Yearly_Tmin _mean_rcp85 _mean Yearly_Tmin _mean_rcp85 _stdev
time
1986-01-01 604.00 14.28 0.32 14.28 0.29 9.95 0.20 9.95 0.19 -0.21 ... -0.21 0.31 20.16 0.32 20.16 0.28 5.73 0.25 5.73 0.24
1987-01-01 7,895.00 14.31 0.32 14.31 0.29 9.97 0.20 9.97 0.19 -0.14 ... -0.14 0.31 20.17 0.32 20.17 0.28 5.76 0.25 5.76 0.24
1988-01-01 15,186.00 14.31 0.32 14.31 0.29 9.98 0.20 9.98 0.19 -0.12 ... -0.12 0.31 20.21 0.32 20.21 0.28 5.78 0.25 5.78 0.24
1989-01-01 22,477.00 14.31 0.32 14.31 0.29 9.99 0.20 9.99 0.19 -0.12 ... -0.12 0.31 20.20 0.32 20.20 0.28 5.79 0.25 5.79 0.24
1990-01-01 29,768.00 14.35 0.32 14.35 0.29 10.02 0.20 10.02 0.19 -0.08 ... -0.08 0.31 20.23 0.32 20.23 0.28 5.82 0.25 5.82 0.24

5 rows × 21 columns

6.2 Definition von Funktionen zur Farbgestaltung der Diagramme

import colorsys

def darken_color(based_hex_color, i):
    """Verdunkelt eine Hex-Farbe um einen bestimmten Faktor (0 bis 1)."""
    factor = 1 - (i * 0.3)
    
    hex_color = based_hex_color.lstrip('#')  # Entfernt '#' falls vorhanden
    r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)
    
    # Jede Farbkomponente reduzieren, um die Farbe abzudunkeln (Faktor zwischen 0 und 1)
    r = int(r * factor)
    g = int(g * factor)
    b = int(b * factor)
    
    # Zurück in Hex umwandeln
    return f"#{r:02x}{g:02x}{b:02x}"

def darken_and_shift_hue(base_hex_color, i):
    """Verdunkelt eine Hex-Farbe und verändert ihren Farbton leicht."""

    factor = 1 - (i * 0.2)  # Verdunkelungsfaktor
    hue_shift = i * 0.05  # Farbtonverschiebung

    # Hex in RGB umwandeln
    hex_color = base_hex_color.lstrip('#')
    r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)

    # RGB (0-255) in HLS (Farbton, Helligkeit, Sättigung) umwandeln
    h, l, s = colorsys.rgb_to_hls(r / 255.0, g / 255.0, b / 255.0)

    # Helligkeit (Verdunkelung) anpassen und Farbton verschieben
    l = max(0, l * factor)  # Sicherstellen, dass die Helligkeit nicht negativ wird
    h = (h + hue_shift) % 1.0  # Farbton im gültigen Bereich halten

    # Zurück in RGB umwandeln
    r, g, b = colorsys.hls_to_rgb(h, l, s)
    r, g, b = int(r * 255), int(g * 255), int(b * 255)  # In 8-Bit-Integer-Format konvertieren

    return f"#{r:02x}{g:02x}{b:02x}"

# Basisfarben definieren  
base_line_color_1 = "#1f77b4"
base_fill_color_1 = '#aec7e8'

base_line_color_2 = '#ff7f0e'
base_fill_color_2 = '#ffbb78'

6.3 Definition einer Funktion zur Erstellung von Liniendiagrammen mit Fehlerbereich

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

def plot_with_shaded_area(ax, x, y_mean, y_stdev, line_color, fill_color, line_label, fill_label, marker_style):
    """Hilfsfunktion zum Plotten von Mittelwertlinien mit schattiertem Fehlerbereich."""
    ax.plot(x, y_mean, color=line_color, label=line_label, marker=marker_style, markevery=5, linestyle='--')
    ax.fill_between(x, y_mean - y_stdev, y_mean + y_stdev, color=fill_color, alpha=0.3, label=fill_label)

def plot_line_and_shade(filtered_df_average, variable_name_list):
    # Diagramm erstellen
    fig, ax = plt.subplots(figsize=(13, 7), facecolor='#f1f1f1', edgecolor='k')

    y_max_list = []
    y_min_list = []
    for i, variable_name in enumerate(variable_name_list):
        # Daten für RCP4.5 plotten
        plot_with_shaded_area(
            ax=ax,
            x=filtered_df_average.index,
            y_mean=filtered_df_average[f"{variable_name}_rcp45_mean"],
            y_stdev=filtered_df_average[f"{variable_name}_rcp45_stdev"],
            line_color=darken_and_shift_hue(base_line_color_1, i),
            fill_color=darken_and_shift_hue(base_fill_color_1, i),
            line_label=f"{variable_name}_rcp45_mean",
            fill_label=f"{variable_name}_rcp45_stdev",
            marker_style=None
        )

        # Daten für RCP8.5 plotten
        plot_with_shaded_area(
            ax=ax,
            x=filtered_df_average.index,
            y_mean=filtered_df_average[f"{variable_name}_rcp85_mean"],
            y_stdev=filtered_df_average[f"{variable_name}_rcp85_stdev"],
            line_color=darken_and_shift_hue(base_line_color_2, i),
            fill_color=darken_and_shift_hue(base_fill_color_2, i),
            line_label=f"{variable_name}_rcp85_mean",
            fill_label=f"{variable_name}_rcp85_stdev",
            marker_style=None
        )

        # Dynamische Anpassung der Y-Achse
        interval = 1
        rcp45_min = filtered_df_average[f"{variable_name}_rcp45_mean"].min() - \
                    filtered_df_average[f"{variable_name}_rcp45_stdev"].max()
        rcp85_min = filtered_df_average[f"{variable_name}_rcp85_mean"].min() - \
                    filtered_df_average[f"{variable_name}_rcp85_stdev"].max()
        y_min = min(rcp45_min, rcp85_min) - 0.5
        y_min = y_min // interval * interval
        rcp45_max = filtered_df_average[f"{variable_name}_rcp45_mean"].max() + \
                    filtered_df_average[f"{variable_name}_rcp45_stdev"].max()
        rcp85_max = filtered_df_average[f"{variable_name}_rcp85_mean"].max() + \
                    filtered_df_average[f"{variable_name}_rcp85_stdev"].max()
        y_max = max(rcp45_max, rcp85_max) + 0.5
        y_max = (y_max + interval) // interval * interval
        y_max_list.append(y_max)
        y_min_list.append(y_min)

    ax.set_ylim(min(y_min_list), max(y_max_list))
    
    # X-Achse für bessere Lesbarkeit anpassen
    ax.set_xlim(filtered_df_average.index.min(), filtered_df_average.index.max())
    ax.set_xticks(filtered_df_average.index[::5])
    tick_positions = filtered_df_average.index[::5]
    tick_labels = [str(pd.to_datetime(date).year) for date in tick_positions]
    ax.set_xticks(ticks=tick_positions, labels=tick_labels, rotation=0)
    
    # Gitterlinien hinzufügen
    ax.grid(visible=True, color='#b0b0b0', linestyle='--', linewidth=0.8, alpha=0.6)
    ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.2f'))

    # Achsenbeschriftungen und Titel anpassen
    ax.set_xlabel('Jahr', fontsize=14)
    ax.set_ylabel('Temperatur (°C)', fontsize=14, labelpad=10)
    ax.set_title(
        f"Durchschnittliche {variable_name.split('_')[0]}-Temperatur \n(Vergleich der Szenarien RCP 4.5 und RCP 8.5)",
        fontsize=14,
        fontweight='bold'
    )
    
    # Beschreibung und Quelle hinzufügen
    plt.figtext(
        0.5,
        -0.05,
        (
            'Beschreibung: Durchschnittliche Lufttemperatur pro Jahr, gültig in 2 m Höhe über der Oberfläche.\n'
            'Quelle: Hooyberghs, H., Berckmans, J., Lefebre, F., De Ridder, K. (2019):\n'
            'Temperaturstatistiken für Europa basierend auf Klimaprojektionen.\n'
            'Copernicus Climate Change Service (C3S) Climate Data Store (CDS).\n'
            'DOI: 10.24381/cds.8be2c014 (Zugriff am 14-10-2024)'
        ),
        ha='left',
        va='center',
        fontsize=9,
        wrap=True,
        backgroundcolor='w',
    )
    
    # Legende anpassen
    if len(variable_name_list) > 1:
        ax.legend(loc='center left', fontsize=9, frameon=True,
                  title='Szenario', title_fontsize=11,
                  bbox_to_anchor=(1.0, 0.5))
    else:
        ax.legend(loc='upper left', fontsize=10, frameon=True,
                  title='Szenario', title_fontsize=11,
                  bbox_to_anchor=(0.0, 1.00))
    
    # Layout anpassen und Diagramm anzeigen
    fig.tight_layout()
    plt.show()

6.4 Visualisierung des Liniendiagramms mit Fehler

if __name__ == "__main__":
    # Einzelne Datensatzvariable visualisieren
    plot_line_and_shade(filtered_df_average=filtered_df_average,
                         variable_name_list=[f"{nc_files[0]['ds_period']}_Tmax_mean"])
    plot_line_and_shade(filtered_df_average=filtered_df_average,
                         variable_name_list=[f"{nc_files[0]['ds_period']}_Tmean_mean"])
    plot_line_and_shade(filtered_df_average=filtered_df_average,
                         variable_name_list=[f"{nc_files[0]['ds_period']}_Tmin_mean"])

    # Mehrere Datensatzvariablen visualisieren
    plot_line_and_shade(filtered_df_average=filtered_df_average,
                         variable_name_list=[f"{nc_files[0]['ds_period']}_Tmax_mean",
                                             f"{nc_files[0]['ds_period']}_Tmean_mean",
                                             f"{nc_files[0]['ds_period']}_Tmin_mean"])
../../_images/4794f6bedc20040bf6f559f0273fc6b2b5041ee1cca04a2fa77977121fd51fab.png ../../_images/f7a0e6fb16f4542c7b2f1ae4a7eac2027b472786b609332d62509c90565ca030.png ../../_images/af4664d1c70333d54a3d8bdb0612895915b7318616a38361ca3dc7366b0cc485.png ../../_images/b6f5ce866e4c7470b5253313e9bd604ea36a876f85ef8e7811a3763165d63872.png