Notationen wie im vorigen Dokument

1 Die Säkularstation in Potsdam

Säkularstation 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 Telegrafenberg 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 (‘comma/character separated values’ - das heißt, Spaltenwerte sind mittels Kommata bzw. Semikolons getrennt).

2 Einlesen der Daten

Umweltdaten aus unterschiedlichen Quellen liegen typischerweise in irgendeinem Tabellenformat vor. R verfügt über verschiedene Möglichkeiten, diese externen Daten einzulesen und weiter zu verarbeiten. Wir werden nun die Daten der Säkularstation Potsdam einlesen.

Bevor wir diese einlesen können, müssen wir R zunächst erklären, wo diese Daten liegen. Wir setzten also das ‘Working Directory’ fest (setwd()) - das Verzeichnis, auf das wir zugreifen möchten, da dort Deine Datendateien liegen.

R erwartet forward slashes (z.B. “../../”) und keine backslashes (“....", was die typische Windows-Schreibweise bei Verzeichnissen ist). Achtet außerdem darauf, dass die Schreibweise des Verzeichnisses 1-zu-1 richtig angegeben ist (z.B. keine Leerzeichen einfügen, wo keine sind). Apropos Leerzeichen: Die können manchmal Probleme bereiten. Also”C:/Umweltdatenanalyse/Dataframes_Daten/” (mit Unterstrich) eignet sich besser als “C:/Umweltdatenanayse/Dataframes Daten/” (mit Leerzeichen). Achtet außerdem darauf, dass Ihr in euren Verzeichnis- und Dateinamen keine Sonderzeichen oder Umlaute verwendet. Die werden - je nach Systemeinstellung - von R oftmals leider auch nicht richtig erkannt.

wdir  <- "F:/lehre/Umweltdatenanalyse/"  # das musst Du entsprechend Deines Verzeichnisses natürlich anpassen
setwd(wdir)

Mittels list.files() können wir uns nun alle Dateien und Unterverzeichnisse in unserer ‘Working Directory’ auflisten lassen:

list.files()  # der Output sieht bei Dir anders aus
## [1] "Dataframes.html"  "Dataframes.Rmd"   "Dataframes2.html" "Dataframes2.Rmd" 
## [5] "img"              "README.md"        "uebung"

Die Daten, die wir einlesen möchten, liegen in der Datei mit dem sperrigen Namen ‘produkt_tu_stunde_18930101_20201231_03987.txt’. Mit der Funktion read.table() können wir nun die Tabelle einlesen, die in R als Dataframe gespeichert wird.

df <- read.table(file="../data/saekularstation/produkt_tu_stunde_18930101_20201231_03987.txt", header=TRUE, sep=";") 
# 1. Argument: Dateiname inkl. Endung für den Dateityp. Enthält hier einen relativen Pfad zum Datenverzeichnis.
# 2. 'header=TRUE' sagt, dass die erste Zeile in der Datei die Spaltenüberschrift beinhaltet; sollte das einmal nicht der Fall sein wählst Du 'header=FALSE'. 
# 3. Mittels 'sep=";"' erklären wir, dass die Spalten in der Datei durch Semikolons getrennt sind. 

  1. R nutzt und erwartet Punkte als Dezimalzeichen in den Daten. Die Dezimalzeichen für die Werte in der Tabelle, die wir hier nutzen, sind bereits Punkte. Allerdings kann es sein, dass - gerade von Datenquellen aus Deutschland - Kommata als Dezimalzeichen genutzt werden. In solchen Fällen müssen wir das beim Einlesen spezifizieren: read.table(..., dec=","). R wandelt dann alle Kommata in Punkte um.

  2. Ebenfalls kann es beim Einlesen von Tabellen zu Problemen kommen, wenn R nicht weiß, durch welches Zeichen in der Datei die Spalten getrennt sind. Ohne weiter Angabe R interpretiert jeden ‘white space’ in einer Zeile - also jedes Leerzeichen oder Tab - als Spaltentrennung. Das kann zu Problemen führen, wenn eine Spalte mit Text, z.B. Ortsnamen wie “Bad Belzig”, Freizeichen enthalten, die nicht als neue Spalte interpretiert werden sollen. Abhilfe schafft die Spezifikation read.table(..., sep="\t"). Nun interpretiert R nur jeden Tabstopp als Spaltentrennung… und “Bad Belzig” ist gerettet, sofern die Spaltentrennung in der Tabelle durch ein anderes Zeichen als das Leerzeichen gegeben ist.


Das passiert im Übrigen, wenn Du versuchst den Datensatz in Excel oder OpenOffice Calc einzulesen…

calcFail

Aufgabe 1: 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?
  • Lasse Dir die ersten und letzten Zeilen des Dataframes sowie eine Zusammenfassung der Spalten anzeigen.
dim(df)
## [1] 1122010       6
str(df)
## 'data.frame':  1122010 obs. of  6 variables:
##  $ STATIONS_ID: int  3987 3987 3987 3987 3987 3987 3987 3987 3987 3987 ...
##  $ MESS_DATUM : int  1893010101 1893010102 1893010103 1893010104 1893010105 1893010106 1893010107 1893010108 1893010109 1893010110 ...
##  $ QN_9       : int  5 5 5 5 5 5 5 5 5 5 ...
##  $ TT_TU      : num  -12.3 -10.9 -9.9 -9.4 -8.9 -8.9 -8.7 -8.5 -8.8 -8.4 ...
##  $ RF_TU      : num  84 84 84 84 84 84 83 84 82 81 ...
##  $ eor        : chr  "eor" "eor" "eor" "eor" ...
head(df)
##   STATIONS_ID MESS_DATUM QN_9 TT_TU RF_TU eor
## 1        3987 1893010101    5 -12.3    84 eor
## 2        3987 1893010102    5 -10.9    84 eor
## 3        3987 1893010103    5  -9.9    84 eor
## 4        3987 1893010104    5  -9.4    84 eor
## 5        3987 1893010105    5  -8.9    84 eor
## 6        3987 1893010106    5  -8.9    84 eor
tail(df)
##         STATIONS_ID MESS_DATUM QN_9 TT_TU RF_TU eor
## 1122005        3987 2020123118    3   1.8    81 eor
## 1122006        3987 2020123119    3   1.3    82 eor
## 1122007        3987 2020123120    3   1.9    80 eor
## 1122008        3987 2020123121    3   1.4    84 eor
## 1122009        3987 2020123122    3   1.2    88 eor
## 1122010        3987 2020123123    3   1.4    90 eor
summary(df)
##   STATIONS_ID     MESS_DATUM             QN_9            TT_TU        
##  Min.   :3987   Min.   :1.893e+09   Min.   : 1.000   Min.   :-26.800  
##  1st Qu.:3987   1st Qu.:1.925e+09   1st Qu.: 5.000   1st Qu.:  2.600  
##  Median :3987   Median :1.957e+09   Median : 5.000   Median :  8.800  
##  Mean   :3987   Mean   :1.957e+09   Mean   : 5.015   Mean   :  8.873  
##  3rd Qu.:3987   3rd Qu.:1.989e+09   3rd Qu.: 5.000   3rd Qu.: 14.900  
##  Max.   :3987   Max.   :2.020e+09   Max.   :10.000   Max.   : 38.600  
##      RF_TU            eor           
##  Min.   :  7.00   Length:1122010    
##  1st Qu.: 67.00   Class :character  
##  Median : 84.00   Mode  :character  
##  Mean   : 78.69                     
##  3rd Qu.: 94.00                     
##  Max.   :100.00

Donnerwetter: Stündliche Werte von 1893 bis 2020!

3 Datum und Zeit im Dataframe

Nicht alles in diesem Dataframe erschließt sich auf Anhieb, z.B. dürfte das Format des “MESS_DATUM” nicht ganz klar sein. Das werden wir gleich ändern. Zunächst wollen wir unseren Dataframe ein wenig aufräumen: weniger sperrige Spaltennamen verwenden und Spalten rausschmeißen, die wir in unserem Kontext nicht brauchen.

# Etwas weniger sperrige Spaltennamen
colnames(df) <- c("id", "datetime", "qn9", "temp", "rh", "eor")
# Wir wollen nur die Spalten mit Datum, Temperatur und relativer Luftfeuchtigkeit behalten
df <- df[c("datetime", "temp", "rh")]

Weiter geht es mit der Formatierung der Datumsspalte. Dafür müssen wir unter anderem angeben, in welchem Format das Datum aktuell vorliegt. Das geht über das Argument format. Bei unseren Daten geben die ersten vier Zeichen des Datums das Jahr an (%Y), die nächsten zwei den Monat (%m), die nächsten zwei den Tag (%d) und die letzten zwei die Stunde (%H).

# Das kann etwas dauern... es sind sehr viele Zeilen
df <- transform(df, datetime = strptime(as.character(datetime), format="%Y%m%d%H"))
head(df)
##              datetime  temp rh
## 1 1893-01-01 01:00:00 -12.3 84
## 2 1893-01-01 02:00:00 -10.9 84
## 3 1893-01-01 03:00:00  -9.9 84
## 4 1893-01-01 04:00:00  -9.4 84
## 5 1893-01-01 05:00:00  -8.9 84
## 6 1893-01-01 06:00:00  -8.9 84

Aufgabe 2: Formatierung der Datumspalte

Überlegt, wie die Formatierung der Datumsspalte angepasst werden müsste, wenn das Datum folgendermaßen vorliegen würde: 01.01.1893 01:00

testdate <- "01.01.1893 01:00"
strptime(as.character(testdate), format="%d.%m.%Y %H:%M")
## [1] "1893-01-01 01:00:00 LMT"

Nun wollen wir einen schnellen ersten Plot erstellen. Die stündlichen Temperaturwerte (y-Achse) über die Zeit (x-Achse). Viele grafische Parameter ließen sich noch spezifizieren, wie wir später noch sehen werden…

# 1. Argument: Werte x-Achse (Datum)
# 2. Argument: Werte y-Achse (Temperatur)
# 3. Argument: 'type=l' - Linien (default sind Punkte)
plot(df$datetime, df$temp, type="l") 

Ein ganz schönes Gezappel, aber gut: Es sind ja auch über 100 Jahre stündlicher Daten!

4 Jahresmittelwerte darstellen

Anstelle aller stündlichen Werte wollen wir jetzt alle Jahresmittelwerte von 1893 bis 2020 darstellen. Die Mittelwerte müssen wir zuerst berechnen. Dabei hilft uns die Funktion aggregate(). Dafür aggregieren wir alle Werte eines Jahres und bilden den Mittelwert. Um das Aggregieren zu erleichtern, erstellen wir zunächst eine neue Spalte in df, die nur die Jahreswerte aus datetime beinhaltet.

# Spalte nur mit Jahreszahlen pro Eintrag
df$yr <- format(df$datetime, format = "%Y")

# Berechnung der Jahremittelwerte. Ergebnis wir im neuen Dataframe "df.yr" abgespeichert.
dfa   <- aggregate(df$temp, by = list(df$yr), FUN = mean)

# So sieht's aus
summary(dfa)
##    Group.1                x         
##  Length:128         Min.   : 6.540  
##  Class :character   1st Qu.: 8.118  
##  Mode  :character   Median : 8.824  
##                     Mean   : 8.873  
##                     3rd Qu.: 9.468  
##                     Max.   :11.308
# Schönere Spaltenüberschriften
colnames(dfa) <- c("yr", "temp")

Jetzt können wir die Zeitreihe plotten

plot(dfa$yr, dfa$temp, type="l")

4.1 Abbildung zurechtbasteln

Die obige Abbildung sieht schon ganz okay aus (beeindruckend ist insbesondere das deutliche Erwärmungssignal). Wir wollen aber hier ein paar Möglichkeiten kennenlernen, Abbildungseigenschaften zu kontrollieren (Überschrift, Achsenbeschriftung etc.). R bietet hier eine Vielzahl an Gestaltungsmöglichkeiten (guck mal z.B. hier). Es macht keinen Sinn, sich da systematisch dran zu machen. Hier also lieber für unseren Fall ein konkretes Beispiel mit Erläuterungen im Code:

# Die erste Zeile hatten wir schon...
plot(dfa$yr, dfa$temp, type = "l",
    # Linenfarbe ändern (col=""), Überschrift (main="") hinzufügen; "\n" erstellt neue Zeile
    col = "red", main = "Jahresmitteltemperatur (1893-2020)\nSäkularstation Potsdam",
    # Achsenbeschriftung hinzufügen (bzw. abwählen)
    ylab = "Temperatur (°C)", xlab="",
    # Größenparameter für Liniendicke und Achsen- bzw. Labelbeschriftungen
    lwd = 2, cex.axis = 1.2, cex.lab=1.3)

# Grid hinzufügen
grid(col="gray50")

# Punkte hinzufügen
points(dfa$yr[which.min(dfa$temp)], min(dfa$temp), type="o", cex=2, lwd = 2, col="blue")
points(dfa$yr[which.max(dfa$temp)], max(dfa$temp), type="o", cex=2, lwd = 2, col="orange")

# Legende hinzufügen ('topleft' Position; 'legend' Vektor mit Legendenenträgen
legend("topleft", legend = c("Kältestes Jahr (1940)", "Wärmstes Jahr (2019)"), 
    # Punkttyp; Größe; Farbe (Reihenfolge passend zu Reihenfolge der Legendeneinträge)
    pch = 1, cex = 1.1, col = c("blue", "orange"),
    # bty="n" falls kein Kasten um die Legende gewünscht wird
    bty = "n")
    
# Vertikale Linie mit Beschriftung hinzufügen
abline(v = 1990, lty = 2, col = "gray30", lwd = 1.5)
text(1979, 6.8, "Wieder-\nvereinigung", col = "gray30")

5 Die zehn wärmsten Jahre identifizieren

Da wir nun schon einen Dataframe mit den Jahresmitteltemperaturen (dfa) haben, können wir auch gleich die zehn wärmsten Jahre raussuchen. Dafür nutzen wir die Funktion order, die wir im ersten Teil ja schon kennengelernt haben.

# Mittels 'order' sortieren wir den Dataframe; Indizierung [1:10,] sagt nur die ersten 10 Zeilen
dfwarm <- dfa[order(dfa$temp, decreasing = TRUE),][1:10,] 
dfwarm
##       yr     temp
## 127 2019 11.30806
## 126 2018 11.25048
## 128 2020 11.10231
## 122 2014 10.97670
## 123 2015 10.71597
## 115 2007 10.52424
## 108 2000 10.40418
## 42  1934 10.38329
## 124 2016 10.30926
## 116 2008 10.26071

Neun der zehn wärmsten Jahre liegen im 21. Jahrhundert. Nur ein Jahr (1934) liegt weiter zurück.

Aufgabe 3: Winter is not comming

Wieviel Jahre aus dem 21. Jahrhundert gehören zu den zehn kältesten Jahren der Zeitreihe? Vermutlich weißt Du die Antwort auch so, aber wir wollen es hier anhand der Datenreihe einmal analysieren.

# Wieder sortieren wir 'dfa'; dieses Mal wolen wir die kältesten Jahre filtern (decreasing = FALSE)
dfcold <- dfa[order(dfa$temp, decreasing = FALSE),][1:10,]
dfcold
##       yr     temp
## 48  1940 6.540118
## 10  1902 7.010308
## 49  1941 7.116153
## 64  1956 7.219570
## 104 1996 7.290870
## 30  1922 7.363037
## 95  1987 7.422021
## 50  1942 7.429852
## 70  1962 7.573116
## 27  1919 7.583699

Tatsächlich, nicht ein einziges Jahr!

6 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 R übersetzt heißt das, dass wir aus unserem großen Dataframe df vier ‘subsets’ erstellen möchten, die jeweils die Daten einer Normalperiode, also 1901-1930, 1931-1960, 1961-1990 und 1991-2020, enthalten. Aus dem ersten Teil erinnern wir uns an regelbasierte Teilmengen…

Aufgabe 4: Regelbasierte Teilmengen

Erstelle vier vier Teilmengen (also Teildatensätze) aus df, welche die Werte für die vier Normalperioden beinhalten.

df1901 <- df[which(df$yr >= 1901 & df$yr <=1930),]
df1931 <- df[which(df$yr >= 1931 & df$yr <=1960),]
df1961 <- df[which(df$yr >= 1961 & df$yr <=1990),]
df1991 <- df[which(df$yr >= 1991),]

ODER

df1901 <- subset(df, df$yr >= 1901 & df$yr <=1930)
df1931 <- subset(df, df$yr >= 1931 & df$yr <=1960)
df1961 <- subset(df, df$yr >= 1961 & df$yr <=1990)
df1991 <- subset(df, df$yr >= 1991)

Im nächsten Schritt wollen wir für jede Normalperiode die mittlere Temperatur berechnen und in einem separaten Dataframe abspeichern.

# Berechnung der Mittelwerte
avgT1901 <- mean(df1901$temp)
avgT1931 <- mean(df1931$temp)
avgT1961 <- mean(df1961$temp)
avgT1991 <- mean(df1991$temp)

# Anlegen eines neuen Dataframes
avgTnp <- data.frame(period = c("1901-1930", "1931-1960", "1961-1990", "1991-2020"),
                     t.potsdam = c(avgT1901, avgT1931, avgT1961, avgT1991))
print(avgTnp)
##      period t.potsdam
## 1 1901-1930  8.415486
## 2 1931-1960  8.699164
## 3 1961-1990  8.711370
## 4 1991-2020  9.817714

Die letzte Tabelle zeigt also, dass die jüngste (älteste) Normalperiode, die wärmste (kälteste) war - auch das kommt nicht gerade unerwartet, oder?

6.1 Vergleich der Normalperioden

Wie groß ist die Differenz zwischen den einzelnen sowie der wärmsten und kältesten Normalperiode? Wann war die Erwärmung am stärksten? All das wollen wir berechnen:

# Wir kennen die kälteste bzw. wärmste NP; und berechnen die Differenz
delta_ges <- avgTnp$t.potsdam[4] - avgTnp$t.potsdam[1]
print(paste("Wärmste NP (1991-2020) - kälteste NP (1901-1930):", round(delta_ges, digits=2)))
## [1] "Wärmste NP (1991-2020) - kälteste NP (1901-1930): 1.4"
# Berechnung Differenz zwischen den Normalperioden 
dt.potsdam <- diff(avgTnp$t.potsdam)
avgTnp     <- cbind(avgTnp, "dt.potsdam" = c(NA,dt.potsdam))

# 'avgTnp' hat nun eine weitere Spalte, die delta_T für die Normalperioden zeigt
avgTnp
##      period t.potsdam dt.potsdam
## 1 1901-1930  8.415486         NA
## 2 1931-1960  8.699164 0.28367828
## 3 1961-1990  8.711370 0.01220563
## 4 1991-2020  9.817714 1.10634409

R bietet natürlich nicht nur die Möglichkeit, Tabellen einzulesen, sondern auch in R erstellte/bearbeitete Dataframes als Datei auszuschreiben. Im folgenden Beispiel schreiben wir den Dataframe avgTnp als Datei aus.

# Wir definieren das Verzeichnis, in das die Datei geschrieben werden soll
setwd(wdir)  # in dem Fall das gleiche Verzeichnis, in dem wir schon die ganze Zeit arbeiten; das kann aber natürlich angepasst werden

# Wir definieren den Dateinamen, den die ausgeschriebene Tabelle erhalten soll
fname <- "Tnp_potsdam.txt"

# Mittels 'write.table' schreiben wir die Datei aus
write.table(avgTnp, file = fname, col.names = TRUE, row.names = FALSE)
# 1. Argument: das Objekt, das wir ausgeben wollen
# 2. Argument: der Dateiname, den die ausgeschriebene Datei erhalten soll
# col.names=TRUE - wir wollen die Spaltenüberschriften mit ausschreiben
# row.names=FALSE - wir wollen die Zeilennummerierung nicht mit ausschreiben

Wenn Du jetzt in Dein Arbeitsverzeichnis schaust, sollte die Datei “Tnp_potsdam.txt” erzeugt worden sein, die Du nun in einem beliebigen Texteditor (oder auch in Excel/OpenOffice Calc - aber das brauchen wir ja bald nicht mehr…) öffnen kannst.


6.2 Visualisierung

Auf Grundlage unseres letzten Plots wollen wir den mittleren Temperaturanstieg über die Normalperioden anzeigen.

# Das hatten wir schon (hier etwas reduziert...)
plot(dfa$yr, dfa$temp, type = "l",
        col = "red", main = "Jahresmitteltemperatur (1893-2020)\nSäkularstation Potsdam",
      ylab = "Temperatur (°C)", xlab="", lwd = 2, cex.axis = 1.2, cex.lab=1.3)
grid(col="gray50")

# Hinzufügen vertikaler Linien, die die Normalperioden abgrenzen
abline(v = c(1900, 1930, 1960, 1990, 2020), col = "dodgerblue3", lwd = 2, lty=3)

# Hinzufügen horizontaler Linien für die jeweilige Durchschnittstemperatur der Normalperioden
# Zugegeben: etwas umständlich mit den Plot-Grundfunktionen in R
lines(c(1900, 1930), rep(avgT1901, 2), col="dodgerblue3", lwd = 3) # x-Wert c(von, bis); y-Wert (rep(Wert, 2x))
lines(c(1930, 1960), rep(avgT1931, 2), col="dodgerblue3", lwd = 3)
lines(c(1960, 1990), rep(avgT1961, 2), col="dodgerblue3", lwd = 3)
lines(c(1990, 2020), rep(avgT1991, 2), col="dodgerblue3", lwd = 3)

# Hervorheben der Differenz zwischen der letzten und der aktuellen NP
lines(rep(1990,2), c(avgT1961, avgT1991), col = "darkturquoise", lwd=5)
txt <- expression(paste(Delta, "T = 1.1°C"))
text(1992, 9.2, txt, pos = 4, col="darkturquoise", cex=1.4)

7 Mittlere Tagesgänge

Wir haben ja bereits die Funktion aggregate() kennengelernt, mit deren Hilfe wir die Jahremittelwerte berechnet haben. Am folgenden Beispiel gruppieren wir nun über Tagesstunden und berechnen Tagesmittelwerte der Temperatur und der relativen Luftfeuchtigkeit - dafür brauchen wir nur wenige Zeilen.

df$hr <- format(df$datetime, format = "%H:%M")
dfh <- aggregate(df$temp, by = list(df$hr), FUN = mean)
dfh <- cbind(dfh, aggregate(df$rh, by = list(df$hr), FUN = mean)[,2])
colnames(dfh) <- c("hours", "temp", "rh")
print(dfh)
##    hours      temp       rh
## 1  00:00  7.143430 85.92023
## 2  01:00  6.851835 87.08368
## 3  02:00  6.576347 88.05495
## 4  03:00  6.338821 88.89051
## 5  04:00  6.138487 89.60166
## 6  05:00  6.063132 89.91613
## 7  06:00  6.258181 89.33557
## 8  07:00  6.885615 86.92804
## 9  08:00  7.807471 83.26417
## 10 09:00  8.875138 78.96571
## 11 10:00  9.926198 74.55701
## 12 11:00 10.828393 70.66420
## 13 12:00 11.520239 67.67898
## 14 13:00 11.992088 65.71427
## 15 14:00 12.224182 64.85983
## 16 15:00 12.144740 65.09944
## 17 16:00 11.780729 66.66901
## 18 17:00 11.169504 69.21899
## 19 18:00 10.405378 72.44481
## 20 19:00  9.593606 75.84366
## 21 20:00  8.838171 78.83070
## 22 21:00  8.285211 81.17668
## 23 22:00  7.838021 83.10447
## 24 23:00  7.468984 84.62813

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 rh auf eine Achse plotten, da ihre Wertebereiche zu unterschiedlich sind (siehe Tabelle). Darum lernen wir bei dieser Gelegenheit die Sekundärachse kennen.

# Mit 'par(mfrow=c())' richten wir mehr Platz rechts vom Plot - brauchen wir für die zweite y-Achse (versuch's erst mal ohne, dann siehst Du, was gemeint ist)
par(mar=c(5, 4, 4, 5))

# Plotten der Temperatur OHNE Achsen(beschriftungen)
plot(dfh$temp, main = "Mittlerer Tagesgang in Potsdam",
     ylab = "", yaxt="n", ylim = c(5,13), 
     xlab = "Uhrzeit", xaxt="n",  
     type = "l", col = "orangered", lwd = 2)

# Hinzufügen der x-Achse
axis(1, 1:24, dfh$hours)  # 1=Achse unten, aka x-Achse, Markierungen bei 1-24, Beschriftung Stunden aus dfh

# Hinzufügen der linken y-Achse und deren Beschriftung manuell
axis(2, ylim=c(5,13),  col="firebrick2", col.axis="orangered")    # 2=Achse links
mtext("Lufttemperatur (°C)", col="orangered", side=2, line=2.5)   # side=2 (links), line=Abstand zur Achse

# Mittels `par(new = TRUE)` geben wir an, dass der nächste Plot zum bestehenden ergänzt werden soll
par(new = TRUE)

# Nun plotten wir die zweite Zeitreihe (rel. Luftfeuchte) und bestellen alle 'goodies' ab
plot(dfh$rh, col = "blue",        # y-Achsenwerte, Farbe, Darstellung = Linien
     type = "l", lwd = 2,         # Darstellung = Linien; Liniendicke = 2 (100% breiter als 1)
     xaxt="n", yaxt="n",          # x- und y-Achse wollen wir nicht
     xlab="", ylab="")            # Achsenbeschriftung wollen wir nicht

# Die Legende...
legend("topright", legend=c("T", "RH"), col=c("orangered", "blue"), lty=1, bty="n")

# Wir fügen die rechte y-Achse und deren Beschriftung manuell hinzu
axis(4, col="blue", col.axis="blue")                           # 4=Achse rechts
mtext("Relative Luftfeuchtigkeit (%)", col="blue", side=4, line=2.5)

Aufgabe 5: Wie war das nochmal?

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?

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.


8 Vorschläge für die Coding-Werkstatt

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

  2. 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!

  3. Stelle den mittleren Tagesgang der Temperatur in den Monaten Dez-Feb im Vergleich zu den Monaten Jun-Aug dar. Was fällt auf?