Arbeiten mit Zeitreihen

Zeitreihen

Begriff der Zeitreihe

Eine ’‘’Zeitreihe’’’ ist eine zeitlich geordnete Folge von Beobachtungen, bei der sich die Anordnung der Merkmalsausprägungen zwingend aus dem Zeitablauf ergibt (etwa Aktienkurse, Bevölkerungsentwicklung, Wetterdaten).

Die einzelnen Zeitpunkte werden zu einer Menge von Beobachtungszeitpunkten \(T\) zusammengefasst, bei der für jeden Zeitpunkt \(t \in T\) genau eine Beobachtung vorliegt. Zeitreihen treten in allen Bereichen der Wissenschaft auf.

Quelle: https://de.wikipedia.org/wiki/Zeitreihenanalyse

Zeitreihen in R

Das R-Paket tsibble stellt eine Datenstruktur (das tsibble - wie tibble aus dem tidyverserse wobei ts für Time Series steht) sowie verschiedene Funktionen bereit, die das Arbeiten mit Zeitreihen vereinfachen.

Datenstruktur tsibble

  • Gehört zu den Paketen aus dem tidyverts

  • Spalte mit Zeit wird als Index bezeichnet

  • Link zur Einführung

  • Normalen Dataframe mit as_tsibble in tsibble konvertieren

    • Zeitpunkte dürfen nicht doppelt vorkommen
    • Angeben, welche Spalte Index sein soll

Winddaten vom Deutschen Wetterdienst

Übersicht Stationen

data("metaIndex")
d_stationen <- metaIndex |> 
  filter(var == "wind", res == "10_minutes") |>
  mutate(Dauer = round(as.numeric(bis_datum -von_datum) / 365)) |>
  arrange(desc(Dauer)) |>
  select(von_datum, bis_datum, Dauer, Stationsname, Bundesland) |>
  as_tibble()
d_stationen
# A tibble: 903 × 5
   von_datum  bis_datum  Dauer Stationsname          Bundesland        
   <date>     <date>     <dbl> <chr>                 <chr>             
 1 1988-10-14 2025-08-18    37 Weissenburg-Emetzheim Bayern            
 2 1988-10-14 2025-08-18    37 Weissenburg-Emetzheim Bayern            
 3 1988-10-14 2025-08-18    37 Weissenburg-Emetzheim Bayern            
 4 1989-03-06 2025-08-18    36 Muehldorf             Bayern            
 5 1989-03-06 2025-08-18    36 Muehldorf             Bayern            
 6 1989-03-06 2025-08-18    36 Muehldorf             Bayern            
 7 1991-01-01 2025-08-17    35 Alsfeld               Hessen            
 8 1991-01-01 2025-08-17    35 Alsfeld               Hessen            
 9 1991-01-01 2025-08-17    35 Alsfeld               Hessen            
10 1991-01-01 2025-08-18    35 Freudenstadt          Baden-Wuerttemberg
# ℹ 893 more rows

Daten herunterladen und Zwischenspeichern

