36  Daten manipulieren

36.1 Einer Kreuztabelle Prozentwerte hinzufügen

Ich möchte eine Kreuztabelle ausgeben, die sowohl absolute Häufigkeiten als auch Prozentwerte enthält.

Ich erstelle folgende Kreuztabelle:

A <- rep(c("A1", "A2", "A3"), 10)
B <- sample(1:5, size=30, replace=TRUE)
C <- sample(c("ja", "nein"), size=30, replace=TRUE)
df <- data.frame(rbind(table(B, A),
                 table(C, A)))
df
##      A1 A2 A3
## 1     0  4  1
## 2     1  1  4
## 3     2  1  1
## 4     2  3  0
## 5     5  1  4
## ja    5  3  6
## nein  5  7  4

Die einzelnen Werte sollen nun mit den jeweiligen Prozentwerten ergänzt werden. Dies kann mit den Funktionen transmute() und across() erfolgen.

df %>% 
            # über jede Spalte
  transmute(across(everything(),
                     # x ausgeben und " (x%)" anhängen
                   ~ paste0(.x, " (", round(.x / sum(df), 2) * 100, "%)")
                  )
           )
##          A1      A2      A3
## 1    0 (0%)  4 (7%)  1 (2%)
## 2    1 (2%)  1 (2%)  4 (7%)
## 3    2 (3%)  1 (2%)  1 (2%)
## 4    2 (3%)  3 (5%)  0 (0%)
## 5    5 (8%)  1 (2%)  4 (7%)
## ja   5 (8%)  3 (5%) 6 (10%)
## nein 5 (8%) 7 (12%)  4 (7%)

36.2 Eine gemeinsame Kreuztabelle mit mehrere Variablen erstellen

Häufig erstellen wir verschiedene Kreuztabellen mit der selben “Gruppierungsvariable”, z.B. Geschlecht oder Kontroll- vs. Interventionsgruppe.

Nehmen wir als Beispiel den Datensatz pf8.

load(url("https://www.produnis.de/R/data/pf8.RData"))
# oder
pf8 <- jgsbook::pf8

Möchten wir hinsichtlich der Variable Geschlecht unterscheiden, würden wir die Kreuztabellen mit anderen Variablen einzeln per xtab() oder table() erstellen.

table(pf8$Standort, pf8$Geschlecht)
##            
##             männlich weiblich divers
##   Rheine          90      112      1
##   Münster         75      103      0
##   Bahn            36       48      0
##   Ladbergen       26       17      0
##   Internet        60      157      0
table(pf8$Bildung, pf8$Geschlecht)
##                 
##                  männlich weiblich divers
##   keinen                2        3      0
##   Hauptschule          30       26      0
##   mittlere Reife       46       53      0
##   Ausbildung           10       19      0
##   Fachabitur           23       42      0
##   Abitur               64       76      0
##   Hochschule           50       56      1
table(pf8$Familienstand, pf8$Geschlecht)
##                
##                 männlich weiblich divers
##   ledig              125      162      0
##   Partnerschaft       56       98      0
##   verheiratet         90      136      1
##   geschieden           6       23      0
##   verwitwet            5       13      0
##   getrennt             4        4      0

Eine alternative Vorgehensweise besteht darin, mit der Funktion lapply() eine list der Kreuztabellen zu erstellen.

tbl.list <- lapply(pf8[, c("Standort", "Bildung", "Familienstand")], 
                   function(x) xtabs(~ x + pf8$Geschlecht))

tbl.list
## $Standort
##            pf8$Geschlecht
## x           männlich weiblich divers
##   Rheine          90      112      1
##   Münster         75      103      0
##   Bahn            36       48      0
##   Ladbergen       26       17      0
##   Internet        60      157      0
## 
## $Bildung
##                 pf8$Geschlecht
## x                männlich weiblich divers
##   keinen                2        3      0
##   Hauptschule          30       26      0
##   mittlere Reife       46       53      0
##   Ausbildung           10       19      0
##   Fachabitur           23       42      0
##   Abitur               64       76      0
##   Hochschule           50       56      1
## 
## $Familienstand
##                pf8$Geschlecht
## x               männlich weiblich divers
##   ledig              125      162      0
##   Partnerschaft       56       98      0
##   verheiratet         90      136      1
##   geschieden           6       23      0
##   verwitwet            5       13      0
##   getrennt             4        4      0

Wir haben Zugriff auf die einzelnen Kreuztabellen per tbl.list[[1]] oder tbl.list[[2]].

 tbl.list[[3]]
##                pf8$Geschlecht
## x               männlich weiblich divers
##   ledig              125      162      0
##   Partnerschaft       56       98      0
##   verheiratet         90      136      1
##   geschieden           6       23      0
##   verwitwet            5       13      0
##   getrennt             4        4      0

36.2.1 Zeilen- und Spaltensummen

Mit der Liste können wir nun weiterarbeiten. Zum Beispiel können wir Spalten- und Zeilensummen hinzufügen…

lapply(tbl.list, addmargins)
## $Standort
##            pf8$Geschlecht
## x           männlich weiblich divers Sum
##   Rheine          90      112      1 203
##   Münster         75      103      0 178
##   Bahn            36       48      0  84
##   Ladbergen       26       17      0  43
##   Internet        60      157      0 217
##   Sum            287      437      1 725
## 
## $Bildung
##                 pf8$Geschlecht
## x                männlich weiblich divers Sum
##   keinen                2        3      0   5
##   Hauptschule          30       26      0  56
##   mittlere Reife       46       53      0  99
##   Ausbildung           10       19      0  29
##   Fachabitur           23       42      0  65
##   Abitur               64       76      0 140
##   Hochschule           50       56      1 107
##   Sum                 225      275      1 501
## 
## $Familienstand
##                pf8$Geschlecht
## x               männlich weiblich divers Sum
##   ledig              125      162      0 287
##   Partnerschaft       56       98      0 154
##   verheiratet         90      136      1 227
##   geschieden           6       23      0  29
##   verwitwet            5       13      0  18
##   getrennt             4        4      0   8
##   Sum                286      436      1 723

36.2.2 Prozenttabellen

… oder Prozenttabellen erstellen. Um die Prozentwerte zeilenweise zu bilden, muss der Parameter margin=1 angegeben werden. Für spaltenweises Vorgehen gilt margin=2.

lapply(tbl.list, prop.table, margin=1)
## $Standort
##            pf8$Geschlecht
## x              männlich    weiblich      divers
##   Rheine    0.443349754 0.551724138 0.004926108
##   Münster   0.421348315 0.578651685 0.000000000
##   Bahn      0.428571429 0.571428571 0.000000000
##   Ladbergen 0.604651163 0.395348837 0.000000000
##   Internet  0.276497696 0.723502304 0.000000000
## 
## $Bildung
##                 pf8$Geschlecht
## x                   männlich    weiblich      divers
##   keinen         0.400000000 0.600000000 0.000000000
##   Hauptschule    0.535714286 0.464285714 0.000000000
##   mittlere Reife 0.464646465 0.535353535 0.000000000
##   Ausbildung     0.344827586 0.655172414 0.000000000
##   Fachabitur     0.353846154 0.646153846 0.000000000
##   Abitur         0.457142857 0.542857143 0.000000000
##   Hochschule     0.467289720 0.523364486 0.009345794
## 
## $Familienstand
##                pf8$Geschlecht
## x                  männlich    weiblich      divers
##   ledig         0.435540070 0.564459930 0.000000000
##   Partnerschaft 0.363636364 0.636363636 0.000000000
##   verheiratet   0.396475771 0.599118943 0.004405286
##   geschieden    0.206896552 0.793103448 0.000000000
##   verwitwet     0.277777778 0.722222222 0.000000000
##   getrennt      0.500000000 0.500000000 0.000000000

36.2.3 Signifikanztests

