Code
# Speicherort sowie Dateiname anpassen
<- read_delim("./HIER RELATIVEN DATEIPFAD EINGEBEN", "HIER SEPERATOR EINGEBEN") depo
Die Projektstruktur steht. Nun können die Daten eingelesen und die nötigen Datentypen definiert werden.
Lädt die Daten zuerst von Moodle herunter.
Hinweise:
Siehe [Einführung] für den Standort der Zähler 211 und 502.
Alle für die Fallstudie Profil S benötigten Daten könnt ihr unter folgendem Link herunterladen.
Die Zähldaten des WPZ wurden vorgängig bereinigt (z.B. wurden Stundenwerte entfernt, an denen am Zähler Wartungsarbeiten stattgefunden haben). Das macht es für uns einfach, denn wir können die Daten ohne vorgängige Bereinigung einlesen. Behaltet aber im Hinterkopf, dass die Datenaufbereitung, die Datenbereinigung mit viel Aufwand verbunden ist.
# Speicherort sowie Dateiname anpassen
<- read_delim("./HIER RELATIVEN DATEIPFAD EINGEBEN", "HIER SEPERATOR EINGEBEN") depo
Hinweis: Im Stundenformat zeigen die Werte bei 11:00 die Zähldaten zwischen 11:00 bis 12:00 Uhr.
# lese die Daten ein
<- read_delim("datasets/fallstudie_s/WPZ/211_sihlwaldstrasse_2017_2022.csv", ";")
depo
# erstes Sichten und anpassen der Datentypen
str(depo)
<- depo |>
depo mutate(
Datetime = as.POSIXct(DatumUhrzeit, format = "HIER STEHT DAS DATUMSFORMAT", tz = "CET"),
# nun schreiben wir uns das Datum in eine seperate Spalte
Datum = as.Date(Datetime)
)
# hier der code mit dem richtigen Format
<- depo |>
depo mutate(
Datetime = as.POSIXct(DatumUhrzeit, format = "%d.%m.%Y %H:%M", tz = "CET"),
Datum = as.Date(Datetime)
)
Ihr könnt selbst wählen, ob ihr Fussgänger:innen oder Fahrräder untersuchen wollt (je nachdem ob sie in eurem Datensatz vorhanden sind).
# In dieser Auswertung werden nur Personen zu Fuss betrachtet!
# it select werden spalten ausgewaehlt oder eben fallengelassen
<- depo |>
depo ::select(-c(Velo_IN, Velo_OUT, Zeit, DatumUhrzeit)) dplyr
Tipp: Wenn man R sagt: “addiere mir Spalte x mit Spalte y”, dann macht R das für alle Zeilen in diesen zwei Spalten. Wenn man nun noch sagt: “speichere mir das Ergebnis dieser Addition in einer neuen Spalte namens Total”, dann hat man die Aufgabe bereits gelöst. Arbeitet mit mutate()).
Hinweis: Ihr habt das auch schon in Kapitel [Einführung und Installation] gemacht.
# Berechnen des Totals, da dieses in den Daten nicht vorhanden ist
<- depo |>
depo mutate(Total = Fuss_IN + Fuss_OUT)
# Entferne die NA's in dem df.
<- na.omit(depo) depo
# Einlesen
<- read_delim("datasets/fallstudie_s/WPZ/order_105742_data.txt", ";") meteo
Tipp: Das Datum wird als Integer erkannt. Zuerst muss es in Text umgewandelt werden aus dem dann das eigentliche Datum herausgelesen werden kann. Das ist mühsam - darum hier der Code.
<- mutate(meteo, time = as.Date(as.character(time), "%Y%m%d")) meteo
Die Zeitangaben sind in UTC: 00:40 UTC = 02:40 Sommerzeit = 01:40 Winterzeit, Beispiel: 13 = beinhaltet Messperiode von 12:01 bis 13:00
–> Da wir mit Tageshöchstwerten oder -summen rechnen, können wir zum Glück ignorieren, dass das nicht mit den Daten der Zählstellen übereinstimmt. Learning: es ist zentral immer die Metadaten zu checken.
Hinweis Was ist eigentlich Niederschlag:
Werden den anderen Spalten die richtigen Typen zugewiesen? Falls nicht, ändert die Datentypen.
Nun schneiden wir den Datensatz auf die Untersuchungsdauer zu.
Dann müssen auch hier alle nicht verfügbare Werte (NA’s) herausgefiltert werden.
Prüft nun, wie die Struktur des data.frame (df) aussieht und ob alle NA Werte entfernt wurden (sum(is.na(df$Variable))). Stimmen alle Datentypen?
# Die eigentlichen Messwerte sind alle nummerisch
<- meteo |>
meteo mutate(
tre200nx = as.numeric(tre200nx),
tre200jx = as.numeric(tre200jx),
rre150n0 = as.numeric(rre150n0),
rre150j0 = as.numeric(rre150j0),
sremaxdv = as.numeric(sremaxdv)
|>
) filter(time >= depo_start, time <= depo_end) # schneide dann auf Untersuchungsdauer
# Was ist eigentlich Niederschlag:
# https://www.meteoschweiz.admin.ch/home/wetter/wetterbegriffe/niederschlag.html
# Filtere Werte mit NA
<- na.omit(meteo)
meteo # Pruefe ob alles funktioniert hat
str(meteo)
sum(is.na(meteo)) # zeigt die Anzahl NA's im data.frame an
Jetzt fügen wir viele Convinience Variabeln hinzu. Wir brauchen:
|>
...mutate(Wochenende = ifelse(Wochentag %in% c(6,7), "Wochenende", "Werktag")) |>
mutate(Wochenende = as.factor(Wochenende)) |>
...
je als Faktor: 3. Kalenderwoche: isoweek() 4. Monat: month() 5. Jahr: year()
<- depo |>
depo # wday sortiert die Wochentage automatisch in der richtigen Reihenfolge
mutate(
Wochentag = wday(Datetime, week_start = 1),
Wochentag = factor(Wochentag),
# Werktag oder Wochenende hinzufuegen
Wochenende = ifelse(Wochentag %in% c(6, 7), "Wochenende", "Werktag"),
Wochenende = as.factor(Wochenende),
# Kalenderwoche hinzufuegen
KW = isoweek(Datetime),
KW = factor(KW),
# monat und Jahr
Monat = month(Datetime),
Monat = factor(Monat),
Jahr = year(Datetime),
Jahr = factor(Jahr))
Dies machen wir auch mit dem “meteo” Datensatz.
# Wir gruppieren die Meteodaten noch nach Kalenderwoche und Werktag / Wochenende
# Dafür brauchen wir zuerst diese als Convenience Variablen
<- meteo |>
meteo # wday sortiert die Wochentage automatisch in der richtigen Reihenfolge
mutate(
Wochentag = wday(time, week_start = 1),
Wochentag = factor(Wochentag),
# Werktag oder Wochenende hinzufuegen
Wochenende = ifelse(Wochentag %in% c(6, 7), "Wochenende", "Werktag"),
Wochenende = as.factor(Wochenende),
# Kalenderwoche hinzufuegen
KW = isoweek(time),
KW = factor(KW),
# monat und Jahr
Monat = month(time),
Monat = factor(Monat),
Jahr = year(time),
Jahr = factor(Jahr))
<- meteo |>
meteo_day group_by(Jahr, Monat, KW, Wochenende) |>
summarise(
tre200nx = mean(tre200nx),
tre200jx = mean(tre200jx),
rre150n0 = mean(rre150n0),
rre150j0 = mean(rre150j0),
sremaxdv= mean(sremaxdv))
Wieder zurück zum Depo-Datensazt.
Ich mache den folgenden Punkt nachgelagert, da zu viele Operationen in einem Schritt auch schon mal etwas durcheinander erzeugen können.
Phase Covid (Code untenstehend). Wir definieren 5 Phasen:
von Anfang Untersuchungsperiode bis 1 Jahr vor Lockdown 1
Lockdown 1
zwischen den Lockdowns
Lockdown 2
Ende 2. Lockdown bis Ende Untersuchungsperiode
Wir packen alle Phasen (normal, die beiden Lockdowns und Covid aber ohne Lockdown) in eine Spalte –> long-format ist schöner (und praktischer für das plotten) als wide-format.
Später im multivariaten Modell werden die Levels der Variablen (z.B. bei der Phase Covid: Pre, Normal, Lockdown 1 und 2, Covid) per “default” alphabetisch geordnet und die Effektstärken der einzelnen Levels gegenüber dem ersten Level gerechnet. Das macht wenig Sinn, den die Levels sind nicht alphabetisch, sondern gemäss der Liste oben. Das passen wir ebenfalls an.
Hier der Code dazu:
<- depo |>
depo mutate(Phase = case_when(
< lock_1_start ~ "Pre",
Datetime >= lock_1_start & Datetime <= lock_1_end ~ "Lockdown_1",
Datetime > lock_1_end & Datetime < lock_2_start ~ "inter",
Datetime >= lock_2_start & Datetime <= lock_2_end ~ "Lockdown_2",
Datetime > lock_2_end ~ "Post"
Datetime
))
# hat das gepklappt?!
unique(depo$Phase)
<- depo |>
depo # mit factor() koennen die levels direkt einfach selbst definiert werden.
# wichtig: speizfizieren, dass aus R base, ansonsten kommt es zu einem
# mix-up mit anderen packages
mutate(Phase = base::factor(Phase, levels = c("Pre", "Lockdown_1", "Inter", "Lockdown_2", "Post")))
str(depo)
Neben dem Lockdown können auch die Schulferien einen Einfluss auf die Besuchszahlen haben. Wir haben die Schulferien bereits als .csv eingelesen. Allerdings können wir die Schulferien nicht mit der case_when()-Funktion zuweisen, da diese mit dieser Funktion alle Vektoren im Datensatz “schulferien” verglichen werden, und nicht elementweise für jede Zeile im “depo”-Datensatz. Dies führt dazu, dass die Bedingungen nur einmal überprüft werden und dann auf den gesamten Vektor angewendet werden, anstatt Zeile für Zeile.
# schreibe nun eine Funktion zur zuweisung Ferien. WENN groesser als start UND kleiner als
# ende, DANN schreibe ein 1
for (i in 1:nrow(schulferien)) {
$Ferien[depo$Datum >= schulferien[i, "Start"] & depo$Datum <= schulferien[i, "Ende"]] <- 1
depo
}$Ferien[is.na(depo$Ferien)] <- 0
depo
# als faktor speichern
$Ferien <- factor(depo$Ferien) depo
# Fuer einige Auswertungen muss auf die Stunden als nummerischer Wert zurueckgegriffen werden
$Stunde <- hour(depo$Datetime)
depo# hour gibt uns den integer
typeof(depo$Stunde)
Die Daten wurden durch den WPZ kalibriert (Kommastellen).
Rundet sie auf 0 Nachkommastellen (Ganzzahl; unser Modell kann nicht mit Kommazahlen in der ahbängigen Variable umgehen). Der Befeht lautet round()
Definiert sie sicherheitshalber als Integer
Macht das für IN, OUT und Total.
$Total <- round(depo$Total, digits = 0)
depo$Fuss_IN <- round(depo$Fuss_IN, digits = 0)
depo$Fuss_OUT <- round(depo$Fuss_OUT, digits = 0) depo
Wir setzen den Fokus unserer Untersuchung auf die Veränderung der Besuchszahlen in der Abend- und Morgendämmerung sowie der Nacht. Dafür müssen wir diese tageszeitliche Einteilung der Daten erst machen. Da dies über den Umfang dieser Fallstudie geht, liefere ich euch hier den Code dazu.
Die wichtigsten Punkte:
Die Tageslänge wurde für den Standort Zürich (Zeitzone CET) mit dem Package “suncalc” berechnet. Dabei wurden Sommer- und Winterzeit berücksichtigt.
Die Einteilung der Tageszeit beruht auf dem Start und dem Ende der astronomischen Dämmerung sowie der Golden Hour. Der Morgen und der Abend wurden nach dieser Definition berechnet und um je eine Stunde Richtung Tag verlängert.
Untenstehenden Code könnt ihr einfach kopieren.
Hinweis: damit case_when() funktioniert, müsst ihr dplyr Version als 1.1.1 oder neuer haben.
# Einteilung Standort Zuerich
<- 47.38598
Latitude <- 8.50806
Longitude
# Start und das Ende der Sommerzeit:
# https://www.schulferien.org/schweiz/zeit/zeitumstellung/
# Welche Zeitzone haben wir eigentlich?
# Switzerland uses Central European Time (CET) during the winter as standard time,
# which is one hour ahead of Coordinated Universal Time (UTC+01:00), and
# Central European Summer Time (CEST) during the summer as daylight saving time,
# which is two hours ahead of Coordinated Universal Time (UTC+02:00).
# https://en.wikipedia.org/wiki/Time_in_Switzerland
# Was sind Astronomische Dämmerung und Golden Hour ueberhaupt?
# https://sunrisesunset.de/sonne/schweiz/zurich-kreis-1-city/
# https://www.rdocumentation.org/packages/suncalc/versions/0.5.0/topics/getSunlightTimes
# Wir arbeiten mit folgenden Variablen:
# "nightEnd" : night ends (morning astronomical twilight starts)
# "goldenHourEnd" : morning golden hour (soft light, best time for photography) ends
# "goldenHour" : evening golden hour starts
# "night" : night starts (dark enough for astronomical observations)
<-
lumidata getSunlightTimes(
date = seq.Date(depo_start, depo_end, by = 1),
keep = c("nightEnd", "goldenHourEnd", "goldenHour", "night"),
lat = Latitude,
lon = Longitude,
tz = "CET"
|>
) as_tibble()
# jetzt haben wir alle noetigen Angaben zu Sonnenaufgang, Tageslaenge usw.
# diese Angaben koennen wir nun mit unseren Zaehldaten verbinden:
<- depo |>
depo left_join(lumidata, by = c(Datum = "date"))
<- depo |>
depo mutate(Tageszeit = case_when(
>= nightEnd & Datetime <= goldenHourEnd ~ "Morgen",
Datetime > goldenHourEnd & Datetime < goldenHour ~ "Tag",
Datetime >= goldenHour & Datetime <= night ~ "Abend",
Datetime .default = "Nacht"
|>
)) mutate(Tageszeit = factor(Tageszeit, levels = c("Morgen", "Tag", "Abend", "Nacht"), ordered = TRUE))
# behalte die relevanten Var
<- depo |> dplyr::select(-nightEnd, -goldenHourEnd, -goldenHour, -night, -lat, -lon)
depo
# Plotte zum pruefn ob das funktioniert hat
ggplot(depo, aes(y = Datetime, color = Tageszeit, x = Stunde)) +
geom_jitter() +
scale_color_manual(values = mycolors)
sum(is.na(depo))
# bei mir hat der Zusatz der Tageszeit noch zu einigen NA-Wertren gefueht.
# Diese loesche ich einfach:
<- na.omit(depo)
depo # hat das funktioniert?
sum(is.na(depo))
Unsere Daten liegen im Stundenformat vor. Für einige Auswertungen müssen wir aber auf ganze Tage zurückgreifen können.
Tipp: Wenn man die Convinience Variablen als grouping variable einspeisst, dann werden sie in das neue df übernommen und müssen nicht nochmals hinzugefügt werden
<- depo |>
depo_d group_by(VARIABLE1, VARIABLE2, ...) |> # Gruppieren nach den Variablen
summarise(Total = sum(Fuss_IN + Fuss_OUT),# Berechnen der gewünschten Werte
Fuss_IN = sum(Fuss_IN),
...
# hier werden also pro Nutzergruppe und Richtung die Stundenwerte pro Tag aufsummiert
<- depo |>
depo_d group_by(Datum, Wochentag, Wochenende, KW, Monat, Jahr, Phase) |>
summarise(
Total = sum(Fuss_IN + Fuss_OUT),
Fuss_IN = sum(Fuss_IN),
Fuss_OUT = sum(Fuss_OUT)
)# Wenn man die Convinience Variablen als grouping variable einspeisst, dann werden sie in
# das neue df uebernommen und muessen nicht nochmals hinzugefuegt werden
# pruefe das df
head(depo_d)
# nun gruppieren wir nicht nur nach Tag sondern auch noch nach Tageszeit
<- depo |>
depo_daytime group_by(Jahr, Monat, KW, Phase, Ferien, Wochenende, Tageszeit) |>
summarise(
Total = sum(Fuss_IN + Fuss_OUT),
Fuss_IN = sum(Fuss_IN),
Fuss_OUT = sum(Fuss_OUT))
<- depo_daytime |>
mean_phase_d group_by(Phase, Tageszeit) |>
summarise(
Total = mean(Total),
IN = mean(Fuss_IN),
OUT = mean(Fuss_OUT))
Tipp: Braucht wiederum group_by() und summarise(). Nun brauchen wir nur noch das Total, keine Richtungstrennung mehr.
<- depo |>
depo_m group_by(Jahr, Monat) |>
summarise(Total = sum(Total))
<- depo_m |>
depo_m mutate(
Ym = paste(Jahr, Monat), # und mache eine neue Spalte, in der Jahr und
Ym = lubridate::ym(Ym)
# formatiere als Datum )
# Gruppiere die Werte nach Monat und TAGESZEIT
<- depo |>
depo_m_daytime group_by(Jahr, Monat, Tageszeit) |>
summarise(Total = sum(Total))
# sortiere das df aufsteigend (nur das es sicher stimmt)
<- depo_m_daytime |>
depo_m_daytime mutate(
Ym = paste(Jahr, Monat), # und mache eine neue Spalte, in der Jahr und
Ym = lubridate::ym(Ym)
# formatiere als Datum )
Macht euch mit den Daten vertraut. Plottet sie, seht euch die df’s an, versteht, was sie representieren.
Z.B. sind folgende Befehle und Plots wichtig:
str()
summarize()
head()
Scatterplot, x = Datum, y = Anzahl pro Zeiteinheit
Histrogram
usw.
Hinweis: Geht noch nicht zu weit mit euren Plots. Die Idee ist, dass man sich einen Überblick über die Daten verschafft und noch keine “analysierenden” Plots erstellt.
Nachdem nun alle Daten vorbereitet sind folgt im nächsten Schritt die deskriptive Analyse.