Arbeiten mit DataFrames: local global warming...

Die Säkularstation in Potsdam

Wir arbeiten in dieser Lektion mit einer legendären Zeitreihe, die direkt vor unserer Haustür liegt: Den Aufzeichnungen der sog. Säkularstation auf dem Telegraphenberg in Potsdam. Hier werden seit über 100 Jahren Klimabeobachtungen aufgezeichnet. Wir wollen uns auf die Beobachtungen der bodennahen Temperatur und der relativen Luftfeuchte beschränken.

Bei so einer langen Zeitreihe stellt sich gleich die Frage, ob wir den Effekt der lokalen Erwärmung sehen können...aber eins nach dem anderen.

Die Daten werden über das opendata-Portal des DWD bereitgestellt. Die Säkularstation hat die ID 03987. Die Zeitreihe findet Ihr unter ../data/saekularstation/produkt_tu_stunde_18930101_20201231_03987.txt als csv-Datei.

Aufgabe: Daten einlesen

Zum Einlesen dieser Daten nutzen wir das Paket pandas, das Ihr bereits kurz in Lektion 1 kennengelernt habt.

In [1]:
import pandas as pd

Genauer gesagt nutzen wir die Funktion pd.read_csv. Ruft doch einmal die Hilfe zu dieser Funktion auf.

  • Über welchen Parameter teilt Ihr der Funktion mit, welches Zeichen in der Datei als Spaltentrenner verwendet wird?
  • Und welches Zeichen wird nun in der Datei als Spaltentrenner verwendet?
  • Lest auf Grundlage dieser Erkenntnis die Datei als DataFrame mit Namen df ein.

Lösung

In [2]:
df = pd.read_csv("../data/saekularstation/produkt_tu_stunde_18930101_20201231_03987.txt", sep=";")

Aufgabe: Erkunden des Dataframes

Nutze die Funktionen, die du im ersten Teil kennengelernt hast, zum Erkunden des Dataframes.

  • Wieviele Zeilen und Spalten hat der Dataframe?
  • Welche Struktur hat der Dataframe? Was gibt es für Spalten, was bedeuten sie?
  • Lass Dir die ersten und letzten Zeilen des Dataframes sowie eine Zusammenfassung der Spalten anzeigen.

Lösung

In [3]:
# Zeilen, Spalten
df.shape
Out[3]:
(1122010, 6)
In [4]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1122010 entries, 0 to 1122009
Data columns (total 6 columns):
 #   Column       Non-Null Count    Dtype  
---  ------       --------------    -----  
 0   STATIONS_ID  1122010 non-null  int64  
 1   MESS_DATUM   1122010 non-null  int64  
 2   QN_9         1122010 non-null  int64  
 3   TT_TU        1122010 non-null  float64
 4   RF_TU        1122010 non-null  float64
 5   eor          1122010 non-null  object 
dtypes: float64(2), int64(3), object(1)
memory usage: 51.4+ MB
In [5]:
df.head()
Out[5]:
STATIONS_ID MESS_DATUM QN_9 TT_TU RF_TU eor
0 3987 1893010101 5 -12.3 84.0 eor
1 3987 1893010102 5 -10.9 84.0 eor
2 3987 1893010103 5 -9.9 84.0 eor
3 3987 1893010104 5 -9.4 84.0 eor
4 3987 1893010105 5 -8.9 84.0 eor
In [6]:
df.tail()
Out[6]:
STATIONS_ID MESS_DATUM QN_9 TT_TU RF_TU eor
1122005 3987 2020123119 3 1.3 82.0 eor
1122006 3987 2020123120 3 1.9 80.0 eor
1122007 3987 2020123121 3 1.4 84.0 eor
1122008 3987 2020123122 3 1.2 88.0 eor
1122009 3987 2020123123 3 1.4 90.0 eor
In [7]:
df.describe()
Out[7]:
STATIONS_ID MESS_DATUM QN_9 TT_TU RF_TU
count 1122010.0 1.122010e+06 1.122010e+06 1.122010e+06 1.122010e+06
mean 3987.0 1.956569e+09 5.014505e+00 8.873072e+00 7.868548e+01
std 0.0 3.694889e+07 1.783774e+00 8.368938e+00 1.817310e+01
min 3987.0 1.893010e+09 1.000000e+00 -2.680000e+01 7.000000e+00
25% 3987.0 1.925010e+09 5.000000e+00 2.600000e+00 6.700000e+01
50% 3987.0 1.957010e+09 5.000000e+00 8.800000e+00 8.400000e+01
75% 3987.0 1.989010e+09 5.000000e+00 1.490000e+01 9.400000e+01
max 3987.0 2.020123e+09 1.000000e+01 3.860000e+01 1.000000e+02

Datum und Zeit im DateFrame

Wir wollen unseren DataFrame zunächst ein wenig aufräumen: weniger sperrige Spaltennamen verwenden und Spalten rausschmeißen, die in unserem Kontext nicht brauchen.

In [8]:
# Etwas weniger sperrige Spaltennamen verwenden
df.columns = ['id', 'datetime', 'qn9', 'temp', 'rh', 'eor']
# Nur die Spalten mit Datum, Temperatur und relativer Luftfeuchte behalten 
df = df[["datetime", "temp", "rh"]]

Ein schneller Plot...

In [9]:
df.temp.plot()
Out[9]:
<AxesSubplot:>

Ein ganz schönes Gezappel, aber gut: Es sind ja auch über 100 Jahre stündlicher Daten! Was wir aber vermissen: die Zeitinformation auf der x-Achse. Dazu fehlen zwei Schritte:

  1. Zunächst müsen wir pandas mitteilen, dass es die Spalte datetime auch als Datum interpretieren soll. Bisher sind das einfach Strings, also Zeichenketten. Dazu nutzen wir die Funktion pd.to_datetime.

  2. Nun wollen wir, dass die Spalte datetime nicht nur irgendeine Spalte sei, sondern das primäre "Label" einer Zeile ...nämlich der index. Dazu nutzen wir set_index.

In [10]:
# datetime-Spalte auch als datetime interpretieren
df.datetime = pd.to_datetime(df.datetime, format="%Y%m%d%H")
# datetime als index verwenden
df = df.set_index("datetime")
In [11]:
df.temp.plot()
Out[11]:
<AxesSubplot:xlabel='datetime'>

Ja, so ist's besser. Wir schauen uns nochmal den "Kopf" des DataFrames an und sehen, dass die Spalte datetime jetzt als Index fungiert.

In [12]:
df.head()
Out[12]:
temp rh
datetime
1893-01-01 01:00:00 -12.3 84.0
1893-01-01 02:00:00 -10.9 84.0
1893-01-01 03:00:00 -9.9 84.0
1893-01-01 04:00:00 -9.4 84.0
1893-01-01 05:00:00 -8.9 84.0

Die Spalte datetime lässt sich nun auch nicht mehr über den Spaltennamen aufrufen...Ihr erhaltet einen AttributeError - aua.

In [14]:
df.datetime
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_13456/2199413736.py in <module>
----> 1 df.datetime

~/miniconda3/envs/umweltdv/lib/python3.7/site-packages/pandas/core/generic.py in __getattr__(self, name)
   5476         ):
   5477             return self[name]
-> 5478         return object.__getattribute__(self, name)
   5479 
   5480     def __setattr__(self, name: str, value) -> None:

AttributeError: 'DataFrame' object has no attribute 'datetime'

...sondern über das index-Attribut:

In [ ]:
df.index

Aufgabe

Nur zur Erinnerung an frühere Zeiten: versucht doch mal, den Datensatz in Excel oder OpenOffice Calc einzulesen und die obige Abbildung zu reproduzieren.

Lösung

Ich hab es mal in OpenOffice Calc probiert...mit folgendem Ergebnis: couldnotdoit.png

Jahresmittelwerte darstellen

Wie oben schon angesprochen, sieht man bei über 100 Jahren stündlicher Daten erstmal nicht so viel. Schauen wir uns doch mal die Zeitreihe der Jahresmittelwerte an. Die geht in pandas sehr komfortabel mit Hilfe der resample-Funktion.

resample

Kern der resample-Funktion ist das Zeitinterval, auf welchem die Mittelung stattfinden soll. Anschließend teilt man pandas mit, welche Funktion denn auf diesem Zeitintervall angewendet werden soll. Hier wäre das die Funktion mean. Das Ergebnis ist ein neuer DataFrame, den wir hier dfa nennen.

In [15]:
# Jahresmittelwerte ("1a")
dfa = df.resample("1a").mean()
In [16]:
dfa.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 128 entries, 1893-12-31 to 2020-12-31
Freq: A-DEC
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   temp    128 non-null    float64
 1   rh      128 non-null    float64
dtypes: float64(2)
memory usage: 3.0 KB
In [17]:
dfa.temp.plot()
Out[17]:
<AxesSubplot:xlabel='datetime'>

Abbildungen zurechtbasteln

Die obige Abbildung sieht ja schon ganz gut aus - v.a. beeindruckend, was das Erwärmungssignal angeht. Wir wollen aber ein paar Möglichkeiten kennenlernen, Abbildungenseigenschaften zu kontrollieren. Eine kleine Auswahl der Plot-Elemente gibt die folgende Abbildung:

Grundsätzlich ist das Arbeitspferd für Abbildungen in Python matplotlib. Auch die plot-Funktion, die an den DataFrames dranhängt, baut auf matplotlib auf. Wir können matplotlib mit pandas auf beliebige Weise kombinieren. Dazu importieren wir zunächst das pyplot-Modul aus matplotlib.

In [18]:
import matplotlib.pyplot as plt

Wir versuchen gar nicht erst, uns die unterschiedlichen Plot-Eigenschaften und Manipulationsmöglichkeiten systematisch zu erarbeiten. Also lieber ein Beispiel mit Erläuterungen im Code.

In [19]:
# Wir machen von Anfang an klar, dass wir größere Beschriftungen haben wollen
# Kleine Labels sind ein stetes Ärgernis in Abbildungen
plt.rcParams.update({'font.size': 16})

# Wir erzeugen einen Canvas, also eine Leinwand, auf die wir pinseln:
#   nrows: Zahl der Zeilen für subplots
#   ncols: Zahl der Spalten für subplots
#   figsize: Größe der gesamten Abbildung (Breite, Höhe)
fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(10,5))

# Wir benutzen nun die originäre plot-Funktion aus matplotlib
plt.plot(dfa.index, dfa.temp, "-", color="tab:blue")
# fügen ein Gitter hinzu
plt.grid()
# ein Label für die y-Achse (x-Achse ist selbsterklärend)
plt.ylabel("Temperatur (°C)")
# einen Titel
plt.title("Jahresmitteltemperatur, Säkularstation Potsdam")
# ein Label
plt.plot(pd.to_datetime("1941-01-01"), dfa.temp.min(), "o", color="purple", ms=15, mfc="None")
plt.text(pd.to_datetime("1943-12-01"), 6.5, "Kältestes Jahr (1941)", color="purple")
# Noch ein Label
plt.plot(pd.to_datetime("2019-12-31"), dfa.temp.max(), "o", color="tab:red", ms=15, mfc="None")
plt.text(pd.to_datetime("2018-01-01"), 11.2, "Wärmstes Jahr (2019)", 
         horizontalalignment="right", color="tab:red")
# Grenzen ziehen
plt.xlim(pd.to_datetime("1893-01-01"), pd.to_datetime("2021-01-01"))
# Eine vertikale Linie zeichnen
plt.axvline(pd.to_datetime("1990-10-03"), ls="--", color="grey")
plt.text(pd.to_datetime("1992-01-01"), 6.5, "Wieder-\nvereinigung", color="grey")
Out[19]:
Text(1992-01-01 00:00:00, 6.5, 'Wieder-\nvereinigung')

Zitat von Brad Salomon:

"[...] there’s no getting around the fact that matplotlib can be a technical, syntax-heavy library. Creating a production-ready chart sometimes requires a half hour of Googling and combining a hodgepodge of lines in order to fine-tune a plot. [...] However, understanding how matplotlib’s interfaces interact is an investment that can pay off down the road."

Die zehn wärmsten Jahre

Da wir nun schon einen DataFrame mit den Jahresmitteltemperaturen haben, können wir uns auch gleich die zehn wärmsten Jahre raussuchen. Wir nutzen dafür den Befehl sort_values. Der Parameter by bestimmt, nach welchem Merkmal sortiert wird, der Parameter ascending... - naja, is klar, oder? Das Ergebnis ist wiederum ein DataFrame, aus dem wir die ersten zehn Einträge auswählen.

In [20]:
# Die 10 wärmsten Jahre seit 1893
dfa.sort_values(by=["temp"], ascending=False)[0:10]
Out[20]:
temp rh
datetime
2019-12-31 11.308059 73.159932
2018-12-31 11.250479 72.051484
2020-12-31 11.102311 72.958561
2014-12-31 10.976701 79.818950
2015-12-31 10.715970 76.307078
2007-12-31 10.524235 80.299315
2000-12-31 10.404178 79.585383
1934-12-31 10.383288 76.701989
2016-12-31 10.309255 75.975296
2008-12-31 10.260713 79.343465

