8. Data processing

Foreword on working directory, data and packages

The Data for this tutorial are provided via Github. In order to reproudce the code with your own data, replace the url with your local filepath

If you work locally, you may define the working directory

setwd("YOUR/FILEPATH") 
# this will not work. if you want to use it, define a path on your computer

This should be the place where your data and other folders are stored. more Information can be found here

Code chunks for saving data and results are disabeled in this tutorial (using a #). Please replace (“YOUR/FILEPATH”) with your own path.

The code is written in such a way, that packages will be automatically installed (if not already installed) and loaded. Packages will be stored in the default location of your computer.

Einführung

Das Paket “dplyr” bringt für die Datenaufbereitung eine Reihe nützlicher Funktionen mit, um Daten umzuformen, zu prüfen, zu bereinigen, zu gruppieren oder zusammenzufassen. Es bietet somit sinnvolle Funktionen als Voraussetzung für die eigentliche Datenanalyse. Typische Probleme die im Rahmen der Datenaufbereitung adressiert werden:

  • Fehlende Werte, z.B. keine Angabe NA
  • Unerwartete Daten, z.B. falscher Datentyp
  • Umformung der Daten, z.B. Zusammenführung von zwei Tabellen oder Transposition einer Tabelle
  • Neue Variablen (Spalten) berechnen, z.B. die Summe von Werten aus anderen Spalten

Das Paket “dyplr” ist im Paket “tidyverse” enthalten, das noch eine Reihe weiterer Pakete, wie z.B. ggplot2 oder “lubridate”, mitbringt (s. “tidyverse_packages()”).

if (!require(tibble)){install.packages("tibble"); library(tibble)}
## Warning: Paket 'tibble' wurde unter R Version 3.6.2 erstellt
if (!require(lubridate)){install.packages("lubridate"); library(lubridate)}
## Warning: Paket 'lubridate' wurde unter R Version 3.6.3 erstellt
if (!require(tidyr)){install.packages("tidyr"); library(tidyr)}
## Warning: Paket 'tidyr' wurde unter R Version 3.6.3 erstellt
if (!require(ggplot2)){install.packages("ggplot2"); library(ggplot2)}
## Warning: Paket 'ggplot2' wurde unter R Version 3.6.2 erstellt
if (!require(dplyr)){install.packages("dplyr"); library(dplyr)}
## Warning: Paket 'dplyr' wurde unter R Version 3.6.2 erstellt
if (!require(nycflights13)){install.packages("nycflights13"); library(nycflights13)}  #load flights data
## Warning: Paket 'nycflights13' wurde unter R Version 3.6.3 erstellt

Der Grundgedanken von “dyplyr”

Die Grundidee von “dyplyr” basiert darauf, dass komplexe Datenanalysen nach dem Lego-Prinzip in einzelne einfache Bausteine zerlegt werden. Darüber hinaus ist ein Grundprinzip des Paketes, dass alle Operationen ein Dataframe (tibble) erwarten (andere Datenstrukturen werden nicht akzeptiert) und wiederum ein solches weiterreichen. Somit können Dataframes entlang einer Arbeitskette, ähnlich einem Fließband, mit spezifischen Operationen bearbeitet werden. Für die lineare Aneinanderreihung der einzelnen Operationen kann mit dem Pipe-Befehl “%>%” schließlich eine Sequenz von auf die Dataframes angewandte Befehle konstruieren werden.

Daten Bereinigung mittels “tidy”

Hier ist ein Beispiel, wie die Funktion gather() auf einem erfundenen Datensatz angewendet werden kann. In diesem Experiment haben drei Menschen zwei verschiedene Medikamente bekommen und ihre Herzfrequenz wurde aufgezeichnet (vgl. https://blog.rstudio.com/2014/07/22/introducing-tidyr/):

messy <- data.frame(
  name = c("Wilbur", "Petunia", "Gregory"),
  a = c(67, 80, 64),
  b = c(56, 90, 50)
)

Es existieren drei Variablen (Name, Medikament und Herzschlag), aber nur der Name steht derzeit in einer Spalte. Mit gather() kann die Spalte “a” und “b” entsprechend der Werte der Variablen “Medikament” und “Herzschlag” zusammengefasst werden:

gather(messy, drug, heartrate, a:b)
##      name drug heartrate
## 1  Wilbur    a        67
## 2 Petunia    a        80
## 3 Gregory    a        64
## 4  Wilbur    b        56
## 5 Petunia    b        90
## 6 Gregory    b        50

Manchmal werden zwei Variablen in einer Spalte zusammengefasst. Die Funktion separate() erlaubt es, sie zu trennen. Wir haben Daten, die angeben wie viel Zeit Menschen mit Telefonieren verbringen, gemessen an zwei Standorten (Arbeit und Zuhause). Jede Person wurde nach dem Zufallsprinzip entweder einer Untersuchungsgruppe oder einer Kontrollgruppe zugeordnet.

set.seed(10)
messy <- data.frame(
  id = 1:4,
  trt = sample(rep(c('control', 'treatment'), each = 2)),
  work.T1 = runif(4),
  home.T1 = runif(4),
  work.T2 = runif(4),
  home.T2 = runif(4)
)

Um diese Daten zu ordnen, wird zunächst wieder die Funktion gather() verwendet, sodass die Spalten “work.T1”, “home.T1”, “work.T1”, “work.T2” und “home.T2” entsprechend der Variabeln “Schlüssel” und “Zeit” sortiert sind.

tidier <- gather(messy,key, time, -id, -trt)

head(tidier, 8)
##   id       trt     key       time
## 1  1 treatment work.T1 0.08513597
## 2  2   control work.T1 0.22543662
## 3  3   control work.T1 0.27453052
## 4  4 treatment work.T1 0.27230507
## 5  1 treatment home.T1 0.61582931
## 6  2   control home.T1 0.42967153
## 7  3   control home.T1 0.65165567
## 8  4 treatment home.T1 0.56773775

Als nächstes verwenden wir die Funktion separate(), um die Einträge zu der Spalte “Schlüssel” in Ort und Zeit aufzuteilen, wobei wir einen regulären Ausdruck verwenden, um das Zeichen zu beschreiben, das sie trennt.

tidy <- separate(tidier, key, into = c("location", "time"), sep = "\\.")
head(tidy,8)
##   id       trt location time
## 1  1 treatment     work   T1
## 2  2   control     work   T1
## 3  3   control     work   T1
## 4  4 treatment     work   T1
## 5  1 treatment     home   T1
## 6  2   control     home   T1
## 7  3   control     home   T1
## 8  4 treatment     home   T1

Als weiteres Beispiel der Datenaufbereitung wird ein Datensatz aus der PanTHERIA-Datenbank (s. Ecology 90:2648. http://esapubs.org/archive/ecol/E090/184/) verwendet. Zur ersten Bereinigung und Aufbereitung der Daten werden in diesem Fall folgende Befehlen verwendet. Machen Sie sich bewusst, was in der folgende Codezeilen ausgeführt wird indem Sie die einzelnen Funktionen recherchieren.

urlfile <- "https://raw.githubusercontent.com/RafHo/teaching/master/angewandte_geodatenverarbeitung/datasource/PanTHERIA_1-0_WR05_Aug2008.txt.txt" # get url from github repositury

mammals <- read.table(urlfile, sep = "\t", header = TRUE, stringsAsFactors = FALSE)

names(mammals) <- sub("X[0-9._]+", "", names(mammals))
names(mammals) <- sub("MSW05_", "", names(mammals))
mammals <- select(mammals, Order, Binomial, AdultBodyMass_g, AdultHeadBodyLen_mm, HomeRange_km2, LitterSize)
names(mammals) <- gsub("([A-Z])", "_\\L\\1", names(mammals), perl = TRUE)
names(mammals) <- gsub("^_", "", names(mammals), perl = TRUE)
mammals[mammals == -999] <- NA
names(mammals)[names(mammals) == "binomial"] <- "species"
mammals <- tbl_df(mammals) # for prettier printing

Die Daten geladenen Daten erkunden

Dataframes sehen bei der Erkundung mit “dplyr” etwas anders aus. Ein Dataframe kann in der Konsole einfach mit seinem Namen aufgerufen werden:

mammals
## # A tibble: 5,416 x 6
##    order  species   adult_body_mass~ adult_head_body~ home_range_km2 litter_size
##    <chr>  <chr>                <dbl>            <dbl>          <dbl>       <dbl>
##  1 Artio~ Camelus ~          492714.              NA          196.          0.98
##  2 Carni~ Canis ad~           10392.             745.           1.01        4.5 
##  3 Carni~ Canis au~            9659.             828.           2.95        3.74
##  4 Carni~ Canis la~           11989.             872.          18.9         5.72
##  5 Carni~ Canis lu~           31757.            1055          160.          4.98
##  6 Artio~ Bos fron~          800143.            2700           NA           1.22
##  7 Artio~ Bos grun~          500000               NA           NA           1   
##  8 Artio~ Bos java~          635974.            2075           NA           1.22
##  9 Prima~ Calliceb~            1117.             355.          NA           1.01
## 10 Prima~ Calliceb~              NA               NA           NA          NA   
## # ... with 5,406 more rows

Mit der Funktion glimpse des Paketes “tibble” (tidyverse) kann er auch transponiert betrachtet werden:

glimpse(mammals)
## Observations: 5,416
## Variables: 6
## $ order                  <chr> "Artiodactyla", "Carnivora", "Carnivora", "C...
## $ species                <chr> "Camelus dromedarius", "Canis adustus", "Can...
## $ adult_body_mass_g      <dbl> 492714.47, 10392.49, 9658.70, 11989.10, 3175...
## $ adult_head_body_len_mm <dbl> NA, 745.32, 827.53, 872.39, 1055.00, 2700.00...
## $ home_range_km2         <dbl> 1.963200e+02, 1.010000e+00, 2.950000e+00, 1....
## $ litter_size            <dbl> 0.98, 4.50, 3.74, 5.72, 4.98, 1.22, 1.00, 1....

Spalten auswählen

Die Funktion select() ermöglicht es, eine Untermenge nach Spalten auszuwählen. Es funktioniert ähnlich der Basis-R-Funktion subset(), erlaubt aber auch eine ausgefallene Verwendung zusätzlicher Kriterien, wie z.B. contains(), starts_with() und ends_with():

select(mammals, adult_head_body_len_mm)
## # A tibble: 5,416 x 1
##    adult_head_body_len_mm
##                     <dbl>
##  1                    NA 
##  2                   745.
##  3                   828.
##  4                   872.
##  5                  1055 
##  6                  2700 
##  7                    NA 
##  8                  2075 
##  9                   355.
## 10                    NA 
## # ... with 5,406 more rows
select(mammals, adult_head_body_len_mm, litter_size)
## # A tibble: 5,416 x 2
##    adult_head_body_len_mm litter_size
##                     <dbl>       <dbl>
##  1                    NA         0.98
##  2                   745.        4.5 
##  3                   828.        3.74
##  4                   872.        5.72
##  5                  1055         4.98
##  6                  2700         1.22
##  7                    NA         1   
##  8                  2075         1.22
##  9                   355.        1.01
## 10                    NA        NA   
## # ... with 5,406 more rows
select(mammals, adult_head_body_len_mm:litter_size)
## # A tibble: 5,416 x 3
##    adult_head_body_len_mm home_range_km2 litter_size
##                     <dbl>          <dbl>       <dbl>
##  1                    NA          196.          0.98
##  2                   745.           1.01        4.5 
##  3                   828.           2.95        3.74
##  4                   872.          18.9         5.72
##  5                  1055          160.          4.98
##  6                  2700           NA           1.22
##  7                    NA           NA           1   
##  8                  2075           NA           1.22
##  9                   355.          NA           1.01
## 10                    NA           NA          NA   
## # ... with 5,406 more rows
select(mammals, -adult_head_body_len_mm)
## # A tibble: 5,416 x 5
##    order        species             adult_body_mass_g home_range_km2 litter_size
##    <chr>        <chr>                           <dbl>          <dbl>       <dbl>
##  1 Artiodactyla Camelus dromedarius           492714.         196.          0.98
##  2 Carnivora    Canis adustus                  10392.           1.01        4.5 
##  3 Carnivora    Canis aureus                    9659.           2.95        3.74
##  4 Carnivora    Canis latrans                  11989.          18.9         5.72
##  5 Carnivora    Canis lupus                    31757.         160.          4.98
##  6 Artiodactyla Bos frontalis                 800143.          NA           1.22
##  7 Artiodactyla Bos grunniens                 500000           NA           1   
##  8 Artiodactyla Bos javanicus                 635974.          NA           1.22
##  9 Primates     Callicebus cupreus              1117.          NA           1.01
## 10 Primates     Callicebus discolor               NA           NA          NA   
## # ... with 5,406 more rows
select(mammals, contains("body"))
## # A tibble: 5,416 x 2
##    adult_body_mass_g adult_head_body_len_mm
##                <dbl>                  <dbl>
##  1           492714.                    NA 
##  2            10392.                   745.
##  3             9659.                   828.
##  4            11989.                   872.
##  5            31757.                  1055 
##  6           800143.                  2700 
##  7           500000                     NA 
##  8           635974.                  2075 
##  9             1117.                   355.
## 10               NA                     NA 
## # ... with 5,406 more rows
select(mammals, starts_with("adult"))
## # A tibble: 5,416 x 2
##    adult_body_mass_g adult_head_body_len_mm
##                <dbl>                  <dbl>
##  1           492714.                    NA 
##  2            10392.                   745.
##  3             9659.                   828.
##  4            11989.                   872.
##  5            31757.                  1055 
##  6           800143.                  2700 
##  7           500000                     NA 
##  8           635974.                  2075 
##  9             1117.                   355.
## 10               NA                     NA 
## # ... with 5,406 more rows
select(mammals, ends_with("g"))
## # A tibble: 5,416 x 1
##    adult_body_mass_g
##                <dbl>
##  1           492714.
##  2            10392.
##  3             9659.
##  4            11989.
##  5            31757.
##  6           800143.
##  7           500000 
##  8           635974.
##  9             1117.
## 10               NA 
## # ... with 5,406 more rows
select(mammals, 1:3)
## # A tibble: 5,416 x 3
##    order        species             adult_body_mass_g
##    <chr>        <chr>                           <dbl>
##  1 Artiodactyla Camelus dromedarius           492714.
##  2 Carnivora    Canis adustus                  10392.
##  3 Carnivora    Canis aureus                    9659.
##  4 Carnivora    Canis latrans                  11989.
##  5 Carnivora    Canis lupus                    31757.
##  6 Artiodactyla Bos frontalis                 800143.
##  7 Artiodactyla Bos grunniens                 500000 
##  8 Artiodactyla Bos javanicus                 635974.
##  9 Primates     Callicebus cupreus              1117.
## 10 Primates     Callicebus discolor               NA 
## # ... with 5,406 more rows

Zeilen filtern

Die Funktion filter() ermöglicht es, nach Zeilen eine eine Teilmenge zu filtern. Alle gültigen logischen Operatoren können verwendet werden:

filter(mammals, adult_body_mass_g > 1e7)[ , 1:3]
## # A tibble: 12 x 3
##    order   species                adult_body_mass_g
##    <chr>   <chr>                              <dbl>
##  1 Cetacea Caperea marginata              32000000.
##  2 Cetacea Balaenoptera musculus         154321304.
##  3 Cetacea Balaenoptera physalus          47506008.
##  4 Cetacea Balaena mysticetus             79691179.
##  5 Cetacea Balaenoptera borealis          22106252.
##  6 Cetacea Balaenoptera edeni             20000000.
##  7 Cetacea Berardius bairdii              11380000.
##  8 Cetacea Eschrichtius robustus          27324024.
##  9 Cetacea Eubalaena australis            23000000.
## 10 Cetacea Eubalaena glacialis            23000000.
## 11 Cetacea Megaptera novaeangliae         30000000.
## 12 Cetacea Physeter catodon               14540960.
filter(mammals, species == "Balaena mysticetus")
## # A tibble: 1 x 6
##   order  species   adult_body_mass~ adult_head_body_~ home_range_km2 litter_size
##   <chr>  <chr>                <dbl>             <dbl>          <dbl>       <dbl>
## 1 Cetac~ Balaena ~        79691179.            12187.             NA           1
filter(mammals, order == "Carnivora" & adult_body_mass_g < 200)
## # A tibble: 3 x 6
##   order  species   adult_body_mass~ adult_head_body_~ home_range_km2 litter_size
##   <chr>  <chr>                <dbl>             <dbl>          <dbl>       <dbl>
## 1 Carni~ Mustela ~            180.               244.          NA           5.44
## 2 Carni~ Mustela ~            190.               229.           0.21        6.5 
## 3 Carni~ Mustela ~             78.4              188.           0.07        5.07

Zeilen anordnen

Mit der Funktion arrange() können Zeilen nach einer oder mehreren Spalten in aufsteigender oder absteigender Reihenfolge sortiert werden. Ich wähle die ersten drei Spalten nur aus, um die Ausgabe besser lesbar zu machen:

arrange(mammals, adult_body_mass_g)[ , 1:3]
## # A tibble: 5,416 x 3
##    order        species                     adult_body_mass_g
##    <chr>        <chr>                                   <dbl>
##  1 Chiroptera   Craseonycteris thonglongyai              1.96
##  2 Chiroptera   Kerivoula minuta                         2.03
##  3 Soricomorpha Suncus etruscus                          2.26
##  4 Soricomorpha Sorex minutissimus                       2.46
##  5 Soricomorpha Suncus madagascariensis                  2.47
##  6 Soricomorpha Crocidura lusitania                      2.48
##  7 Soricomorpha Crocidura planiceps                      2.5 
##  8 Chiroptera   Pipistrellus nanulus                     2.51
##  9 Soricomorpha Sorex nanus                              2.57
## 10 Soricomorpha Sorex arizonae                           2.7 
## # ... with 5,406 more rows
arrange(mammals, desc(adult_body_mass_g))[ , 1:3]
## # A tibble: 5,416 x 3
##    order   species                adult_body_mass_g
##    <chr>   <chr>                              <dbl>
##  1 Cetacea Balaenoptera musculus         154321304.
##  2 Cetacea Balaena mysticetus             79691179.
##  3 Cetacea Balaenoptera physalus          47506008.
##  4 Cetacea Caperea marginata              32000000.
##  5 Cetacea Megaptera novaeangliae         30000000.
##  6 Cetacea Eschrichtius robustus          27324024.
##  7 Cetacea Eubalaena australis            23000000.
##  8 Cetacea Eubalaena glacialis            23000000.
##  9 Cetacea Balaenoptera borealis          22106252.
## 10 Cetacea Balaenoptera edeni             20000000.
## # ... with 5,406 more rows
arrange(mammals, order, adult_body_mass_g)[ , 1:3]
## # A tibble: 5,416 x 3
##    order        species                adult_body_mass_g
##    <chr>        <chr>                              <dbl>
##  1 Afrosoricida Microgale pusilla                   3.4 
##  2 Afrosoricida Microgale parvula                   3.53
##  3 Afrosoricida Geogale aurita                      6.69
##  4 Afrosoricida Microgale fotsifotsy                7.7 
##  5 Afrosoricida Microgale longicaudata              8.08
##  6 Afrosoricida Microgale brevicaudata              8.99
##  7 Afrosoricida Microgale principula               10.2 
##  8 Afrosoricida Microgale drouhardi                10.5 
##  9 Afrosoricida Microgale cowani                   12.3 
## 10 Afrosoricida Microgale taiva                    12.4 
## # ... with 5,406 more rows

Spalten erstellen

Mit der Funktion mutate() können neue Spalten (Variablen) hinzufügt werden. Neu erstellte Spalten können auf der Grundlage schon bestehender Spalten erstellt bzw. errechnet werden. Zur besseren Sichtbarmachunug werden die erstelltebn Spalten mit glimpse() ausgeführt:

glimpse(mutate(mammals, adult_body_mass_kg = adult_body_mass_g / 1000))
## Observations: 5,416
## Variables: 7
## $ order                  <chr> "Artiodactyla", "Carnivora", "Carnivora", "C...
## $ species                <chr> "Camelus dromedarius", "Canis adustus", "Can...
## $ adult_body_mass_g      <dbl> 492714.47, 10392.49, 9658.70, 11989.10, 3175...
## $ adult_head_body_len_mm <dbl> NA, 745.32, 827.53, 872.39, 1055.00, 2700.00...
## $ home_range_km2         <dbl> 1.963200e+02, 1.010000e+00, 2.950000e+00, 1....
## $ litter_size            <dbl> 0.98, 4.50, 3.74, 5.72, 4.98, 1.22, 1.00, 1....
## $ adult_body_mass_kg     <dbl> 492.71447, 10.39249, 9.65870, 11.98910, 31.7...
glimpse(mutate(mammals, g_per_mm = adult_body_mass_g / adult_head_body_len_mm))
## Observations: 5,416
## Variables: 7
## $ order                  <chr> "Artiodactyla", "Carnivora", "Carnivora", "C...
## $ species                <chr> "Camelus dromedarius", "Canis adustus", "Can...
## $ adult_body_mass_g      <dbl> 492714.47, 10392.49, 9658.70, 11989.10, 3175...
## $ adult_head_body_len_mm <dbl> NA, 745.32, 827.53, 872.39, 1055.00, 2700.00...
## $ home_range_km2         <dbl> 1.963200e+02, 1.010000e+00, 2.950000e+00, 1....
## $ litter_size            <dbl> 0.98, 4.50, 3.74, 5.72, 4.98, 1.22, 1.00, 1....
## $ g_per_mm               <dbl> NA, 13.9436618, 11.6717219, 13.7428214, 30.1...
glimpse(mutate(mammals, g_per_mm = adult_body_mass_g / adult_head_body_len_mm, kg_per_mm = g_per_mm / 1000))
## Observations: 5,416
## Variables: 8
## $ order                  <chr> "Artiodactyla", "Carnivora", "Carnivora", "C...
## $ species                <chr> "Camelus dromedarius", "Canis adustus", "Can...
## $ adult_body_mass_g      <dbl> 492714.47, 10392.49, 9658.70, 11989.10, 3175...
## $ adult_head_body_len_mm <dbl> NA, 745.32, 827.53, 872.39, 1055.00, 2700.00...
## $ home_range_km2         <dbl> 1.963200e+02, 1.010000e+00, 2.950000e+00, 1....
## $ litter_size            <dbl> 0.98, 4.50, 3.74, 5.72, 4.98, 1.22, 1.00, 1....
## $ g_per_mm               <dbl> NA, 13.9436618, 11.6717219, 13.7428214, 30.1...
## $ kg_per_mm              <dbl> NA, 0.0139436618, 0.0116717219, 0.0137428214...

Spalten zusammenfassen und Datensätze gruppieren

Mit der Funktion summarise() können zusammenfassende Statistiken aus Spalten berechnet werden. Lediglich die Verwendung von summarise() ist nicht sehr nützlich, aber in Kombination mit `group_by()´ können Datenblöcke sinnvoll zusammengefasst bzw. gruppiert werden.

summarise(mammals, mean_mass = mean(adult_body_mass_g, na.rm = TRUE))
## # A tibble: 1 x 1
##   mean_mass
##       <dbl>
## 1   177810.
head(summarise(group_by(mammals, order), mean_mass = mean(adult_body_mass_g, na.rm = TRUE)))
## # A tibble: 6 x 2
##   order        mean_mass
##   <chr>            <dbl>
## 1 Afrosoricida      94.8
## 2 Artiodactyla  121329. 
## 3 Carnivora      47386. 
## 4 Cetacea      7373065. 
## 5 Chiroptera        57.7
## 6 Cingulata       4699.

Befehle aneinanderreihen

Sog. Pipes übernehmen die Ausgabe einer Funktion und leiten sie an das erste Argument der nächsten Funktion weiter. Das "magrittr Paket enthält die Pipe-Funktion %>% (s. ?magrittr::%>%).

Pipes können für die Aneinanderreihung fast jeder Funktionen verwendet werden:

x <- rnorm(10)

x %>% max()
## [1] 1.10178
#Das gleiche Resultat liefert:
max(x)
## [1] 1.10178

Nun wird das Dataframe der Säugetiere an das erste Argument der Funktion arrange übergeben und die Anordnung über die Spalte adult_body_mass_g gewählt:

mammals %>% arrange(adult_body_mass_g)
## # A tibble: 5,416 x 6
##    order  species   adult_body_mass~ adult_head_body~ home_range_km2 litter_size
##    <chr>  <chr>                <dbl>            <dbl>          <dbl>       <dbl>
##  1 Chiro~ Craseony~             1.96             31.0             NA        1   
##  2 Chiro~ Kerivoul~             2.03             NA               NA       NA   
##  3 Soric~ Suncus e~             2.26             NA               NA        4   
##  4 Soric~ Sorex mi~             2.46             52.0             NA        8.99
##  5 Soric~ Suncus m~             2.47             NA               NA       NA   
##  6 Soric~ Crocidur~             2.48             31.0             NA       NA   
##  7 Soric~ Crocidur~             2.5              NA               NA        3   
##  8 Chiro~ Pipistre~             2.51             NA               NA        1.96
##  9 Soric~ Sorex na~             2.57             52               NA        6.49
## 10 Soric~ Sorex ar~             2.7              58.0             NA       NA   
## # ... with 5,406 more rows
arrange(mammals, adult_body_mass_g) ### without pipe
## # A tibble: 5,416 x 6
##    order  species   adult_body_mass~ adult_head_body~ home_range_km2 litter_size
##    <chr>  <chr>                <dbl>            <dbl>          <dbl>       <dbl>
##  1 Chiro~ Craseony~             1.96             31.0             NA        1   
##  2 Chiro~ Kerivoul~             2.03             NA               NA       NA   
##  3 Soric~ Suncus e~             2.26             NA               NA        4   
##  4 Soric~ Sorex mi~             2.46             52.0             NA        8.99
##  5 Soric~ Suncus m~             2.47             NA               NA       NA   
##  6 Soric~ Crocidur~             2.48             31.0             NA       NA   
##  7 Soric~ Crocidur~             2.5              NA               NA        3   
##  8 Chiro~ Pipistre~             2.51             NA               NA        1.96
##  9 Soric~ Sorex na~             2.57             52               NA        6.49
## 10 Soric~ Sorex ar~             2.7              58.0             NA       NA   
## # ... with 5,406 more rows

Angenommen, die Art mit dem höchsten Verhältnis von Körpergewicht zu Länge wird gesucht:

#(Pfeife)
mammals %>%
  mutate(mass_to_length = adult_body_mass_g / adult_head_body_len_mm) %>%
  arrange(desc(mass_to_length)) %>%
  select(species, mass_to_length)
## # A tibble: 5,416 x 2
##    species                mass_to_length
##    <chr>                           <dbl>
##  1 Balaena mysticetus              6539.
##  2 Balaenoptera musculus           5063.
##  3 Megaptera novaeangliae          2334.
##  4 Eschrichtius robustus           2309.
##  5 Balaenoptera physalus           2302.
##  6 Elephas maximus                 1704.
##  7 Eubalaena glacialis             1654.
##  8 Eubalaena australis             1625.
##  9 Balaenoptera edeni              1444.
## 10 Balaenoptera borealis           1203.
## # ... with 5,406 more rows
#Das gleiche Resultat liefert (Matrjoschka) / without pipe:
select(
  arrange(
    mutate(mammals, mass_to_length = adult_body_mass_g / adult_head_body_len_mm),
    desc(mass_to_length)),
  species, mass_to_length)
## # A tibble: 5,416 x 2
##    species                mass_to_length
##    <chr>                           <dbl>
##  1 Balaena mysticetus              6539.
##  2 Balaenoptera musculus           5063.
##  3 Megaptera novaeangliae          2334.
##  4 Eschrichtius robustus           2309.
##  5 Balaenoptera physalus           2302.
##  6 Elephas maximus                 1704.
##  7 Eubalaena glacialis             1654.
##  8 Eubalaena australis             1625.
##  9 Balaenoptera edeni              1444.
## 10 Balaenoptera borealis           1203.
## # ... with 5,406 more rows

Mit Zeitreihen arbeiten

Auf den ersten Blick erscheinen Angaben zum Datum und der Uhrzeit simple. Man verwendet sie ständig im normalen Leben. Je mehr man sich jedoch mit ihnen auseinandersetzt, desto komplizierter scheinen sie zu werden, z.B.: * Hat jedes Jahr 365 Tage? * Hat jeder Tag 24 Stunden Zeit? * Hat jede Minute 60 Sekunden?

Das Paket “lubridate” hilft mit ebensolchen Besonderheiten umzugehen und standardisiert Datums- bzw. Zeitangaben.

Ein tibble Dataframe kennt drei Typen von Datums- bzw. Zeitangaben * Eine Datumsangabe (als gekennzeichnet) * Eine Zeitangabe (als

Um das aktuelle Datum oder die aktuelle Uhrzeit zu erhalten, kann today() oder now() verwendet werden:

today()
## [1] "2020-04-10"
now()
## [1] "2020-04-10 12:12:53 CEST"

Ansonsonsten existieren drei Möglichkeiten, um eine Datums- bzw. Uhrzeitsangabe zu erstellen: * Aus einer Zeichenkette. * Aus einzelnen Datum-Uhrzeit-Komponenten. * Aus einem vorhandenen Datum-Uhrzeit-Objekt.

#Aus einer Zeichenkette

ymd("2017-01-31")
## [1] "2017-01-31"
mdy("January 31st, 2017")
## [1] "2017-01-31"
dmy("31-Jan-2017")
## [1] "2017-01-31"
ymd(20170131)
## [1] "2017-01-31"
ymd_hms("2017-01-31 20:11:59")
## [1] "2017-01-31 20:11:59 UTC"
mdy_hm("01/31/2017 08:01")
## [1] "2017-01-31 08:01:00 UTC"
ymd(20170131, tz = "UTC")
## [1] "2017-01-31 UTC"
#Aus einzelnen Datum-Uhrzeit-Komponenten

flights %>% 
  select(year, month, day, hour, minute)
## # A tibble: 336,776 x 5
##     year month   day  hour minute
##    <int> <int> <int> <dbl>  <dbl>
##  1  2013     1     1     5     15
##  2  2013     1     1     5     29
##  3  2013     1     1     5     40
##  4  2013     1     1     5     45
##  5  2013     1     1     6      0
##  6  2013     1     1     5     58
##  7  2013     1     1     6      0
##  8  2013     1     1     6      0
##  9  2013     1     1     6      0
## 10  2013     1     1     6      0
## # ... with 336,766 more rows
flights %>% 
  select(year, month, day, hour, minute) %>% 
  mutate(departure = make_datetime(year, month, day, hour, minute))
## # A tibble: 336,776 x 6
##     year month   day  hour minute departure          
##    <int> <int> <int> <dbl>  <dbl> <dttm>             
##  1  2013     1     1     5     15 2013-01-01 05:15:00
##  2  2013     1     1     5     29 2013-01-01 05:29:00
##  3  2013     1     1     5     40 2013-01-01 05:40:00
##  4  2013     1     1     5     45 2013-01-01 05:45:00
##  5  2013     1     1     6      0 2013-01-01 06:00:00
##  6  2013     1     1     5     58 2013-01-01 05:58:00
##  7  2013     1     1     6      0 2013-01-01 06:00:00
##  8  2013     1     1     6      0 2013-01-01 06:00:00
##  9  2013     1     1     6      0 2013-01-01 06:00:00
## 10  2013     1     1     6      0 2013-01-01 06:00:00
## # ... with 336,766 more rows
make_datetime_100 <- function(year, month, day, time) {
  make_datetime(year, month, day, time %/% 100, time %% 100)
}

flights_dt <- flights %>% 
  filter(!is.na(dep_time), !is.na(arr_time)) %>% 
  mutate(
    dep_time = make_datetime_100(year, month, day, dep_time),
    arr_time = make_datetime_100(year, month, day, arr_time),
    sched_dep_time = make_datetime_100(year, month, day, sched_dep_time),
    sched_arr_time = make_datetime_100(year, month, day, sched_arr_time)
  ) %>% 
  select(origin, dest, ends_with("delay"), ends_with("time"))

flights_dt
## # A tibble: 328,063 x 9
##    origin dest  dep_delay arr_delay dep_time            sched_dep_time     
##    <chr>  <chr>     <dbl>     <dbl> <dttm>              <dttm>             
##  1 EWR    IAH           2        11 2013-01-01 05:17:00 2013-01-01 05:15:00
##  2 LGA    IAH           4        20 2013-01-01 05:33:00 2013-01-01 05:29:00
##  3 JFK    MIA           2        33 2013-01-01 05:42:00 2013-01-01 05:40:00
##  4 JFK    BQN          -1       -18 2013-01-01 05:44:00 2013-01-01 05:45:00
##  5 LGA    ATL          -6       -25 2013-01-01 05:54:00 2013-01-01 06:00:00
##  6 EWR    ORD          -4        12 2013-01-01 05:54:00 2013-01-01 05:58:00
##  7 EWR    FLL          -5        19 2013-01-01 05:55:00 2013-01-01 06:00:00
##  8 LGA    IAD          -3       -14 2013-01-01 05:57:00 2013-01-01 06:00:00
##  9 JFK    MCO          -3        -8 2013-01-01 05:57:00 2013-01-01 06:00:00
## 10 LGA    ORD          -2         8 2013-01-01 05:58:00 2013-01-01 06:00:00
## # ... with 328,053 more rows, and 3 more variables: arr_time <dttm>,
## #   sched_arr_time <dttm>, air_time <dbl>
#Mit diesen Daten kann die Verteilung der Abfahrtszeiten über das Jahr visualisiert werden:

flights_dt %>% 
  ggplot(aes(dep_time)) + 
  geom_freqpoly(binwidth = 86400) # 86400 seconds = 1 day

#Or within a single day:

flights_dt %>% 
  filter(dep_time < ymd(20130102)) %>% 
  ggplot(aes(dep_time)) + 
  geom_freqpoly(binwidth = 600) # 600 s = 10 minutes

#Aus einem vorhandenen Datum-Uhrzeit-Objekt.
as_datetime(today())
## [1] "2020-04-10 UTC"
as_date(now())
## [1] "2020-04-10"

Mit den Funktionen year(), month(), mday() (Tag des Monats), yday() (Tag des Jahres), wday() (Tag der Woche), hour(), minute() und second() können einzelne Teile des Datums exrahiert werden:

datetime <- ymd_hms("2016-07-08 12:34:56")

year(datetime)
## [1] 2016
month(datetime)
## [1] 7
mday(datetime)
## [1] 8
yday(datetime)
## [1] 190
wday(datetime)
## [1] 6

Die Arithmetik im Umgang mit Datums- bzw. Uhrzeitsangabe umfasst Subtraktion, Addition und Division. Das Paket “lubridate” kennt drei wichtige Klassen, die Zeitspannen darstellen: * Dauer, die eine genaue Anzahl von Sekunden darstellen. * Perioden, die menschliche Einheiten wie Wochen und Monate darstellen. * Intervalle, die einen Start- und Endpunkt darstellen.

#Dauer
h_age <- today() - ymd(19791014)
h_age
## Time difference of 14789 days
as.duration(h_age)
## [1] "1277769600s (~40.49 years)"
#Perioden
one_pm <- ymd_hms("2016-03-12 13:00:00", tz="America/New_York")
one_pm
## [1] "2016-03-12 13:00:00 EST"
one_pm + days(1)
## [1] "2016-03-13 13:00:00 EDT"
#Intervalle
years(1) / days(1)
## estimate only: convert to intervals for accuracy
## [1] 365.25
next_year <- today() + years(1)
(today() %--% next_year) / ddays(1)
## [1] 365
(today() %--% next_year) %/% days(1)
## Note: method with signature 'Timespan#Timespan' chosen for function '%/%',
##  target signature 'Interval#Period'.
##  "Interval#ANY", "ANY#Period" would also be valid
## [1] 365

Hierzu noch ein paar Übungen: * Warum gibt es months(), aber keine dmonths()? * Erklären Sie, wie days(overnight x 1) funktioniert? * Erstellen Sie einen Vektor von Daten, der den ersten Tag eines jeden Monats im Jahr 2015 angibt und einen Vektor von Daten, der den ersten Tag eines jeden Monats im aktuellen Jahr angibt. * Schreiben Sie eine Funktion, die Ihren Geburtstag (als Datum) angibt und berechnet wie alt Sie in Jahren sind. * Warum funktioniert (today() %--% (today() + years(1)) / months(1) nicht?

In R ist die Zeitzone ein Attribut der Datums- bzw. Uhrzeitangabe, das ergänzend angegeben wird. Zum Beispiel repräsentieren diese drei Objekte den gleichen Zeitpunkt in der Zeit:

(x1 <- ymd_hms("2015-06-01 12:00:00", tz = "America/New_York"))
## [1] "2015-06-01 12:00:00 EDT"
(x2 <- ymd_hms("2015-06-01 18:00:00", tz = "Europe/Copenhagen"))
## [1] "2015-06-01 18:00:00 CEST"
(x3 <- ymd_hms("2015-06-02 04:00:00", tz = "Pacific/Auckland"))
## [1] "2015-06-02 04:00:00 NZST"

Durch Subtraktion kann geprüft werden, ob sie gleich sind:

x1 - x2
## Time difference of 0 secs
x1 - x3
## Time difference of 0 secs

“lubridate” verwendet immer UTC (Coordinated Universal Time), sofern nicht anderrs angegeben. Es hat keine Sommerzeit, was eine bequeme Darstellung für die Berechnung darstellt. Operationen, die Datumszeiten kombinieren, wie c(), lassen die Zeitzone oft fallen. In diesem Fall werden die Datumszeiten in Ihrer lokalen Zeitzone angezeigt:

x4 <- c(x1, x2, x3)
x4
## [1] "2015-06-01 12:00:00 EDT" "2015-06-01 12:00:00 EDT"
## [3] "2015-06-01 12:00:00 EDT"

Zeitzonen können auf zwei Wegen geändert werden:

#Anzeige soll angepasst werden; Zeitzone mit angeben
x4a <- with_tz(x4, tzone = "Australia/Lord_Howe")
x4a
## [1] "2015-06-02 02:30:00 +1030" "2015-06-02 02:30:00 +1030"
## [3] "2015-06-02 02:30:00 +1030"
x4a - x4
## Time differences in secs
## [1] 0 0 0
#Der zugrunde liegenden Zeitpunkt in der Zeitangabe soll geändert werden.
x4b <- force_tz(x4, tzone = "Australia/Lord_Howe")
x4b
## [1] "2015-06-01 12:00:00 +1030" "2015-06-01 12:00:00 +1030"
## [3] "2015-06-01 12:00:00 +1030"
x4b - x4
## Time differences in hours
## [1] -14.5 -14.5 -14.5
Previous
Next