„Die Nidder ist ein linker Nebenfluss der Nidda im Bundesland Hessen, Deutschland. Von der Quelle bis kurz vor der Mündung verläuft sie etwa 10 km südöstlich parallel zur Nidda.“ (Quelle: Wikipedia am 21.01.24) Als zugezogener Nidderauer möchte ich den Fluss kennenzulernen, der nur 100m vor meiner Haustür entlang fließt. Die klassische Erwartung wäre nun sicherlich, dass ich den Fluss ablaufe, doch als Datenanalyst möchte ich die Nidder über ihre Daten kennenlernen und dabei gleich Ideen zur Datenanalyse (Statistik) mitgeben.
Am 23. Januar hatte ich schon einmal die Temperaturentwicklung mittels Minitab 21.4 angesehen. Diesen Beitrag vorab zu lesen ist sicherlich von Vorteil. Dieses Mal möchte ich erneut per Analyse - zumindest den zentralen Teil - betrachten. Dafür nutze ich R ohne selber über R-Kenntnisse zu verfügen. Ich habe mir hierfür R-Studio installiert. Alles andere verlief im Dialog mit ChatGPT. Entsprechend hätte auch Python genutzt werden können.
Feedback und Diskussionen sind willkommen.
Zu den Daten
Die hier betrachteten Daten sind frei zugänglich und stammen vom Hessischen Landesamt für Naturschutz, Umwelt und Geologie. Die Temperaturdaten beginnen ab April 2012, wobei die Daten vom Juni 2021 bis August 2022 fehlen. Fälschlicherweise wurden sie auf „0“ gesetzt. Das ist nicht korrekt, denn solch ein Vorgehen birgt die Gefahr, dass sie für die Auswertung mit herangezogen werden und so die Ergebnisse verfälschen. Besser ist es einen fehlenden Wert als solchen zu beschreiben. Ich vermute, dass das Temperaturmessgerät zu der Zeit defekt war.
Die Datei und der Code
Dieser Abschnitt ist für die, die es wirklich genau wissen wollen und darf ansonsten übersprungen werden ;-)
# Installiere und lade notwendige Pakete
install.packages("ggplot2")
install.packages("minpack.lm")
library(ggplot2)
library(minpack.lm)
# EINLESEPROZESS: Die ersten Zeilen in der Datei sind Kommentarzeilen, die jeweils anfangs mit einer Raute gekennzeichnet sind. Der Einleseprozess ist so gestaltet, dass die Kommentarzeilen ignoriert werden. Dafür muss zeilwenweise eingelesen werden. Würde diese Korrektur bereits manuell passiert sein, wäre der Code deutlich kürzer.
# Pfad zur CSV-Datei (bitte an die aktuelle Datei anpassen)
csv_file_path <- "windecken_nidder_wassertemperatur_tagesmittelwerte_17042012-01082024.csv"
# Lese die Datei zeilenweise ein
lines <- readLines(csv_file_path)
# Filtere die Zeilen heraus, die mit # beginnen
data_lines <- lines[!grepl("^#", lines)]
# Schreibe die gefilterten Zeilen in eine temporäre Datei
temp_file <- tempfile()
writeLines(data_lines, temp_file)
# Lese die gefilterten Daten in R ein
temperature_data <- read.table(temp_file, sep = "\t", dec = ",", header = TRUE)
# FEHLENDE DATEN durch NA ersetzen: In den Daten sind fehlende Daten mit 0 definiert. Dies ist für eine Analyse aber nicht vorteilhaft, da sich mit 0 rechnen lässt. In dieser einfachen Variante wird daher direkt 0 als fehlend definiert, auch wenn 0°C im Winter selbst bei einem Fluss vorkommen kann.
# Ersetze "0" in den Spalten 2, 3 und 4 durch NA
cols_to_check <- c(2, 3, 4)
temperature_data[, cols_to_check] <- lapply(temperature_data[, cols_to_check], function(x1) {
x1[x1 == 0] <- NA
return(x1)
})
# MODELLIERUNG: Wichtig zu verstehen ist, dass das verwendete Paket zur Modellierung Zahlenwerte benötigt. Als Modellierungsgleichung wurde hier (etwas abweichend zur ersten Analyse) eine mit der Zeit ansteigende Winkelfunktion vorgegeben, dessen Parameter A, B, C, D, E über einen Solver ermittelt werden sollen. D ist dabei der interessante Parameter, der den durchschnittlichen (täglichen) Temperaturanstieg beschreibt. A, B und C passen die Winkelfunktion an die jährliche Temperaturschwankung (Winter-Sommer) an und E verschiebt dies noch auf der y-(Temperatur-)Achse.
# Konvertiere die Datumsspalte in eine numerische Spalte
temperature_data$Tag_numeric <- as.numeric(as.Date(temperature_data$Tag, format = "%d.%m.%Y"))
# Definiere die Modellgleichung
model_function <- function(x, A, B, C, D, E) {
A * sin((B * x - C) / 365.25 * 2 * pi) + D * x + E
}
# Definiere die Startwerte
start_values <- list(A = 10, B = 1, C = 15000, D = 0.1/365, E = 10)
# Passe das Modell an, NA-Werte werden ignoriert
fit <- nlsLM(Tages.Mittel ~ model_function(Tag_numeric, A, B, C, D, E),
data = temperature_data,
start = start_values,
na.action = na.exclude)
#AUSGABE DER ERGEBNISSE: Sowohl via Parameter, als auch grafisch. Bei der Visualisierung wird darauf geachtet, dass die Achsenbeschriftung der x-Achse wieder aus der Spalte mit dem Datumsformat und nicht aus der umgewandelten numerischen Spalte kommt. Außerdem werden Lücken in den Daten auch beim Fit gewürdigt.
# Füge die Vorhersagen zum Datensatz hinzu und behalte die Zeilenstruktur bei
temperature_data$Fitted <- NA
temperature_data$Fitted[!is.na(temperature_data$Tages.Mittel)] <- predict(fit)
summary(fit)
# Visualisiere die Daten und die angepasste Kurve
ggplot(temperature_data, aes(x = as.Date(Tag, format = "%d.%m.%Y"))) +
geom_point(aes(y = Tages.Mittel), color = "blue") +
geom_line(aes(y = Fitted), color = "red") +
labs(title = "Modellierung der Temperaturdaten von Nidderau (Hessen)",
x = "Tag",
y = "Tagesmittel (°C)") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Es werden von ChatGPT sogar nochmal die einzelnen Schritte erklärt. Hier ein Beispiel aus einer früheren Version des Codes:
"Erklärung der Schritte:
Pakete installieren und laden: minpack.lm wird für den Solver und ggplot2 für die Visualisierung verwendet.
Datei einlesen und filtern: Die Datei wird zeilenweise eingelesen, und Zeilen, die mit # beginnen, werden herausgefiltert. Danach werden die Daten in eine temporäre Datei geschrieben und eingelesen. Werte von "0" in den Spalten 2, 3 und 4 werden durch NA ersetzt und entfernt.
Modellgleichung definieren: Die Modellgleichung wird als Funktion definiert.
Startwerte festlegen: Die Startwerte für den Solver werden definiert.
Modell anpassen: nlsLM passt das Modell an die Daten an.
Ergebnisse anzeigen: Die Anpassungsergebnisse werden angezeigt.
Daten visualisieren: Die Daten und die angepasste Kurve werden visualisiert.
Dieser Code verwendet den Levenberg-Marquardt-Algorithmus, um die Parameter des Modells an die Daten anzupassen und die angepasste Kurve zusammen mit den Originaldaten darzustellen."
Die Ergebnisse
Wie auf der Grafik rechts zu sehen ist, passt sich der Fit (rot) sehr gut an die vorhandenen Daten (blau) an. Interessant ist der Parameter D, der den mittleren Temperaturanstieg beschreibt. Dieser ist 0,000276 °C pro Tag und mit einem p-Wert von weniger als 2e-16 extrem signifikant. Zum Vergleich: Die Methode aus dem Januar brachte einen Anstieg von 0,000268°C pro Tag hervor, ein Unterschied von nur 3%. Am Ende bleibt auch hier die Aussage von einem Temperaturanstieg von rund 0,1°C pro Jahr erhalten.
Mein Vorgehen
Ich habe es nicht gewagt ChatGPT all meine Wünsche in einem großen Wunschzettel verpackt mitzugeben. Manche Themen haben sich auch erst mit der Zeit ergeben. Vielmehr bin ich Schritt für Schritt vorgegangen.
Begonnen habe ich mit einem einfachen Datensatz ohne die Kommentarzeilen und einen einfachen Wunsch nach Modellierung. Dabei habe ich die Grundgleichung "y = (A * sin((B * x - C) / 365.25 * 2 * pi) + D * x + E)" und den Wunsch dies per Solver zu lösen vorgegeben. Überrascht war ich, dass ChatGPT von sich aus auch direkt eine Visualisierung mit integriert hat. Allerdings gab es beim ersten Code auch gleich eine Fehlermeldung, weil ChatGPT nicht geahnt/berücksichtigt hat, dass die Zeit im für die Modellierung nicht verwendbaren Datumsformat gegeben war. Hier habe ich manuell darauf hingewiesen und einen neuen Code erhalten.
Auf Grundlage dieses Basiscodes habe ich dann weitere Wünsche gestellt. Der erste war die Berücksichtigung bzw. Nichtberücksichtigung von Kommentarzeilen in der Datei während des Einleseprozesses. Dann wollte ich, dass das Modell in der Grafik fehlende Werte nicht berücksichtigt. Ohne diese beiden Sonderwünsche zur flexiblen Datenhandhabung wäre der Code viel übersichtlicher ausgefallen.
Bei einem Fehler hatte ich länger mit zu kämpfen. Als ich dann aber den Code, die Fehlermeldung und den von mir bereits manuell eingekreisten Fehlerort ChatGPT gab, konnte er den Code direkt korrigieren.
Mein Fazit
Die Nidder wird noch immer wärmer. Aber diese Aussage ist hier nur Nebensache. Hat man Verständnis für das Ziel seiner Datenanalyse, sind einen statistische Methoden nicht ganz fremd und kann man sich auch ein wenig reindenken, was der R-Code oder auch der Python-Code macht, so lassen sich selbst für Beginner mit Hilfe von ChatGPT & Co mittlerweile mit überschaubarem Aufwand tiefgründige Datenanalysen durchführen. Der Vorteil von R und Python ist, dass sie durch die zahlreichen Pakete der Community quasi unbegrenzt Analysen zulassen und bei den einzelnen Analysen auch sehr viele Möglichkeiten und Verfahren bieten. Die Kluft zwischen Entwicklungsumgebung und Standardsoftware wird durch die Komplexitätsreduktion für den Normalanwender also immer kleiner. Dennoch habe ich jetzt hier aus guten zeitlichen Gründen darauf verzichtet die Daten an sich über R zu diskutieren und auch Themen, wie Residuendiagramme & Co zu erstellen. Aber auch das wäre nur eine Frage der zu investierenden Zeit.
Comentarios