In diesem Post untersuchen wir eine recht häufige Fragestellung im Bereich der Datenanalyse – die Auswertung von Umfragedaten. Umfragen sind eine gängige Angelegenheit in vielen Organisationen: man möchte wissen, ob die Kunden zufrieden sind oder was die Mitarbeiter vom Management denken. Wir werden nicht alle Aspekte der Analyse betrachten – da gibt es viel zu tun –, sondern ein paar zentrale Aspekte herausgreifen.
Laden wir zuerst ein paar nützliche Pakete:
library(tidyverse)
library(likert)
library(sjmisc)
library(sjPlot)
Dann laden wir Umfragedaten:
data(extra, package = "pradadata")
Das Paket pradadata
muss vorab (einmalig) installiert werden (s. Installationshinweise).
Diese Daten untersuchen die Extraversion von Menschen (z.B. Mitarbeiter*innen); außerdem werden noch ein paar mit der Extraversion korrelierte Variablen erhoben (für unsere Zwecke im Detail nicht weiter von Belang).
glimpse(extra)
#> Observations: 826
#> Variables: 34
#> $ timestamp <chr> "11.03.2015 19:17:48", "11.03.2015 19:18:05...
#> $ code <chr> "HSC", "ERB", "ADP", "KHB", "PTG", "ABL", "...
#> $ i01 <int> 3, 2, 3, 3, 4, 3, 4, 3, 4, 4, 3, 3, 4, 4, 3...
#> $ i02r <int> 3, 2, 4, 3, 3, 2, 4, 3, 4, 4, 3, 4, 3, 3, 3...
#> $ i03 <int> 3, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 4, 1...
#> $ i04 <int> 3, 2, 4, 4, 4, 4, 3, 3, 4, 4, 3, 3, 2, 4, 3...
#> $ i05 <int> 4, 3, 4, 3, 4, 2, 3, 2, 3, 3, 3, 2, 3, 3, 3...
#> $ i06r <int> 4, 2, 1, 3, 3, 3, 3, 2, 4, 3, 3, 3, 3, 3, 3...
#> $ i07 <int> 3, 2, 3, 3, 4, 4, 2, 3, 3, 3, 2, 4, 2, 3, 3...
#> $ i08 <int> 2, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 2, 3, 3, 4...
#> $ i09 <int> 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 2, 4, 4, 4...
#> $ i10 <int> 1, 1, 1, 2, 4, 3, 2, 1, 2, 3, 1, 3, 2, 3, 2...
#> $ n_facebook_friends <dbl> 250, 106, 215, 200, 100, 376, 180, 432, 200...
#> $ n_hangover <dbl> 1, 0, 0, 15, 0, 1, 1, 2, 5, 0, 1, 2, 20, 2,...
#> $ age <int> 24, 35, 25, 39, 29, 33, 24, 28, 29, 38, 25,...
#> $ sex <chr> "Frau", "Frau", "Frau", "Frau", "Frau", "Ma...
#> $ extra_single_item <int> 4, 3, 4, 3, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4...
#> $ time_conversation <dbl> 10, 15, 15, 5, 5, 20, 2, 15, 10, 10, 1, 5, ...
#> $ presentation <chr> "nein", "nein", "nein", "nein", "nein", "ja...
#> $ n_party <dbl> 20, 5, 3, 25, 4, 4, 3, 6, 12, 5, 10, 5, 10,...
#> $ clients <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
#> $ extra_vignette <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
#> $ i21 <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
#> $ extra_vignette2 <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
#> $ major <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
#> $ smoker <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
#> $ sleep_week <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
#> $ sleep_wend <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
#> $ clients_freq <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
#> $ extra_mean <dbl> 2.9, 2.1, 2.6, 2.9, 3.2, 2.8, 2.8, 2.5, 3.2...
#> $ extra_md <dbl> 3.0, 2.0, 3.0, 3.0, 3.5, 3.0, 3.0, 2.5, 3.5...
#> $ extra_aad <dbl> 0.56, 0.54, 1.08, 0.36, 0.80, 0.68, 0.68, 0...
#> $ extra_mode <dbl> 3, 2, 1, 3, 4, 3, 3, 2, 4, 3, 3, 3, 3, 3, 3...
#> $ extra_iqr <dbl> 0.00, 0.75, 2.50, 0.00, 1.00, 0.75, 0.75, 1...
Zuerst wählen wir nur die 10 Items der Umfrage heraus, das ist komfortabler als der Datensatz mit 34 Variablen:
extra %>%
select(i01:i10) -> extra_items
Der Operator %>%
bedeutet so viel wie “und dann”. Demnach liest sich die Syntax oben wie folgt:
Nimm den Datensatz “extra” UND DANN… wähle Spalten i01 bis i10 speichere das Ergebnis als “extra_items”
Werfen wir einen Blick hinein (engl. “to glimpse”):
glimpse(extra_items)
#> Observations: 826
#> Variables: 10
#> $ i01 <int> 3, 2, 3, 3, 4, 3, 4, 3, 4, 4, 3, 3, 4, 4, 3, 4, 4, 4, 3, ...
#> $ i02r <int> 3, 2, 4, 3, 3, 2, 4, 3, 4, 4, 3, 4, 3, 3, 3, 4, 4, 3, 3, ...
#> $ i03 <int> 3, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 4, 1, 2, 3, 3, 1, ...
#> $ i04 <int> 3, 2, 4, 4, 4, 4, 3, 3, 4, 4, 3, 3, 2, 4, 3, 4, 4, 4, 4, ...
#> $ i05 <int> 4, 3, 4, 3, 4, 2, 3, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, 4, 3, ...
#> $ i06r <int> 4, 2, 1, 3, 3, 3, 3, 2, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, ...
#> $ i07 <int> 3, 2, 3, 3, 4, 4, 2, 3, 3, 3, 2, 4, 2, 3, 3, 3, 3, 4, 3, ...
#> $ i08 <int> 2, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 2, 3, 3, 4, 3, 2, 4, 3, ...
#> $ i09 <int> 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 2, 4, 4, 4, 4, 4, 4, 3, ...
#> $ i10 <int> 1, 1, 1, 2, 4, 3, 2, 1, 2, 3, 1, 3, 2, 3, 2, 3, 3, 3, 2, ...
Visualisierung der Itemantworten
Mit dem Paket likert
kann man sich komfortabel ein schönes Diagramm zu den Itemverteilungen ausgeben lassen. Allerdings “verdaut” likert
nur klassische Dataframes, keine neumodischen Tibbles. extra_items
ist aber ein Tibble, d.h. hat die Klasse tbl
:
class(extra_items)
#> [1] "tbl_df" "tbl" "data.frame"
Wir wandeln daher in einen “normalen” Dataframe um:
extra_items %>%
as.data.frame() -> extra_items
class(extra_items)
#> [1] "data.frame"
Nun wandeln wir den Dataframe in ein Objekt vom Typ likert
um, das resultierende Objekt kann dann einfach geplottet werden. Ach ja, likert
möchte die Items gerne vom Typ factor
haben, also wandeln wir alle Items noch um (mit mutate_all()
), und zwar in den Typ factor
:
extra_items %>%
mutate_all(factor) -> extra_items_f
extra_items_f %>%
likert() %>%
plot()
Items umkodieren
Häufig kommt es vor, dass ein Item umkodiert werden muss. Was heißt umkodieren und wozu braucht man es? Normalerweise sind Items “richtig herum” formuliert, etwa “Ich gehe gerne unter Leute” wäre ein Beispiel für ein Extraversionsitem. Es ist in Richtung Extraversion formuliert (oder, synonym, “kodiert”). Aber das Item “Ich bleibe am liebsten alleine zuhause” ist nicht Richtung Extraversion kodiert, sondern im Gegenteil in Richtung Introversion. Leute, die diesem Item zustimmen, sind also nicht hoch sondern gering extrovertiert. Das Item muss zuerst “umgedreht werden”: Hat jemand die höchste Stufe (hier: 4) angekreuzt, so muss das auf die geringste Antwortstufe (hier: 1) umgedreht werden und so weiter.
Das kann man wie folgt leisten; nehmen wir an, Item 01 müsse umkodiert werden:
extra_items %>%
rec(i01, rec = "1=4; 2=3; 3=2; 4=1")
#> # A tibble: 826 x 11
#> i01 i02r i03 i04 i05 i06r i07 i08 i09 i10 i01_r
#> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <dbl>
#> 1 3 3 3 3 4 4 3 2 3 1 2
#> 2 2 2 1 2 3 2 2 3 3 1 3
#> 3 3 4 1 4 4 1 3 2 3 1 2
#> 4 3 3 2 4 3 3 3 3 3 2 2
#> 5 4 3 1 4 4 3 4 2 3 4 1
#> 6 3 2 1 4 2 3 4 3 3 3 2
#> 7 4 4 1 3 3 3 2 3 3 2 1
#> 8 3 3 2 3 2 2 3 2 4 1 2
#> 9 4 4 1 4 3 4 3 3 4 2 1
#> 10 4 4 2 4 3 3 3 3 3 3 1
#> # ... with 816 more rows
Das umkodierte Item (ganz hinten als letzte Spalte im Datensatz) wird praktischerweise mit _r
gekennzeichnet, so kann man einfach damit weiterarbeiten.
Eine weitere häufig benötigte Art des Umkodierens ist, statt einer Antwortzahl wie 1 oder 2 etc. die Beschreibung dieser Antwort aufzuführen, z.B. “ich stimme dieser Aussage überhaupt nicht zu”. Das geht z.B. so:
extra_items_f %>%
mutate_all(funs( case_when(
. == "1" ~ "stimme nicht zu",
. == "2" ~ "stimme eher nicht zu",
. == "3" ~ "stimme eher zu",
. == "4" ~ "stimme voll und ganz zu"))) -> extra_items_rec
glimpse(extra_items_rec)
#> Observations: 826
#> Variables: 10
#> $ i01 <chr> "stimme eher zu", "stimme eher nicht zu", "stimme eher zu...
#> $ i02r <chr> "stimme eher zu", "stimme eher nicht zu", "stimme voll un...
#> $ i03 <chr> "stimme eher zu", "stimme nicht zu", "stimme nicht zu", "...
#> $ i04 <chr> "stimme eher zu", "stimme eher nicht zu", "stimme voll un...
#> $ i05 <chr> "stimme voll und ganz zu", "stimme eher zu", "stimme voll...
#> $ i06r <chr> "stimme voll und ganz zu", "stimme eher nicht zu", "stimm...
#> $ i07 <chr> "stimme eher zu", "stimme eher nicht zu", "stimme eher zu...
#> $ i08 <chr> "stimme eher nicht zu", "stimme eher zu", "stimme eher ni...
#> $ i09 <chr> "stimme eher zu", "stimme eher zu", "stimme eher zu", "st...
#> $ i10 <chr> "stimme nicht zu", "stimme nicht zu", "stimme nicht zu", ...
Betrachten wir diese Syntax genauer:
- Mit
mutate_all
weisen wir an, dass alle Spalten verändert (“mutiert”) werden sollen - Mit
case_when
definieren wir die Bedingungen für die Veränderung - Der Punkt
.
steht dabei als Platzhalter für jeweils eine Spalte, sozusagen die Spalte “i”
Man kann den case_when
Befehl grob so ins Deutsche übersetzen:
Wenn eine Zelle in der aktuellen Spalte den Wert “1” hat, dann soll das in “stimme überhaupt nicht zu” übersetzt werden. Für übrige Werte (2,3,4) gilt das Gleiche.
Zeilenmittelwerte berechnen
Häufig möchte man Zeilenmittelwerte oder Zeilensummen berechnen. Das kann man so machen:
extra_items %>%
row_means(i01:i10, n = 9) -> extra_items
extra_items
#> # A tibble: 826 x 11
#> i01 i02r i03 i04 i05 i06r i07 i08 i09 i10 rowmeans
#> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <dbl>
#> 1 3 3 3 3 4 4 3 2 3 1 2.9
#> 2 2 2 1 2 3 2 2 3 3 1 2.1
#> 3 3 4 1 4 4 1 3 2 3 1 2.6
#> 4 3 3 2 4 3 3 3 3 3 2 2.9
#> 5 4 3 1 4 4 3 4 2 3 4 3.2
#> 6 3 2 1 4 2 3 4 3 3 3 2.8
#> 7 4 4 1 3 3 3 2 3 3 2 2.8
#> 8 3 3 2 3 2 2 3 2 4 1 2.5
#> 9 4 4 1 4 3 4 3 3 4 2 3.2
#> 10 4 4 2 4 3 3 3 3 3 3 3.2
#> # ... with 816 more rows
Mit n = 9
legen wir fest, dass mindestens 9 (von 10) Items von der Person beantwortet worden sollen, sonst wird ein fehlender Wert zurückgeliefert.
Mittelwerte pro Person nach Gruppen aufgeteilt
Häufig ist man an summativen Statistiken pro Gruppe interessiert, etwa: Ist die Belegschaft in Abteilung A engagierter als in Abteilung B? Sind die Kunden mit Produkt X zufriedener als mit Produkt Y?
Diese Frage kann man so beantworten: Zuerst fügen wir eine Gruppierungsvariable hinzu, hier Geschlecht (sex
):
extra_items %>%
mutate(sex = extra$sex) -> extra_items
mutate
definiert eine neue Spalte, in diesem Fall mit dem Inhalt der Spalte sex
aus dem ursprünglichen Datensatz `extra´.
Jetzt berechnen wir die mittlere Extraversion pro Gruppe:
extra_items %>%
row_means(i01:i10, n = 9) %>%
group_by(sex) %>%
summarise(group_mean = mean(rowmeans, na.rm = TRUE))
#> # A tibble: 3 x 2
#> sex group_mean
#> <chr> <dbl>
#> 1 Frau 2.91
#> 2 Mann 2.86
#> 3 <NA> 2.96
Häufigkeitsauswertung
Umfragedaten werden gerne nach Häufigkeiten ausgewertet, das kann so aussehen für das Item i10
:
extra_items %>%
frq(i10)
#>
#> # i10 <integer>
#> # total N=826 valid N=817 mean=2.20 sd=0.88
#>
#> val frq raw.prc valid.prc cum.prc
#> 1 183 22.15 22.40 22.40
#> 2 354 42.86 43.33 65.73
#> 3 212 25.67 25.95 91.68
#> 4 68 8.23 8.32 100.00
#> <NA> 9 1.09 NA NA
frq
steht dabei für “frequencies”, also Häufigkeiten. Einfach, oder?
Vielleicht möchte man diese Häufigkeiten noch visualisiert haben, z.B. so:
sjp.frq(extra_items$i10)
Oder in einer Variante:
sjp.frq(extra_items$i10, type = "histogram",
show.mean = TRUE, normal.curve = TRUE)
Vielleicht auch als schön formatierte Kontingenztabelle - Item 10 in Zusammenhang mit dem Geschlecht:
sjt.xtab(extra_items$i10, extra_items$sex)
i10 | sex | Total | |
---|---|---|---|
Frau | Mann | ||
1 | 140 | 41 | 181 |
2 | 223 | 130 | 353 |
3 | 132 | 78 | 210 |
4 | 32 | 36 | 68 |
Total | 527 | 285 | 812 | χ2=22.661 · df=3 · Cramer’s V=0.167 · p=0.000 |
Mehr davon?! - In unseren Seminaren
Sie möchten mehr über moderne Datenvisualisierung lernen?
In unseren Seminaren lernen Sie alles einiges über professionelle Datenvisualisierung.
Nähere Informationen zum Seminar: https://www.data-divers.com/leistungen/seminare/seminar-dataviser/
Zur Annmeldung: https://www.data-divers.com/leistungen/seminare/seminaranmeldung/
Dozenten:
Prof. Dr. Sebastian Sauer (zur Homepage, zum Blog)
Prof. Dr. Felix Bauer (zur Homepage)
Buch zur modernen Datenanalyse (und Datenvisualisierung) mit R
Dieses Buch erklärt ausführlich die Grundlagen der Datenanalyse mit R: