35  Daten manipulieren

35.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%)

35.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

35.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

35.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

35.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

35.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

35.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

35.4 Klassen für Variablen bilden (klassieren)

Ich möchte die Werte einer Variable in Kategorien bzw. Klassen einteilen.

Angenommen, meine Variable enthält Werte zwischen 0 und 500, und ich möchte diese Werte in die Gruppen “0-70”, “71-200”, “201-400”, “>400”, klassieren, wie gehe ich dann vor?

35.4.1 R base

Im klassischen R erstellen wir zunächst zufällige Zahlen zwischen 0 und 500.

# erzeuge 200 Zufallszahlen von 0 bis 500
dummy <- sample(0:500, 200)
# bzw. direkt als Dataframe
dummy <- data.frame(x = sample(0:500, 200))

Nun erzeugen wir die neue Variable xKAT, in welcher die Klassierung angegeben werden soll.

# wir bilden Kategorien
#      "0-70"
#      "71-200"
#      "201-400"
#      "> 400"
# und speichern das in die neue Variable xKAT
dummy$xKAT[dummy$x <71] <- "0-70"
dummy$xKAT[dummy$x <201 & dummy$x > 70] <- "71-200"
dummy$xKAT[dummy$x <401 & dummy$x > 200] <- "201-400"
dummy$xKAT[dummy$x >400] <- "größer 400"

Anschließend wandeln wir die neue Variable in einen ordinalen Faktor mit korrekter Levelreihenfolge.

dummy$xKAT <- factor(dummy$xKAT, levels=c("0-70",
                                    "71-200",
                                    "201-400",
                                    "größer 400"),
                       ordered=TRUE)
head(dummy$xKAT)
## [1] 201-400    größer 400 71-200     größer 400 71-200     0-70      
## Levels: 0-70 < 71-200 < 201-400 < größer 400

35.4.2 Tidyverse

Zunächst laden wir das tidyverse-Paket und erstellen uns zufällige Zahlen zwischen 0 und 500.

# lade tidyverse
library(tidyverse)
# erzeuge 200 Zufallszahlen von 0 bis 500
dummy <- sample(0:500, 200)
# bzw. direkt als Tibble
dummy <- tibble(x = sample(0:500, 200))

Nun erzeugen wir die neue Variable xKAT, in welcher die Klassierung angegeben werden soll. Hierfür nutzen wir innerhalb von mutate() die ifelse()-Funktion. Diese folgte der Logik “WELCHE - WAS -ANSONSTEN”. In einem ersten beispielhaften Schritt wählen wir all “x < 71” aus, speichern für diese Fälle den character-Wert “0-70”, und bei allen anderen Fällen ein “NA”.

dummy %>%
              # ifelse(WELCHE, WAS, ANSONSTEN)
    mutate(xKAT = ifelse(x < 71 , "0-70", NA))
## # A tibble: 200 × 2
##       x xKAT 
##   <int> <chr>
## 1   211 <NA> 
## 2   241 <NA> 
## 3   449 <NA> 
## 4   131 <NA> 
## 5   476 <NA> 
## 6   459 <NA> 
## # ℹ 194 more rows

Es lassen sich mehrere ifelse()-Ausdrücke kombinieren, indem diese vor den NA-Ausdruck geschrieben werden. Dabei sammeln sich die Klammer-Zu Symbole ) an, kommen Sie hier nicht durcheinander!

# Das lässt sich erweitern, indem der ", NA"-Ausdruck nach hinten wandert
dummy %>%
  mutate(xKAT = ifelse(x < 71 , "0-70",
                ifelse(x < 201 & x >70 , "71-200",
                       NA)))
## # A tibble: 200 × 2
##       x xKAT  
##   <int> <chr> 
## 1   211 <NA>  
## 2   241 <NA>  
## 3   449 <NA>  
## 4   131 71-200
## 5   476 <NA>  
## 6   459 <NA>  
## # ℹ 194 more rows

Der vollständige Befehl zur Klassierung lautet demnach:

# wir bilden Kategorien
#      "0-70"
#      "71-200"
#      "201-400"
#      "> 400"
# und speichern das in die neue Variable xKAT
dummy %>%
  mutate(xKAT = ifelse(x < 71 , "0-70",
                ifelse(x < 201 & x >70 ,  "71-200",
                ifelse(x < 401 & x >200 , "201-400",
                ifelse(x > 400 , "> 400",
                              NA)))))
## # A tibble: 200 × 2
##       x xKAT   
##   <int> <chr>  
## 1   211 201-400
## 2   241 201-400
## 3   449 > 400  
## 4   131 71-200 
## 5   476 > 400  
## 6   459 > 400  
## # ℹ 194 more rows

Wenn - so wie hier - mehrere Konditionen angegeben werden, ist die Funktion case_when() etwas einfacher zu schreiben und zu lesen als die ifelse()-Staffelungen.

# nutze case_when() an Stelle von ifelse()
dummy %>%
  mutate(xKAT = case_when(x < 71 ~ "0-70",
                          x < 201 & x > 70  ~ "71-200",
                          x < 401 & x > 200 ~ "201-400",
                          x > 400 ~ "größer 400")
         )
## # A tibble: 200 × 2
##       x xKAT      
##   <int> <chr>     
## 1   211 201-400   
## 2   241 201-400   
## 3   449 größer 400
## 4   131 71-200    
## 5   476 größer 400
## 6   459 größer 400
## # ℹ 194 more rows

Mit dem Parameter .default kann ein Wert festgelegt werden, der vergeben wird, wenn keine der Bedingungen zutrifft. Standardmäßig werden NAs vergeben.

# nutze case_when() an Stelle von ifelse()
dummy %>%
  mutate(xKAT = case_when(x < 71 ~ "0-70",
                          x < 201 & x > 70  ~ "71-200",
                          x < 401 & x > 200 ~ "201-400",
                          x > 400 ~ "größer 400",
                          .default="keines davon")
         )
## # A tibble: 200 × 2
##       x xKAT      
##   <int> <chr>     
## 1   211 201-400   
## 2   241 201-400   
## 3   449 größer 400
## 4   131 71-200    
## 5   476 größer 400
## 6   459 größer 400
## # ℹ 194 more rows

Anschließend wandeln wir noch die neue Variable in einen ordinalen Faktor mit korrekter Levelreihenfolge.

# die neue Variable als ordinalen Factor mit korrekter
# Levelreihenfolge speichern
dummy <- dummy %>%
  mutate(xKAT = case_when(x < 71 ~ "0-70",
                          x < 201 & x > 70  ~ "71-200",
                          x < 401 & x > 200 ~ "201-400",
                          x > 400 ~ "größer 400",
                          .default="keines davon"),
         xKAT = factor(xKAT, levels=c("0-70",
                                      "71-200",
                                      "201-400",
                                      "größer 400",
                                      "keines davon"),
                       ordered=TRUE)
         )
head(dummy$xKAT)
## [1] 201-400    201-400    größer 400 71-200     größer 400 größer 400
## Levels: 0-70 < 71-200 < 201-400 < größer 400 < keines davon