fname <- "daten/d_wind_raw.RData"
if (!file.exists(fname)) {
  d_wind_raw <- selectDWD(
    name = "Helgoland", res = "10_minutes", var = "wind", per = "historical", current = TRUE
  ) |>
  dataDWD() |>
  bind_rows()
  save(d_wind_raw, file = fname)
} else {
  load(file = fname)
}
Warning: .main -> execute -> knitr::knit -> process_file -> xfun:::handle_error
-> process_group -> call_block -> block_exec -> evaluate -> evaluate::evaluate
-> withRestarts -> withRestartList -> withOneRestart -> doWithOneRestart ->
withRestartList -> withOneRestart -> doWithOneRestart -> with_handlers -> eval
-> eval -> bind_rows -> list2 -> dataDWD -> selectDWD -> createIndex -> sub ->
is.factor -> indexFTP -> dirDWD -> locdir: '~/DWDdata' does not exist, using
tempdir() now.
Warning: .main -> execute -> knitr::knit -> process_file -> xfun:::handle_error
-> process_group -> call_block -> block_exec -> evaluate -> evaluate::evaluate
-> withRestarts -> withRestartList -> withOneRestart -> doWithOneRestart ->
withRestartList -> withOneRestart -> doWithOneRestart -> with_handlers -> eval
-> eval -> bind_rows -> list2 -> dataDWD -> selectDWD -> createIndex -> dirDWD
-> locdir: '~/DWDdata' does not exist, using tempdir() now.
Warning: .main -> execute -> knitr::knit -> process_file -> xfun:::handle_error
-> process_group -> call_block -> block_exec -> evaluate -> evaluate::evaluate
-> withRestarts -> withRestartList -> withOneRestart -> doWithOneRestart ->
withRestartList -> withOneRestart -> doWithOneRestart -> with_handlers -> eval
-> eval -> bind_rows -> list2 -> dataDWD -> locdir: '~/DWDdata' does not exist,
using tempdir() now.
rm(fname)
d_wind_raw |> as_tibble()
# A tibble: 1,454,121 × 6
   STATIONS_ID MESS_DATUM             QN FF_10 DD_10 eor  
         <int> <dttm>              <int> <dbl> <int> <fct>
 1        2115 1996-12-19 10:40:00     1  15.8    70 eor  
 2        2115 1996-12-19 10:50:00     1  16.2    70 eor  
 3        2115 1996-12-19 11:00:00     1  16.6    70 eor  
 4        2115 1996-12-19 11:10:00     1  16      80 eor  
 5        2115 1996-12-19 11:20:00     1  16.3    70 eor  
 6        2115 1996-12-19 11:30:00     1  16.3    70 eor  
 7        2115 1996-12-19 11:50:00     1  16.8    80 eor  
 8        2115 1996-12-19 12:00:00     1   9.4    80 eor  
 9        2115 1996-12-19 12:10:00     1  15.2    80 eor  
10        2115 1996-12-19 12:20:00     1  16.2    80 eor  
# ℹ 1,454,111 more rows

Winddaten aufbereiten und in tsibble konvertieren

d_wind_raw_ts <- d_wind_raw |>
  select(Station = STATIONS_ID, Datum = MESS_DATUM, Geschwindigkeit = FF_10) |>
  distinct(Datum, .keep_all = TRUE) |>
  as_tsibble(key = Station, index = Datum)
d_wind_raw_ts
# A tsibble: 1,454,109 x 3 [10m] <GMT>
# Key:       Station [1]
   Station Datum               Geschwindigkeit
     <int> <dttm>                        <dbl>
 1    2115 1996-12-19 10:40:00            15.8
 2    2115 1996-12-19 10:50:00            16.2
 3    2115 1996-12-19 11:00:00            16.6
 4    2115 1996-12-19 11:10:00            16  
 5    2115 1996-12-19 11:20:00            16.3
 6    2115 1996-12-19 11:30:00            16.3
 7    2115 1996-12-19 11:50:00            16.8
 8    2115 1996-12-19 12:00:00             9.4
 9    2115 1996-12-19 12:10:00            15.2
10    2115 1996-12-19 12:20:00            16.2
# ℹ 1,454,099 more rows

\(\rightarrow\) Zusatzinformation zu Index und Schlüssel

Plot der Rohdaten

ggplot(data = d_wind_raw_ts) + 
  geom_line(mapping = aes(x = Datum, y = Geschwindigkeit))

\(\rightarrow\) Es gibt Lücken im Datensatz, erste Lücke mit Linie verbunden!

Lücken listen mit count_gaps

d_wind_raw_ts |> count_gaps() 
# A tibble: 2,865 × 4
   Station .from               .to                    .n
     <int> <dttm>              <dttm>              <int>
 1    2115 1996-12-19 11:40:00 1996-12-19 11:40:00     1
 2    2115 1996-12-19 13:00:00 1996-12-19 13:00:00     1
 3    2115 1996-12-19 13:20:00 1996-12-19 13:20:00     1
 4    2115 1996-12-19 15:00:00 1996-12-19 15:00:00     1
 5    2115 1996-12-19 15:50:00 1996-12-19 15:50:00     1
 6    2115 1996-12-19 16:40:00 1996-12-19 16:50:00     2
 7    2115 1996-12-19 17:50:00 1996-12-19 17:50:00     1
 8    2115 1996-12-19 19:10:00 1996-12-19 19:10:00     1
 9    2115 1996-12-19 20:00:00 1996-12-19 20:00:00     1
10    2115 1996-12-19 20:40:00 1996-12-19 20:40:00     1
# ℹ 2,855 more rows

Lücken auffüllen mit fill_gaps

d_wind <- d_wind_raw_ts |> fill_gaps()
d_wind 
# A tsibble: 1,474,496 x 3 [10m] <GMT>
# Key:       Station [1]
   Station Datum               Geschwindigkeit
     <int> <dttm>                        <dbl>
 1    2115 1996-12-19 10:40:00            15.8
 2    2115 1996-12-19 10:50:00            16.2
 3    2115 1996-12-19 11:00:00            16.6
 4    2115 1996-12-19 11:10:00            16  
 5    2115 1996-12-19 11:20:00            16.3
 6    2115 1996-12-19 11:30:00            16.3
 7    2115 1996-12-19 11:40:00            NA  
 8    2115 1996-12-19 11:50:00            16.8
 9    2115 1996-12-19 12:00:00             9.4
10    2115 1996-12-19 12:10:00            15.2
# ℹ 1,474,486 more rows

\(\rightarrow\) Es gibt alle 10 Minuten eine Beobachtung, 20387 neue Zeilen, Werte mit NA gekennzeichnet. Damit: Einfacheres Arbeiten!

Plot

ggplot(d_wind) + 
  geom_line(mapping = aes(x = Datum, y = Geschwindigkeit))

\(\rightarrow\) Lücken werden als Lücken dargestellt

Starkwindereignisse

Ereignis: Windgeschwindigkeit innerhalb von 2 Stunden nicht unter 10 m/s und mindestens einmal über 20 m/s (zu Demonstrationszwecken gewählt, kein Kriterium des DWD)

d_wind_stark <- d_wind |>
  mutate(
    v_min_120 = roll_minr(Geschwindigkeit, n = 12, na.rm = TRUE),
    v_max_120 = roll_maxr(Geschwindigkeit, n = 12, na.rm = TRUE)
  ) |>
  filter(v_min_120 >= 10, v_max_120 >= 20)
d_wind_stark 
# A tsibble: 10,512 x 5 [10m] <GMT>
# Key:       Station [1]
   Station Datum               Geschwindigkeit v_min_120 v_max_120
     <int> <dttm>                        <dbl>     <dbl>     <dbl>
 1    2115 1996-12-19 22:00:00            20.5      18.5      20.5
 2    2115 1996-12-19 22:10:00            20.2      18.5      20.5
 3    2115 1996-12-19 22:20:00            19.5      18.5      20.5
 4    2115 1996-12-19 22:30:00            20.2      18.5      20.5
 5    2115 1996-12-19 22:40:00            20        18.5      20.5
 6    2115 1996-12-19 22:50:00            NA        18.8      20.5
 7    2115 1996-12-19 23:00:00            NA        18.9      20.5
 8    2115 1996-12-19 23:10:00            20.9      19.3      20.9
 9    2115 1996-12-19 23:20:00            NA        19.5      20.9
10    2115 1996-12-19 23:30:00            NA        19.5      20.9
# ℹ 10,502 more rows

\(\rightarrow\) Funktionen roll_minr und roll_maxr suchen aus n = 12 Werten (aktuell und 11 vorangegangene) kleinsten und größten Wert heraus. Analog für Summe, Mittelwert und so weiter.

Plot

ggplot(data = d_wind_stark) +
  geom_line(mapping = aes(x = Datum, y = v_min_120))

\(\rightarrow\) Einzelne Ereignisse sind nicht zu unterscheiden!

Ereignisse gruppieren

d_wind_stark_g <- d_wind_stark |>
  mutate(
    neue_gruppe = Datum - lag(Datum, default = ymd_hms("19700101000000")) > 10,
    gruppe = cumsum(neue_gruppe)
  )
d_wind_stark_g 
# A tsibble: 10,512 x 7 [10m] <GMT>
# Key:       Station [1]
   Station Datum               Geschwindigkeit v_min_120 v_max_120 neue_gruppe
     <int> <dttm>                        <dbl>     <dbl>     <dbl> <lgl>      
 1    2115 1996-12-19 22:00:00            20.5      18.5      20.5 TRUE       
 2    2115 1996-12-19 22:10:00            20.2      18.5      20.5 FALSE      
 3    2115 1996-12-19 22:20:00            19.5      18.5      20.5 FALSE      
 4    2115 1996-12-19 22:30:00            20.2      18.5      20.5 FALSE      
 5    2115 1996-12-19 22:40:00            20        18.5      20.5 FALSE      
 6    2115 1996-12-19 22:50:00            NA        18.8      20.5 FALSE      
 7    2115 1996-12-19 23:00:00            NA        18.9      20.5 FALSE      
 8    2115 1996-12-19 23:10:00            20.9      19.3      20.9 FALSE      
 9    2115 1996-12-19 23:20:00            NA        19.5      20.9 FALSE      
10    2115 1996-12-19 23:30:00            NA        19.5      20.9 FALSE      
# ℹ 10,502 more rows
# ℹ 1 more variable: gruppe <int>

\(\rightarrow\) Neue Gruppe falls Abstand zwischen Beobachtungen größer 10 Minuten. Beim Zusammenzählen mit cumsum wird FALSE=0 und TRUE=1 verwendet.

Plotten mit Gruppe

ggplot(data = d_wind_stark_g) +
  geom_line(mapping = aes(x = Datum, y = Geschwindigkeit, group = gruppe))

Ereignisse zusammenfassen

d_starkwindereignisse <- d_wind_stark_g |> 
  as_tibble() |>
  group_by(gruppe) |>
  summarise(
    start = min(Datum),
    dauer = max(Datum) - min(Datum),
    v_max = max(v_max_120)
  )
d_starkwindereignisse 
# A tibble: 322 × 4
   gruppe start               dauer      v_max
    <int> <dttm>              <drtn>     <dbl>
 1      1 1996-12-19 22:00:00 16200 secs  20.9
 2      2 1997-02-04 22:10:00  7200 secs  20.6
 3      3 1997-02-18 17:00:00  8400 secs  21.3
 4      4 1997-02-21 10:50:00  9000 secs  20.2
 5      5 1997-02-25 03:00:00 12600 secs  22.3
 6      6 1997-04-05 21:10:00  7800 secs  20.3
 7      7 1997-04-11 08:50:00  7200 secs  20.5
 8      8 1997-04-11 11:40:00  9000 secs  21.6
 9      9 1997-04-15 13:50:00  1800 secs  36.3
10     10 1997-09-09 13:00:00  7800 secs  20.8
# ℹ 312 more rows

\(\rightarrow\) Mit as_tibble wieder in ‘normalen’ Dataframe umwandeln damit group_by funktioniert wie gewohnt

Histogramm Windgeschwindigkeiten

ggplot(data = d_starkwindereignisse) +
  geom_histogram(mapping = aes(x = v_max), binwidth = 0.5)

\(\rightarrow\) Orkan Oratia

Histogramm Dauer

ggplot(data = d_starkwindereignisse) +
  geom_histogram(mapping = aes(x = dauer), binwidth = 3600) +
  scale_x_time()