Tja, da hat sich tatsächlich ein Jahr reingeschlichen, das nicht im 21. Jahrhundert liegt. Jaja, das Jahr 1934 war aber auch wirklich ein warmes, ich weiß es noch genau.

Aufgabe: Winter is not coming

Wieviel Jahre aus dem 21. Jahrhundert gehören zu den zehn kältesten Jahren der Zeitreihe? Ihr dürft auch raten...nee, doch nicht.

Lösung

In [21]:
# Die 10 kältesten Jahre seit 1893
dfa.sort_values(by=["temp"], ascending=True)[0:10]
Out[21]:
temp rh
datetime
1940-12-31 6.540118 78.924863
1902-12-31 7.010308 81.697032
1941-12-31 7.116153 80.540753
1956-12-31 7.219570 79.580943
1996-12-31 7.290870 82.052026
1922-12-31 7.363037 78.350571
1987-12-31 7.422021 80.551370
1942-12-31 7.429852 78.048744
1962-12-31 7.573116 81.406050
1919-12-31 7.583699 79.656507

Antwort also: nicht ein einziges Jahr!

Mitteltemperatur für Klimanormalperioden

Der Bezugszeitraum für die Berechnung statistischer Kennwerte des Klimas ist die Klimanormalperiode. Die aktuelle Normalperiode ist 1991-2020. Man kommt bei Klimabeobachtungen nicht oft in den Genuss, vier Normalperioden miteinander vergleichen zu können. Bei der Säkularstation ist das möglich.

In pandas können wir mit Hilfe des datetime-index sehr komfortabel Zeiträume auswählen. pandas erkennt dabei normalerweise gut, was für Zeitelemente wir dabei meinen. Im folgenden Beispiel reicht die Angabe der Jahre:

In [22]:
# Mitteltemperatur in Potsdam für Normalperiode 1991-2020
df.loc["1991":"2020"].temp.mean()
Out[22]:
9.817713914164365

Aufgabe: Vergleich der Normalperioden und "Special Feature" zu String-Formatierung

Berechne die Mitteltemperatur für die Klimanormalperioden 1901-1930, 1931-1060, 1961-1990 und 1991-2020.

  • Welches war die wärmste Normalperiode?
  • Welches die kälteste?
  • Wie groß ist Differenz zwischen wärmster und kältester Normalperiode?
  • Wie groß ist die Differenz zwischen der aktuellen und der vergangenen Normalperiode?

Kleines Extra für Eure Lösung: string formatting. Lernt, wie Ihr Zahlen und Zeichenketten effizient zu Strings zusammensetzen könnt. Wie haben ja schon gelernt, dass man strings mit dem +-Zeichen verketten kann. Aber es geht noch besser. Am Besten am Beispiel:

In [23]:
# Strings einbetten
name = "Säkularstation"
print("Name der Station: %s" % name)
Name der Station: Säkularstation
In [24]:
# Floats einbetten, hier gerundet auf zwei Dezimalstellen
lat = 52.3813
lon = 3.0622
print("Standort der %s: %.2f geogr. Länge, %.2f geogr. Breite." % (name, lon, lat))
Standort der Säkularstation: 3.06 geogr. Länge, 52.38 geogr. Breite.
In [25]:
# Integer einbetten
print("Länge der Zeitreihe am Standort %s: %d Stunden" % (name, len(df)))
Länge der Zeitreihe am Standort Säkularstation: 1122010 Stunden
In [26]:
# Datumsformat einbetten
# (das werden wir später nochmal ausführlich machen)
print("Erste Stunde in der Zeitreihe am Standort %s: %s Uhr UTC" % (name, df.index[0].strftime("%d. %B %Y, %H:%M") ) )
Erste Stunde in der Zeitreihe am Standort Säkularstation: 01. January 1893, 01:00 Uhr UTC

Versucht nun also die Antworten auf die obigen Fragen mit Hilfe der String-Formatierung via print "auszudrucken".

Lösung

In [27]:
avgT1901 = df["1901":"1930"].temp.mean()
avgT1931 = df["1931":"1960"].temp.mean()
avgT1961 = df["1961":"1990"].temp.mean()
avgT1991 = df["1991":"2020"].temp.mean()