Zudem können wir Signifikanztests rechnen.

lapply(tbl.list, chisq.test)
## $Standort
## 
##  Pearson's Chi-squared test
## 
## data:  X[[i]]
## X-squared = 26.296, df = 8, p-value = 0.0009347
## 
## 
## $Bildung
## 
##  Pearson's Chi-squared test
## 
## data:  X[[i]]
## X-squared = 9.4526, df = 12, p-value = 0.6639
## 
## 
## $Familienstand
## 
##  Pearson's Chi-squared test
## 
## data:  X[[i]]
## X-squared = 10.503, df = 10, p-value = 0.3975

Wenn wir die Liste in ein Objekt speichern, können wir uns nur die p-Werte ausgeben lassen.

chi.list <- lapply(tbl.list, chisq.test)
chi.list[[1]]$p.value
## [1] 0.0009347078

36.2.4 eine große Kreuztabelle

Zu guter Letzt können wir die einzelnen Kreuztabellen zu einer “großen” Tabelle vereinen.

rbind(tbl.list[[1]],
      tbl.list[[2]],
      tbl.list[[3]])
##                männlich weiblich divers
## Rheine               90      112      1
## Münster              75      103      0
## Bahn                 36       48      0
## Ladbergen            26       17      0
## Internet             60      157      0
## keinen                2        3      0
## Hauptschule          30       26      0
## mittlere Reife       46       53      0
## Ausbildung           10       19      0
## Fachabitur           23       42      0
## Abitur               64       76      0
## Hochschule           50       56      1
## ledig               125      162      0
## Partnerschaft        56       98      0
## verheiratet          90      136      1
## geschieden            6       23      0
## verwitwet             5       13      0
## getrennt              4        4      0
dummy <- rbind(tbl.list[[1]],
      tbl.list[[2]],
      tbl.list[[3]])
# erzeuge ein Datenframe aus den Zeilennamen und den Werten
dummy <- data.frame( row.names(dummy), dummy )

# lösche die alten Rownames
rownames(dummy) <- NULL
dummy
##    row.names.dummy. männlich weiblich divers
## 1            Rheine       90      112      1
## 2           Münster       75      103      0
## 3              Bahn       36       48      0
## 4         Ladbergen       26       17      0
## 5          Internet       60      157      0
## 6            keinen        2        3      0
## 7       Hauptschule       30       26      0
## 8    mittlere Reife       46       53      0
## 9        Ausbildung       10       19      0
## 10       Fachabitur       23       42      0
## 11           Abitur       64       76      0
## 12       Hochschule       50       56      1
## 13            ledig      125      162      0
## 14    Partnerschaft       56       98      0
## 15      verheiratet       90      136      1
## 16       geschieden        6       23      0
## 17        verwitwet        5       13      0
## 18         getrennt        4        4      0

36.3 Zeilen und Spalten tauschen

Ich möchte bei meinem Datensatz Zeilen und Spalten vertauschen.

Dies kann mit der Funktion apply() gemacht werden. Angenommen das Datenframe datensatz sieht wie folgt aus …

# lade Testdatensatz datensatz
datensatz <-read.table(url("http://www.produnis.de/R/data/DieDaten.csv"), sep=";", header=TRUE) 
# zeige "datensaz" an 
datensatz
##     Name   Geschlecht Lieblingsfarbe Einkommen
## 1   Hans    maennlich          gruen      1233
## 2   Caro     weiblich           blau       800
## 3   Lars intersexuell           gelb      2400
## 4   Ines     weiblich        schwarz      4000
## 5 Samira     weiblich           gelb       899
## 6  Peter    maennlich          gruen      1100
## 7  Sarah     weiblich           blau      1900

…so tauschen wir Zeilen und Spalten mittels

