Datencodierung ändern/anpassen

Wiederkehrende Fragen zum Forum

Moderator: EDi

Antworten
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Datencodierung ändern/anpassen

Beitrag von bigben »

Hallo Forum,

in meiner Wahrnehmung häufen sich die Fragen, wie eine kategoriale Variable in eine andere Codierung überführt werden kann.

Beispiele sind
Innerhalb einer Frage (Variable) zum Bildungshintergrund [...] möchte ich die Antworten 2-8 und 13 (ohne Studienabschluss) mit einer 0 und die Antworten 9-12 ( mit Studienabschluss) mit einer 1 kodieren.
viewtopic.php?p=10595#p10595

oder
ich habe eine Variable ("Welche der folgenden Quellen vertrauen sie am meisten wenn es um Impfinformationen geht?") mit einer Mehrfachauswahl von 1 bis 11. [...] Nun möchte ich eine neue Variable erstellen, die lediglich die Ausprägung 5,also das Internet als Infoquelle und die Ausprägung 6, online social networks, beinhaltet.
viewtopic.php?f=22&t=2131

oder
Ich würde gerne einen t-test durchführen um zu schauen, ob Frauen ein höheres Stresserleben aufweisen aus Männer. Nun habe ich aber im Geschlecht auch divers. Ich würde diese nun gerne mit NA ersetzen.
viewtopic.php?f=11&t=2077

oder
wie recodiere ich eine fünfstufige Variable in eine dichotome?
viewtopic.php?f=11&t=1931

oder
"5" soll zur neuen "1" werden
"1" und "2" sollen zur neuen "2" werden
und "3" und "4" sollen zur neuen "3" werden.
viewtopic.php?f=7&t=1776

und es gibt viele weitere Beispiele, aber Ihr glaubt mir bestimmt, dass es eine FAQ ist. Ganz offensichtlich ist das eine grundlegende Funktion für jedes Statistikprogramm und in einem so alten und offenen System wie R gibt es dafür natürlich nicht nur eine Antwort, sondern im Laufe der Zeit sind viele Antworten gefunden worden.

Ich würde in diesem Thread gerne Posts von erfahrenen R'lern sehen, die in je einem Beitrag je eine Herangehensweise darstellen. Gerne können die Vor- und Nachteile der Methoden diskutiert werden, aber alles was nicht streng der Methodendarstellung dient, bitte ich aus diesem Thread herauszuhalten. Jeder, der sein je eigenes Problem besprechen möchte, darf das in seinem je eigenen Thread tun, aber nicht hier. Bei Zuwiderhandlung ist großzügiges Beiträgelöschen zum Wohle der später kommenden hiermit angesagt!

GLG,
Bernhard

-------------------------------------------------------------------------------------------------------------


Beispielproblem 1:

Code: Alles auswählen

bsp1 <- c(-21.91, 7.31, 49.17, 5.0, 17.29, 10.22, -20.03, 25.06, 9.82, 
7.26, -32.53, 17.83, 23.65, -12.9, 13.85, -2.33, 19.19, 25.43, 
34.71, -13.23, 36.13, -5.49, 25.78, 21.36, 2.95, 0.18, -15.61, 
33.37, -13.47, -13.27, -14.64, 4.32, 12.68, 18.26, 7.8, -4.7, 
1.96, -30.54, 6.47, -1.67, 28.63, 43.49, 18.79, 26.98, -7.52, 
-2.9, -15.79, -16.06, 5.11, -24.25, -4.92, -50.87, 1.19, 4.27, 
32.86, 12.74, -17.83, 9.43, -18.69, 9.32, -14.19, 5.14, -26.34, 
-0.96, 36.66, 17.77, 24.13, 25.7, -12.36, 19.86, 1.01, -25.23, 
-32.06, 23.12, -11.02, -38.24, 9.69, 8.44, 8.91, -12.89, 31.54, 
1.27, -1.18, 31.72, -12.31, 22.48, 11.97, -4.26, 13.52, 3.81, 
22.61, 17.75, 11.97, -6.43, -8.67, -30.82, 5.31, 32.27, 15.49, 
-19.52)
Bilde daraus 3 Gruppen, eine für Werte unter -5, eine für Werte über 5 und eine für dazwischen.

Beispielproblem 2:

Code: Alles auswählen

bsp2 <- c("Diverse(r)", "Diverse(r)", "Diverse(r)", "unbekannt", "Mann", 
"Frau", "Diverse(r)", "Mann", "Frau", "Frau", "unbekannt", "Mann", 
"Mann", "unbekannt", "unbekannt", "Mann", "unbekannt", "Mann", 
"Diverse(r)", "Frau", "unbekannt", "unbekannt", "unbekannt", 
"Diverse(r)", "unbekannt", "unbekannt", "unbekannt", "Frau", 
"Frau", "Mann")
Mache daraus etwas t-test-taugliches für den Mann/Frau-Vergleich


Beispielproblem 3:

Code: Alles auswählen

bsp3 <- c(0L, 1L, 7L, 8L, 1L, 2L, 4L, 9L, 1L, 8L, 9L, 3L, 7L, 5L, 4L, 
8L, 9L, 3L, 5L, 9L, 8L, 7L, 4L, 5L, 4L, 2L, 4L, 8L, 7L, 8L, 1L, 
9L, 0L, 5L, 7L, 0L, 1L, 3L, 3L, 6L, 8L, 3L, 0L, 4L, 4L, 0L, 8L, 
9L, 8L, 0L)
Mache alle Werte 0 zu NA, vom Rest mache eine Gruppe für gerade und eine für ungerade Werte.
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Re: Datencodierung ändern/anpassen

Beitrag von bigben »

Hallo!

Und da bin ich gleich wieder mit meiner Lieblingslösung, aber YMMV. Da ich sowas nur gelegentlich recodiere habe ich keine Lust, mir die Syntax von Spezialfunktionen wie car::recode zu merken. Dafür schreibe ich dann lieber ein wenig extra Code. Das ist nicht die kürzeste Variante, aber sie ist sehr vielseitig anpassbar. Im Wesentlichen braucht man dafür eine Funktion, die mit if's jeden Einzelfall abdeckt:

Code: Alles auswählen

unterteile_bsp_1 <- function(x){
    if(x < -5.0) return("negativ")
    if(x > -5 & x < 5) return("ungefähr Null")
    if(x > 5) return("positiv")
    stop("Fallunterscheidung nicht gelungen!")
}
Auf zwei Dinge möchte ich hinweisen: im zweiten if findet ihr das Zeichen & als logisches 'UND'. Das logische 'ODER' wäre |:

Code: Alles auswählen

> TRUE & FALSE
[1] FALSE
> TRUE & TRUE
[1] TRUE
> TRUE | FALSE
[1] TRUE
> FALSE | FALSE
[1] FALSE
Außerdem einen Hinweis wert: Ich habe in meiner Funktion einen Fehler: Sie kann den Fall x == -5 und den Fall x == 5 nicht abdecken. Solche Fehler können vorkommen, dafür habe ich die stop-Funktion am Ende, die mich ggf. auf den Fehler hinweist. In vielen Fällen kann man dieses stop einfach durch ein return(standardwert) ersetzen.

So eine kleine Funktion ist auch mit Anfängerkenntnissen meist schnell geschrieben. Besonders leicht auf ganze Spalten anzuwenden wird sie mithilfe der Funktion "Vectorize". Man achte auf die Großschreibung:

Code: Alles auswählen

bsp <- data.frame(alt = bsp1)

unterteile <- Vectorize(function(x){
    if(x < -5.0) return("negativ")
    if(x >= -5 & x <= 5) return("ungefähr Null")
    if(x > 5) return("positiv")
    stop("Fallunterscheidung nicht gelungen!")
})

bsp$neu <- unterteile(bsp$alt)
head(bsp)
Natürlich muss man so eine Funktion nicht per Vectorize anwenden, man könnte auch Map oder sapply oder eine for-Schleife verwenden.
Nehmen wir für Beispiel 2 an, dass Diverse dem Zufall folgend für den t-Test teils als Mann und Teils als Frau codiert werden sollten.

