Zusammenführen von Datensätzen

Als Abschluss zum Data-Handling mit dem tidyverse wollen wir die im Abschnitt Datenaufbereitung vorbereiteten Daten zu einem Datensatz zusammenfügen.

Die Datensätze sahen dabei so aus:

drugs %>% 
  head()
# A tibble: 6 × 7
  ...1      ...2  ...3  ...4  `All persons9`  Male
  <chr>     <chr> <chr> <chr>          <dbl> <dbl>
1 ""        ""    ""    ""                 0     0
2 "E920000… ""    ""    "Eng…           7139  5294
3 ""        ""    ""    ""                 0     0
4 "U"       ""    "U"   "Unk…            244   202
5 ""        ""    ""    ""                 0     0
6 "E120000… ""    "A"   "Nor…            276   194
# ℹ 1 more variable: Female <dbl>
crime %>% 
  head()
# A tibble: 6 × 25
  Force                HOMICIDE `Attempted murder`
  <chr>                   <dbl>              <dbl>
1 Avon and Somerset          10                 20
2 Bedfordshire                4                 10
3 British Transport P…        1                  3
4 Cambridgeshire              7                 13
5 Cheshire                    3                  6
6 Cleveland                   7                  6
# ℹ 22 more variables:
#   `Intentional destruction of viable unborn child` <dbl>,
#   `Causing death by dangerous driving` <dbl>,
#   `Causing death by careless driving when under the influence of drink or drugs` <dbl>,
#   `Causing death by careless or inconsiderate driving` <dbl>,
#   `Assault with intent to cause serious harm` <dbl>,
#   `Endangering life` <dbl>, …
satisfaction %>% 
  head()
# A tibble: 6 × 6
  Code      Region            Average_Satisfaction
  <chr>     <chr>             <dbl+lbl>           
1 K02000001 United Kingdom    7                   
2 K03000001 Great Britain     7                   
3 E92000001 England           7                   
4 E12000001 North East        7                   
5 E11000004 Tyne and Wear Me… 7                   
6 E06000001 Hartlepool UA     7                   
# ℹ 3 more variables:
#   Average_Worthwile <dbl+lbl>,
#   Average_Happy_Yesterday <dbl+lbl>,
#   Average_Anxious_Yesterday <dbl>

Wir wollen die drei vorbereiteten Teile nun in einem Datensatz integrieren. Dieser soll pro Region 1. die Anzahl aller drogenbezogenen Krankenhausaufenthalte 2. die Anzahl der (versuchten) Mordfälle 3. die mittlere Zufriedenheit über alle Skalen beinhalten

Wir fangen damit an, die Datensätze wie gewünscht vorzubereiten. Aus dem drugs-Datensatz brauchen wir die Regionsbezeichnung, die ONS-Codes und natürlich die Zahl der Einweisungen:

drugs <- drugs %>% 
  select(`ONS-Code`= `...1`,
         Region = `...4`,
         Admissions = `All persons9`)

Aus dem crime-Datensatz brauchen wir die Bezeichnung der Niederlassung, Anzahl der Morde und die Anzahl der versuchten Morde:

crime <- crime %>% 
  select(Force,
         Homicide = HOMICIDE,
         `Attempted murder`)

Aus dem satisfaction-Datensatz brauchen wir den ONS-Code und einen mittleren Zufriedenheitswert

Wie könnte ich das angehen?

satisfaction <- satisfaction %>% 
1  mutate(`ONS-Code` = Code,
2         across(where(haven::is.labelled), ~as.numeric(.)),
         Satisfaction = (Average_Satisfaction + 
                         Average_Worthwile + 
                         Average_Happy_Yesterday + 
                         Average_Anxious_Yesterday)/4) %>% 
  select(`ONS-Code`, Satisfaction)
1
Statt wie in select umzubenennen erstellen wir hier natürlich eine neue Spalte
2
Beim Erstellen des Skripts habe ich festgestellt, dass Haven-Labels keine Arithmetik mögen - so kann man die entfernen.

sum ist keine vektorisierte Funktion! Um eine neue Summenwert pro Zeile zu bilden, sind + und / nötig

Antwort aufdecken

join_

Jetzt müssen wir das ganze nur noch zusammenfügen. Dafür benutzen wir die Familien der join-Funktionen

Zuerst fügen wir die Anzahl der Straftaten zu der Anzahl der Krankenhauseinweisungen hinzu. Dabei matchen wir die Regionen über das Regions-Schlüsselwort und behalten nur die Fälle, in denen in beiden Datensätzen ein Schlüsselwort auftaucht:

overall <- drugs %>% 
  inner_join(crime, 
             by = join_by(Region == Force))
overall
# A tibble: 21 × 5
   `ONS-Code` Region           Admissions Homicide
   <chr>      <chr>                 <dbl>    <dbl>
 1 E10000006  Cumbria                  72        7
 2 E10000017  Lancashire              214       16
 3 E10000023  North Yorkshire          44        9
 4 E10000007  Derbyshire               81        4
 5 E10000018  Leicestershire           56        6
 6 E10000019  Lincolnshire             56       10
 7 E10000021  Northamptonshire          0        8
 8 E12000005  West Midlands           552       NA
 9 E10000028  Staffordshire            59       14
10 E10000031  Warwickshire             26        6
# ℹ 11 more rows
# ℹ 1 more variable: `Attempted murder` <dbl>

Dem overall-Datensatz fügen wir jetzt noch die satisfaction hinzu. Hierzu nutzen wir die ONS-Codes. Dabei wollen wir alle Fälle in overall behalten. Da wir die Spalten umbenannt haben, brauchen wir keine Definition des “by”-Arguments.

overall <- overall %>% 
  left_join(satisfaction)
Joining with `by = join_by(`ONS-Code`)`
summary(overall)
   ONS-Code            Region         
 Length:21          Length:21         
 Class :character   Class :character  
 Mode  :character   Mode  :character  
                                      
                                      
                                      
                                      
   Admissions        Homicide    
 Min.   :  0.00   Min.   : 4.00  
 1st Qu.: 44.00   1st Qu.: 6.00  
 Median : 56.00   Median : 8.50  
 Mean   : 96.33   Mean   : 8.85  
 3rd Qu.: 98.00   3rd Qu.:10.25  
 Max.   :552.00   Max.   :17.00  
                  NA's   :1      
 Attempted murder  Satisfaction  
 Min.   : 2.00    Min.   :7.000  
 1st Qu.: 5.75    1st Qu.:7.250  
 Median : 7.50    Median :7.500  
 Mean   : 9.75    Mean   :7.429  
 3rd Qu.:13.50    3rd Qu.:7.500  
 Max.   :22.00    Max.   :7.750  
 NA's   :1                       

Uns fällt jetzt aber auf, dass wir lieber klein geschriebene Spaltennamen hätten, wie doof. Dafür gibt es mit rename aber natürlich auch eine praktische Lösung im tidyverse. rename erwartet entweder die Angabe jedes Namens, der geändert werden soll als Wert und die neuen Namen als Namen der Argumente. Wir können aber auch die praktischere Variante rename_with nutzen und unsere Änderung mit einer Anweisung und tidy-select-helpern durchführen:

1overall <- overall %>%
2  rename_with(
3    .fn = ~ str_to_lower(.),
4    .cols = everything()
  )
1
Überschreibe overall, indem Du overall nimmst und dann
2
eine Neubenennung anstößt
3
indem Du ausgewählte Spaltennamen in Kleinbuchstaben übersetzt
4
und zwar für alle Spalten.

Damit ist unser Datensatz fertig:

overall
# A tibble: 21 × 6
   `ons-code` region           admissions homicide
   <chr>      <chr>                 <dbl>    <dbl>
 1 E10000006  Cumbria                  72        7
 2 E10000017  Lancashire              214       16
 3 E10000023  North Yorkshire          44        9
 4 E10000007  Derbyshire               81        4
 5 E10000018  Leicestershire           56        6
 6 E10000019  Lincolnshire             56       10
 7 E10000021  Northamptonshire          0        8
 8 E12000005  West Midlands           552       NA
 9 E10000028  Staffordshire            59       14
10 E10000031  Warwickshire             26        6
# ℹ 11 more rows
# ℹ 2 more variables: `attempted murder` <dbl>,
#   satisfaction <dbl>

Den speichern wir noch eben als csv-Datei ab.

overall %>% write_csv('data/drugs_crime_UK.csv')

Aufgabe

Sie benötigen hier wieder die Datensätze aus der Aufgabe zum Import. Der Code zum Import war der folgende:

library(tidyverse)
temp <-  read_csv2('data/temp.csv')
## ℹ Using "','" as decimal and "'.'" as grouping mark. Use `read_delim()` for more control.
## Rows: 338 Columns: 3
## ── Column specification ──────────────────────────
## Delimiter: ";"
## dbl  (2): temp, cw
## date (1): date
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

library(haven)
covid_cases <- read_sav("data/covid_cases.sav")

library(readxl)
charts <- read_excel("data/charts.xlsx")

Fügen Sie den Covid und den Temperatur-Datensatz anhand der Kalenderwoche zusammen. Dabei sollen alle Zeilen, die im Temperatur-Datensatz vorliegen auch im neuen Datensatz vorliegen. Ersetzen Sie anschließend alle möglicherweise vorliegenden fehlenden Werte durch Nullen.

Benennen Sie abschließend die Kalender-Wochen-Spalte in calendar_week und die Fall-Spalte im new_covid_cases um. Lesen Sie dazu die Hilfeseite der rename-Funktion

Speichern Sie sich den so erstellten Datensatz für später als ‘temp_covid.csv’ ab.

temp %>% 
  left_join(covid_cases, by = join_by(cw == calendar_week)) %>% 
  mutate(across(where(is.numeric), ~case_when(is.na(.) ~ 0, T ~ .))) %>% 
  rename('calender_week' = 'cw', 'new_covid_cases' = 'new_cases') %>% 
  write_csv('temp_covid.csv')

Antwort aufdecken

Pivotieren von Datensätzen

Für SPSS-Nutzer:innen sehen viele Datensätze, die wir bisher gesehen haben, wahrscheinlich etwas seltsam aus. Das liegt vielleicht zum Teil daran, dass das tidyverse grundsätzlich das long-Format dem wide-Format vorzieht.

Das folgende Beispiel der Ergebnisse der Bundestagswahlen nach 2002 1 illustriert vielleicht den Unterschied. Im wide-Format ist das zentrale Ziel dass pro Fall eine Zeile vorliegt, im long-Format wird pro Variable eine Spalte angelegt.

long-Format

# A tibble: 35 × 3
    Jahr Partei                Zweitstimmen
   <int> <chr>                        <dbl>
 1  2021 CDU/CSU                       24.1
 2  2021 SPD                           25.7
 3  2021 FDP                           11.5
 4  2021 Bündnis 90/Die Grünen         14.8
 5  2021 Die Linke. PDS                 4.9
 6  2021 AfD                           10.3
 7  2021 Sonstige                       8.7
 8  2017 CDU/CSU                       32.9
 9  2017 SPD                           20.5
10  2017 FDP                           10.7
# ℹ 25 more rows

wide-Format

# A tibble: 5 × 8
   Jahr `CDU/CSU`   SPD   FDP
  <int>     <dbl> <dbl> <dbl>
1  2021      24.1  25.7  11.5
2  2017      32.9  20.5  10.7
3  2013      41.5  25.7   4.8
4  2009      33.8  23    14.6
5  2005      35.2  34.2   9.8
# ℹ 4 more variables:
#   `Bündnis 90/Die Grünen` <dbl>,
#   `Die Linke. PDS` <dbl>, AfD <dbl>,
#   Sonstige <dbl>

Beide Formate haben Vorteile, im tidyverse ist das Hauptargument (neben Geschmaack) für das long-Format, dass sich so vektorisierte Funktionen direkt auf eine Variable anwenden lassen.

Um zwischen den Formaten zu konvertieren gibt es im tidyverse die pivot_wider und pivot_longer Funktionen.

Der Original-Datensatz zu den Bundestagswahlen sieht wie folgt aus:

bundestagswahl
# A tibble: 20 × 8
    Jahr `CDU/CSU`   SPD   FDP
   <int>     <dbl> <dbl> <dbl>
 1  2021      24.1  25.7  11.5
 2  2017      32.9  20.5  10.7
 3  2013      41.5  25.7   4.8
 4  2009      33.8  23    14.6
 5  2005      35.2  34.2   9.8
 6  2002      38.5  38.5   7.4
 7  1998      35.2  40.9   6.2
 8  1994      41.5  36.4   6.9
 9  1990      43.8  33.5  11  
10  1987      44.3  37     9.1
11  1983      48.8  38.2   7  
12  1980      44.5  42.9  10.6
13  1976      48.6  42.6   7.9
14  1972      44.9  45.8   8.4
15  1969      46.1  42.7   5.8
16  1965      47.6  39.3   9.5
17  1961      45.3  36.2  12.8
18  1957      50.2  31.8   7.7
19  1953      45.2  28.8   9.5
20  1949      31    29.2  11.9
# ℹ 4 more variables:
#   `Bündnis 90/Die Grünen` <dbl>,
#   `Die Linke. PDS` <dbl>, AfD <dbl>,
#   Sonstige <dbl>

Wir würden gern eine ggplot-Grafik erstellen, in der die Verläufe der Stimmen über die Zeit dargestellt sind. Da wir alle Zweitstimmen-Prozente auf einem aesthetic darstellen wollen, brauchen wir den Datensatz aber natürlich im long-Format

Das pivotieren geht mit pivot_longer auch sehr einfach:

btw_long <- bundestagswahl %>% 
  pivot_longer(-Jahr,
               names_to = 'Partei',
               values_to = 'Zweitstimmen')
btw_long
# A tibble: 140 × 3
    Jahr Partei                Zweitstimmen
   <int> <chr>                        <dbl>
 1  2021 CDU/CSU                       24.1
 2  2021 SPD                           25.7
 3  2021 FDP                           11.5
 4  2021 Bündnis 90/Die Grünen         14.8
 5  2021 Die Linke. PDS                 4.9
 6  2021 AfD                           10.3
 7  2021 Sonstige                       8.7
 8  2017 CDU/CSU                       32.9
 9  2017 SPD                           20.5
10  2017 FDP                           10.7
# ℹ 130 more rows

Wie erstelle ich damit jetzt einen line-chart?

btw_long %>% 
  ggplot(aes(x = Jahr, y = Zweitstimmen, color = Partei)) +
  geom_line(linewidth = 0.7) +
  scale_color_manual(values = c(AfD = '#0489DB',
                                'Bündnis 90/Die Grünen' = '#1AA037',
                                CDU = '#000000',
                                'Die Linke. PDS' = '#BD3075',
                                FDP = '#FFEF00',
                                Sonstige = 'darkgrey',
                                SPD = '#E3000F'
                                ))
Scale for colour is already present.
Adding another scale for colour, which will
replace the existing scale.

Antwort aufdecken

Zu dieser Grafik wollen wir noch Mittlere Werte über die Zeit +/- Streuungen als Linien hinzufügen.

Wie ginge das denn mit summarise und across? Und wie könnte ich die Linien hinzufügen?

btw_summary <- btw_long %>% 
  group_by(Partei) %>% 
  summarise('M' = mean(Zweitstimmen, na.rm=T),
            'SD' = sd(Zweitstimmen, na.rm=T),
            lower = M - SD,
            upper = M + SD)

Den Datensatz können wir jetzt benutzen:

btw_long %>% 
  ggplot(aes(x = Jahr, y = Zweitstimmen, color = Partei)) +
  geom_line(linewidth = 0.7) +
  geom_hline(data = btw_summary,
             aes(yintercept = M, color = Partei))+
  geom_hline(data = btw_summary,
             aes(yintercept = lower, color = Partei),
             lty = 2)+
  geom_hline(data = btw_summary,
             aes(yintercept = upper, color = Partei),
             lty = 2)+
  scale_color_manual(values = c(AfD = '#0489DB',
                                'Bündnis 90/Die Grünen' = '#1AA037',
                                'CDU/CSU' = '#000000',
                                'Die Linke. PDS' = '#BD3075',
                                FDP = '#FFEF00',
                                Sonstige = 'darkgrey',
                                SPD = '#E3000F'
                                )) 
Scale for colour is already present.
Adding another scale for colour, which will
replace the existing scale.

Antwort aufdecken

Unter der Grafik wollen wir zum Schluss noch die Mittelwerte pro Partei hintereinander als Spalten darstellen. Das heißt, wir müssen die Tabelle ins wide-Format überführen. Auch dieser Schritt ist relativ einfach:

btw_summary %>% 
  select(Partei, M) %>% 
  pivot_wider(values_from = M,
              names_from = Partei,
              names_prefix = 'M ')
# A tibble: 1 × 7
  `M AfD` `M Bündnis 90/Die Grünen` `M CDU/CSU`
    <dbl>                     <dbl>       <dbl>
1     9.2                       8.3        41.2
# ℹ 4 more variables: `M Die Linke. PDS` <dbl>,
#   `M FDP` <dbl>, `M SPD` <dbl>,
#   `M Sonstige` <dbl>

Die pivot_wider-Funktion ist aber wesentlich mächtiger, was wir zum Beispiel sehen können wenn wir die Streuungen mit hinzufügen:

btw_summary %>% 
  select(Partei, M, SD) %>% 
  pivot_wider(values_from = c(M, SD),
              names_from = Partei,
              names_glue = '{.value} {Partei}')
# A tibble: 1 × 14
  `M AfD` `M Bündnis 90/Die Grünen` `M CDU/CSU`
    <dbl>                     <dbl>       <dbl>
1     9.2                       8.3        41.2
# ℹ 11 more variables: `M Die Linke. PDS` <dbl>,
#   `M FDP` <dbl>, `M SPD` <dbl>,
#   `M Sonstige` <dbl>, `SD AfD` <dbl>,
#   `SD Bündnis 90/Die Grünen` <dbl>,
#   `SD CDU/CSU` <dbl>,
#   `SD Die Linke. PDS` <dbl>, `SD FDP` <dbl>,
#   `SD SPD` <dbl>, `SD Sonstige` <dbl>

Die letzte Tabelle ließe sich natürlich auch direkt aus dem Ursprünglichen Datensatz erstellen…

Wie ginge das direkt mit summarise und across?

bundestagswahl %>% 
  summarise(across(-Jahr, 
                   .fns = list(mean = ~mean(., na.rm=T),
                               sd = ~sd(., na.rm=T))))
# A tibble: 1 × 14
  `CDU/CSU_mean` `CDU/CSU_sd` SPD_mean SPD_sd
           <dbl>        <dbl>    <dbl>  <dbl>
1           41.2         7.00     34.6   7.22
# ℹ 10 more variables: FDP_mean <dbl>,
#   FDP_sd <dbl>,
#   `Bündnis 90/Die Grünen_mean` <dbl>,
#   `Bündnis 90/Die Grünen_sd` <dbl>,
#   `Die Linke. PDS_mean` <dbl>,
#   `Die Linke. PDS_sd` <dbl>, AfD_mean <dbl>,
#   AfD_sd <dbl>, Sonstige_mean <dbl>, …

Antwort aufdecken

Aufgabe

Importieren Sie nochmal den Worldbank-Datensatz aus dem Abschnitt zu Grammar of Graphics und ggplot2. Der Import funktionierte wie folgt:

worldbank_indicators <- read_excel("data/worldbank_indicators.xlsx")

Filtern Sie den Datensatz so, dass nur die Daten für die USA und die drei Variablen zur Lebenserwartung im Datensatz vorhanden sind.

Pivotieren Sie den Datensatz ins long-Format, so dass die Lebenserwartung in einer Variable vorliegt.

pivoted_data <- worldbank_indicators %>% 
  filter(`Country Name` == 'United States') %>% 
  select(Year, matches('Life')) %>% 
  pivot_longer(matches('Life'),
               names_to = 'group',
               values_to = 'Life Expectancy')

Antwort aufdecken

Stellen Sie die drei Verläufe in einem facettierten Liniendiagramm dar. Wenn Sie Lust haben, nutzen Sie vorher str_extract um die Gruppe aus der Namensspalte zu extrahieren.

pivoted_data %>% 
  mutate(group = str_extract(group, '\\w+al\\w*')) %>% 
  ggplot(aes(x = Year, y = `Life Expectancy`)) +
  geom_line() +
  facet_wrap(~group)

Antwort aufdecken

Pivotieren Sie den pivotierten Datensatz anschließend so ins wide-Format, dass die Jahreszahlen in den Spalten und die Gruppen in den Zeilen stehen.

pivoted_data %>% 
  mutate(group = str_extract(group, '\\w+al\\w*')) %>% 
  pivot_wider(values_from = `Life Expectancy`,
              names_from = Year,
              names_prefix = 'Year_')
# A tibble: 3 × 23
  group  Year_2000 Year_2001 Year_2002 Year_2003
  <chr>      <dbl>     <dbl>     <dbl>     <dbl>
1 female      79.3      79.5      79.6      79.7
2 male        74.1      74.3      74.4      74.5
3 total       76.6      76.8      76.9      77.0
# ℹ 18 more variables: Year_2004 <dbl>,
#   Year_2005 <dbl>, Year_2006 <dbl>,
#   Year_2007 <dbl>, Year_2008 <dbl>,
#   Year_2009 <dbl>, Year_2010 <dbl>,
#   Year_2011 <dbl>, Year_2012 <dbl>,
#   Year_2013 <dbl>, Year_2014 <dbl>,
#   Year_2015 <dbl>, Year_2016 <dbl>, …

Antwort aufdecken