# vertausche Spalten und Zeilen 
apply(datensatz, MARGIN=1, FUN=function(x) {x})
##                [,1]        [,2]       [,3]           [,4]       [,5]      
## Name           "Hans"      "Caro"     "Lars"         "Ines"     "Samira"  
## Geschlecht     "maennlich" "weiblich" "intersexuell" "weiblich" "weiblich"
## Lieblingsfarbe "gruen"     "blau"     "gelb"         "schwarz"  "gelb"    
## Einkommen      "1233"      " 800"     "2400"         "4000"     " 899"    
##                [,6]        [,7]      
## Name           "Peter"     "Sarah"   
## Geschlecht     "maennlich" "weiblich"
## Lieblingsfarbe "gruen"     "blau"    
## Einkommen      "1100"      "1900"

Da die Funktion apply() eine Typkonversion in die Klasse matrix vornimmt, (und somit alle Datentypen auf den kleinsten gemeinsamen Nenner character zurückfallen, siehe die Anführungszeichen im Output) muss bei Bedarf zurück in die Klasse data.frame konvertiert werden.

# vertausche Spalten und Zeilen 
as.data.frame(apply(datensatz, MARGIN=1, FUN=function(x) {x}))
##                       V1       V2           V3       V4       V5        V6
## Name                Hans     Caro         Lars     Ines   Samira     Peter
## Geschlecht     maennlich weiblich intersexuell weiblich weiblich maennlich
## Lieblingsfarbe     gruen     blau         gelb  schwarz     gelb     gruen
## Einkommen           1233      800         2400     4000      899      1100
##                      V7
## Name              Sarah
## Geschlecht     weiblich
## Lieblingsfarbe     blau
## Einkommen          1900

36.4 binäre Dummy-Variablen zusammenführen

Manchmal liegen Daten in Dummy-Variablen vor. So wird beispielsweise der Beruf einer Person nicht als Ausprägung der Variable Beruf gespeichert, sondern es gibt für jeden Beruf eine Dummy-Variable, die “Ja” oder “Nein” enthält, je nachdem, welchen Beruf die Person ausübt.

# erzeuge Testdaten
df <- data.frame(
  id = 1:5,
  Busfahrer_in = factor(c("ja", "nein", "nein", "ja", "nein")),
  Aerztin = factor(c("nein", "ja", "nein", "nein", "nein")),
  Lehrer_in = factor(c("nein", "nein", "ja", "nein", "nein")),
  Ingenieur_in = factor(c("nein", "nein", "nein", "nein", "ja"))
)
# anzeigen
df
##   id Busfahrer_in Aerztin Lehrer_in Ingenieur_in
## 1  1           ja    nein      nein         nein
## 2  2         nein      ja      nein         nein
## 3  3         nein    nein        ja         nein
## 4  4           ja    nein      nein         nein
## 5  5         nein    nein      nein           ja

Die Aufteilung in solche Dummy-Variablen wird beispielsweise gemacht, um logistische Regression zu berechnen.

In manchen Fällen kann es unerwünscht sein, solche Dummys im Datensatz zu haben.

Mit den Funktionen mutate() und case_when() können die Dummyeinträge in einer neuen Variable vereinigt werden. Das klappt allerdings nur, wenn sich die Dummyeinträge gegenseitig ausschließen (also keine Mehrfachauswahl).

# führe Dummys in "Beruf" zusammen
df %>%
  mutate(Beruf = case_when(
                    Busfahrer_in == "ja" ~ "Busfahrer_in",
                    Aerztin == "ja" ~ "Arzt/Ärztin",
                    Lehrer_in == "ja" ~ "Lehrer/in",
                    Ingenieur_in == "ja" ~ "Ingenieur/in")
    ) 
##   id Busfahrer_in Aerztin Lehrer_in Ingenieur_in        Beruf
## 1  1           ja    nein      nein         nein Busfahrer_in
## 2  2         nein      ja      nein         nein  Arzt/Ärztin
## 3  3         nein    nein        ja         nein    Lehrer/in
## 4  4           ja    nein      nein         nein Busfahrer_in
## 5  5         nein    nein      nein           ja Ingenieur/in