Code: Alles auswählen

genderdf <- data.frame(gender = bsp2, value =  runif(30,0,10))

codiere <- function(gender){
    if(gender == "Mann") return("m")
    if(gender == "Frau") return("f")
    if(gender == "Diverse(r)") return(sample(c("m", "f"),1))
    if(gender == "unbekannt") return(NA)
    stop("Fallunterscheidung nicht gelungen!")
}

genderdf$gender.dicho <- sapply(genderdf$gender, codiere)

t.test(value ~ gender.dicho, data = genderdf)

Nachteile dieses Lösungsansatzes: Es entsteht viel Code und damit viele Gelegenheiten, kleine Fehler einzubauen.

Vorteile dieses Lösungsansatzes: Man muss keine packages einbinden und keine Spezialsyntax lernen. Man braucht das, was man ohnehin im Umgang mit R lernen sollte. Außerdem ist die Lösung sehr flexibel: Ob nun eine, zwei oder drei Spalten ausgewerten werden müssen ist kein Problem und auch der Zufallsrückgabewert oben war leicht umzusetzen.

LG,
Bernhard
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Re: Datencodierung ändern/anpassen

Beitrag von bigben »

Natürlich geht das knapper und eleganter. Gern genommen ist die Funktion recode, deren Syntax so schwierig dann auch nicht ist und die sehr vielseitig ist. Dafür müsst Ihr aber zunächst das Paket "car" installieren. Einfach mit

Code: Alles auswählen

install.packages("car")
library(car)
Hier gibt es ein Beispiel auf den Seiten des Forenbetreibers: https://www.r-statistik.de/R_erweitern/ ... tml#recode

Hier ein Beispiel in diesem Thread:

Code: Alles auswählen

genderdf <- data.frame(gender = bsp2)