print("Mitteltemperaturen der Klimanormalperioden:")
print("   1901-1930: %.1f °C(kälteste)" % avgT1901)
print("   1931-1960: %.1f °C" % avgT1931)
print("   1961-1990: %.1f °C" % avgT1961)
print("   1991-2020: %.1f °C (wärmste)" % avgT1991)
print()
print("Differenzen zwischen den Mittelwerten:")
print("   Wärmste NP (1991-2020) - kälteste NP (1901-1930): %.1f °C" % (avgT1991-avgT1901))
print("   avgT(1991-2020} - avgT(1961-1990): %.1f °C" % (avgT1991-avgT1961))
print("")
print("What do we want: climate justice! When do we want it: now!")
Mitteltemperaturen der Klimanormalperioden:
   1901-1930: 8.4 °C(kälteste)
   1931-1960: 8.7 °C
   1961-1990: 8.7 °C
   1991-2020: 9.8 °C (wärmste)

Differenzen zwischen den Mittelwerten:
   Wärmste NP (1991-2020) - kälteste NP (1901-1930): 1.4 °C
   avgT(1991-2020} - avgT(1961-1990): 1.1 °C

What do we want: climate justice! When do we want it: now!

Nochmal die Abbildung: nun mit den Mittelwerten der Normalperioden

In [28]:
plt.rcParams.update({'font.size': 16})

fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(10,5))

plt.plot(dfa.index, dfa.temp, "-", color="tab:blue")
#plt.grid()
plt.ylabel("Temperatur (°C)")
#plt.title("Jahresmitteltemperatur, Säkularstation Potsdam")
plt.xlim(pd.to_datetime("1893-01-01"), pd.to_datetime("2021-01-01"))
# Grenzen der Normalperioden einzeichnen
plt.axvline(pd.to_datetime("1901-01-01"), ls="-", color="tab:red")
plt.axvline(pd.to_datetime("1931-01-01"), ls="-", color="tab:red")
plt.axvline(pd.to_datetime("1961-01-01"), ls="-", color="tab:red")
plt.axvline(pd.to_datetime("1991-01-01"), ls="-", color="tab:red")
plt.axvline(pd.to_datetime("2021-01-01"), ls="-", color="tab:red")
# T-Mittelwerte der Normalperioden
plt.plot([pd.to_datetime("1901-01-01"), pd.to_datetime("1930-12-31")], [avgT1901, avgT1901], color="tab:red")
plt.plot([pd.to_datetime("1931-01-01"), pd.to_datetime("1960-12-31")], [avgT1931, avgT1931], color="tab:red")
plt.plot([pd.to_datetime("1961-01-01"), pd.to_datetime("1990-12-31")], [avgT1961, avgT1961], color="tab:red")
plt.plot([pd.to_datetime("1991-01-01"), pd.to_datetime("2020-12-31")], [avgT1991, avgT1991], color="tab:red")
# Betonung der Differenz zwischen letzter und aktueller NP
plt.plot([pd.to_datetime("1991-01-01"), pd.to_datetime("1991-01-01")],
            [avgT1961, avgT1991], color="tab:red", lw=5, solid_capstyle="butt")
_ = plt.text(pd.to_datetime("1992-12-01"), 9.1, r"$\Delta$"+"T=%.1f °C" % (avgT1991-avgT1961),
         fontsize=20, color="tab:red", bbox=dict(facecolor='white', alpha=0.5, edgecolor='None'))

Mittlere Tagesgänge

Ein weiteres der vielen beeindruckenden Features von pandas ist die groupby-Funktion. Sie erlaubt es uns, die Einträge des DataFrames nach bestimmten Merkmalen zu gruppieren und für diese Gruppen dann Funktionen anzuwenden. Wir nutzen diese Funktionalität, um mit einer einzigen Zeile den mittleren Tagesgang von Temperatur und relativer Luftfeuchte in Potsdam zu ermitteln. Jetzt wissen wir auch, warum wir die ganze Zeit die Spalte rh mitgeschleppt haben.