genderdf$gender.dicho <- car::recode(bsp2,  "'Mann'='m'; 'Frau'='f'; 
            'Diverse(r)'=NA; 'unbekannt'=NA", as.factor=FALSE)

head(genderdf)
Die Bedienungsanleitung ist hier: https://www.rdocumentation.org/packages ... ics/recode

Vorsicht, denn der Name recode ist zu verlockend. Auch das Package dplyr hat eine gleichnamige Funktion die ähnliches leistet aber eine etwas andere Syntax hat und bestimmt gibt es da draußen noch mehr Code, der eine Funktion diesen Namens hat. Deshalb schlage ich die kaum längere aber eindeutige Schreibweise car::recode vor.
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
jogo
Beiträge: 2085
Registriert: Fr Okt 07, 2016 8:25 am

Re: Datencodierung ändern/anpassen

Beitrag von jogo »

Meine wichtigsten Werkzeuge beim umkodieren:
cut(), ifelse(), recode(), merge(),
... manchmal auch %in% oder indizieren mit einem logischen Vektor.

Gruß, Jörg
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Re: Datencodierung ändern/anpassen

Beitrag von bigben »

Die Funktion ifelse scheint immer dann, wenn eine binäre Entscheidung gefragt ist, wenn das Ergebnis davon abhängt, ob eine Bedingung entweder erfüllt ist, oder nicht. Sie ist ungünstig, wenn mehr Optionen bestehen, aber top im binären Fall. Man definiert eine Bedingung, die die Funktion dann auf jedes Element eines Vektors und damit auf jede Zeile eines dataframes anwendet und man definiert, was im Ja-Fall und was im Nein-Fall das Ergebnis sein soll.

Wir haben beispielsweise eine Liste von Formen und Farben und wollen diejenigen Zeilen finden, in denen rote Kreise stehen:

Code: Alles auswählen

bsp <- data.frame(farbe = c("grün", "Rot", "Rot", "Blau", "Grün", "Rot"),
                  form = c("Kreis", "Kreis", "Dreieck", "Quadrat", "Kreis", "Kreis"))

Das logische UND wird mit dem Zeichen & dargestellt und damit können wir sagen

Code: Alles auswählen

bsp$roterkreis <- ifelse(test = (bsp$farbe == "Rot") & (bsp$form == "Kreis"),
                         yes = "Roter Kreis",
                         no = "was anderes")
Oder wir haben zwei Spalten mit Zahlen und brauchen immer die, deren absoluter Betrag kleiner ist:

Code: Alles auswählen

zahlen <- data.frame(a = c(2, 5, -7, 3, -9, -1),
                     b = c(-3,4, -1, 2, 1, 100))

zahlen$neue <- ifelse(test = abs(zahlen$a)>abs(zahlen$b), 
                      yes = zahlen$a,
                      no  = zahlen$b)
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Re: Datencodierung ändern/anpassen

Beitrag von bigben »

Die Funktion cut ist immer dann zu gebrauchen, wenn man ein kontinuierliches Merkmal hat, das in Größenklassen eingeteilt werden soll. Ob jemand zu dünn oder zu dick ist, wird beispielsweise am Body Mass Index (BMI) festgemacht, der aus Körpergröße und Körpergewicht errechnet wird. Menschen mit einem BMI < 18,5 sind untergewichtig, zwischen 18,5 und 24,5 ist man normalgewichtig, zwischen 24,5 und 30 spricht die WHO von "pre-obesity", über 30 gibt es dann drei Klassen von Übergewicht. Genaueres findet man hier: http://www.euro.who.int/en/health-topic ... -index-bmi

Wenn wir nun einen Vector mit BMI-Werten haben:

Code: Alles auswählen

bmi <- c(21.27, 35.99, 16.03, 31.95, 20.55, 27.05, 35.88, 25.47, 28.18, 
28.13, 33.5, 16.3, 31.7, 17.94, 21.34, 28.75, 29.38, 22.54, 19.28, 
30.49, 16.22, 15.79, 22.53, 23.83, 26, 28.43, 20.91, 34.85, 26.92, 
24.71)
dann gelingt die Gewichtsklassifikation mit einem Aufruf von cut, wobei wir die cutoff-Werte als Argument breaks übergeben:

Code: Alles auswählen

cut(bmi, breaks = c(0, 18.5, 24.5, 30, 35, 40, Inf), ordered_result = TRUE)
Wer will, kann auch noch Namen für die verschiedenen Klassen vergeben, dafür gibt es das labels-Argument.

Auch kann man sich zwischen rechts- und linksoffenen Intervallen entscheiden, siehe dazu

Code: Alles auswählen

help(cut)
LG,
Bernhard
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Re: Datencodierung ändern/anpassen

Beitrag von bigben »

Das Paket dplyr ist als Teil des tidyverse ja sehr beliebt. Wer das package ohnehin (immer?) eingebunden hat, findet eine sehr vielseitige Lösung in der Funktion case_when

Im Eingangspost hatte ich als Beispiel 1 die Aufgabe, aus einer Zahlenserie drei Gruppen zu machen. Greifen wir bsp1 aus dem Eingangspost auf:

Code: Alles auswählen

library(dplyr)

case_when(bsp1 < -5 ~ "klein und negativ",
          bsp1 > 5  ~ "groß und positiv",
          is.na(bsp1) ~ "fehlender Wert",
          TRUE ~ "nicht weit weg von Null")
Eindrucksvoll konzise: Wir haben um die Null ein beidseits geschlossenes Intervall gezogen, was mit cut nicht gegangen wäre und haben einen ganz einfachen Weg, mit NA umzugehen.

In Beispiel 2 ging es darum, dass wir nur Frauen und Männer untersuchen wollen, weil unser t-Test nur 2 Gruppen vergleichen kann. Das könnte so aussehen:

Code: Alles auswählen

case_when(bsp2 == "Frau" ~ "Frau",
          bsp2 == "Mann" ~ "Mann",
          TRUE ~ NA_character_)
Beispiel 3 könnte so aussehen:

Code: Alles auswählen

case_when( bsp3 == 0 ~ NA_character_,
           bsp3 %% 2 == 0 ~ "gerade",
           bsp3 %% 1 != 0 ~ "ungerade")
LG,
Bernhard
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
Antworten