In [29]:
# groupby zusammen mit der Stunde des Tages, die wir aus dem Zeitindex des DatFrame extrahieren
dfh = df.groupby(df.index.hour).mean()
In [30]:
dfh
Out[30]:
temp rh
datetime
0 7.143430 85.920234
1 6.851835 87.083679
2 6.576347 88.054951
3 6.338821 88.890505
4 6.138487 89.601656
5 6.063132 89.916130
6 6.258181 89.335565
7 6.885615 86.928044
8 7.807471 83.264165
9 8.875138 78.965712
10 9.926198 74.557015
11 10.828393 70.664200
12 11.520239 67.678980
13 11.992088 65.714267
14 12.224182 64.859829
15 12.144740 65.099444
16 11.780729 66.669005
17 11.169504 69.218995
18 10.405378 72.444813
19 9.593606 75.843658
20 8.838171 78.830695
21 8.285211 81.176684
22 7.838021 83.104471
23 7.468984 84.628128

Schöner sieht das natürlich in einer Abbildung aus. Wir wissen aber jetzt schon, dass wir Schwierigkeiten kriegen, wenn wir die Variable temp und relhum auf eine Achse plotten, da ihre Wertebereiche zu unterschiedlich sind (siehe Tabelle). Darum lernen wir bei dieser Gelegenheit die Sekundärachse kennen.

In [31]:
fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(10,5))
tpl = plt.plot(dfh.index, dfh.temp, "-", color="tab:blue", label="T")
plt.ylabel("T (°C)")
# Hier kommt die Sekundärachse ins Spiel
plt.twinx()
rhpl = plt.plot(dfh.index, dfh.rh, "-", color="tab:red", label="RH")
plt.ylabel("Rel. Luftfeuchte (%)")
plt.title("Mittlerer Tagesgang in Potsdam: 1893-2021")
_ = plt.legend(handles=[tpl[0], rhpl[0]])

Aufgabe

Kleine Auffrischung aus der Angewandten Klimatologie im Bachelor: Erkläre Deinen Nachbar:innen, den Verlauf der beiden Kurven. Warum fällt das Temperaturmaximum nicht mit dem mittleren Sonnenhöchststand zusammen? Wie lässt sich der Verlauf der relativen Luftfeuchte erklären.

Lösung

Die Temperatur der Erdoberfläche steigt, wenn die Nettostrahlung positiv ist und sinkt, wenn sie negativ ist. Die Nettostrahlung wiederum ergibt sich aus der Differenz aus kurzwelliger Strahlungseinnahme (Globalstrahlung abzüglich Albedo) und effektiver langwelliger Ausstrahlung (Ausstrahlung abzüglich atmosphärischer Gegenstrahlung). Die Nettostrahlung ist positiv auch über den Sonnenhöchststand hinaus, wird im Laufe des Nachmittags negativ und erst eine Weile nach Sonnenaufgang wieder positiv. Desweiteren braucht es eine Weile, bis sich die Temperaturdynamik von der Oberfläche bis in 2 m Messhöhe fortsetzt.

Die Dynamik der relativen Luftfeuchte

$RH = \frac{e}{E(T)}$

ist maßgeblich von der Temperatur getrieben, da der Sättigungdampfdruck $E(T)$ gemäß Magnus-Formel exponentiell von der Lufttemperatur $T$ abhängt. Die Dynamik der tatsächlichen Luftfuechte (z.B. ausgedrückt als Dampfdruck $e$) geht darin fast unter.

Mögliche Vertiefungen in der Coding-Werkstatt

  1. Ermittle die Differenz der Mitteltemperatur zwischen den Normalperioden 1961-1990 und 1991-2020 für die Bergwetterwarte Hohenpeißenberg. Wo ist die Differenz größer: in Potsdam oder am Hohenpeißenberg?

    Die Daten findet Ihr auf dem opendata-Server des DWD (Link). Ihr müsst in dem opendata-Verzeichnis allerdings erstmal rausfinden, in welcher Datei die Daten der Station Hohenpeißenberg liegen...

  1. Berechne als zusätzliche Spalte des DataFrame df den Dampfdruck (in hPa). Mache Dir dazu die Definition der relativen Luftfeuchte zunutze. Verwende in erster Näherung die Magnusformel über ebenen Wasserflächen:

    $e = 6.112 * exp(\frac{17.61 * T}{243.12 + T})$

    Stelle nun den mittleren Tagesgang der Temperatur und des Dampfdrucks dar!

  1. Etwas schwerer...stelle den mittleren Tagesgang der Temperatur in den Monaten Dez-Feb im Vergleich zu den Monaten Jun-Aug dar. Was fällt auf?
In [ ]: