- 1 Analyse der Impfbereitschaft
- 2 Vorbereitung
- 3 Daten verstehen
- 4 Modell mit den Big-Five-Dimensionen als Prädiktoren
- 5 Zur Datenqualität
- 6 Visualisierung 1
- 6.1 Median-Aufteilung
- 6.2 Dichotomisierung anhand des Skalen-Mittelpunkts
- 6.3 Anteile berechnen (eindimensional)
- 6.4 Als Balkendiagramm
- 6.5 Anteil hoher Big-Five-Werte bei Impfwilligen (zweidimensional)
- 6.6 Kontingenzbalken 1
- 6.7 Kontingenzbalken 2
- 6.8 Größter Unterschied zwischen den Big-Five-Dimensionen bei den Hoch-Impfbereiten
- 6.9 Plot Zur Differenz
- 6.10 Korrespondenz (Kontingenz) von Extraversion und Impfbereitschaft
- 6.11 Waffel-Diagramm
- 6.12 Font-Awesome-Visualisierung (Pictogramm)
- 6.13 Was für ein Aufwand …
- 6.14 Fazit
- 7 Visualisierung 2
- 8 Finale Visualisierungen
1 Analyse der Impfbereitschaft
Im Rahmen einer Umfrage wurde die Impfbereitschaft von Studentis im Hinblick auf eine Covid-19-Impfung erfasst.
Es stellt sich die Aufgabe, zentrale Erkenntnisse aus diesen Daten darzustellen. In diesem Post werden einige Ansätze dazu vorgestellt.
2 Vorbereitung
2.1 Pakete laden
library(tidyverse)
library(corrr) # Komfort bei Korrelationen
library(broom) # tidy Regressionsergebnisse
library(waffle) # Waffeldiagramm
library(hrbrthemes) # Ggplot2 style
library(magick) # Bildbearbeitung
library(GGally) # Regressionskoeffizienten plotten
library(broom.helpers) # Regressionskoeffizienten plotten
2.2 Daten laden
d <- read_csv("https://raw.githubusercontent.com/sebastiansauer/2021-sose/master/data/Impfbereitschaft/d3.csv")
Ein Blick in die Daten:
glimpse(d)
## Rows: 164
## Columns: 23
## $ timestamp <chr> "23/04/2021 12:22:18", "23/04/2021 12:26:45", "23/04/2021 …
## $ willingness <dbl> 10, 10, 3, 7, 10, 10, 9, 10, 9, 5, 10, 10, 10, 9, 10, 10, …
## $ health <dbl> 9, 10, 10, 10, 10, 10, 9, 9, 9, 10, 10, 10, 10, 9, 8, 10, …
## $ fear <dbl> 5, 7, 2, 6, 5, 3, 7, 5, 5, 1, 2, 7, 7, 7, 10, 6, 4, 2, 3, …
## $ cases <dbl> 1, 7, 0, 10, 8, 4, 3, 5, 3, 4, 4, 1, 5, 3, 0, 12, 2, 2, 2,…
## $ extra1 <dbl> 2, 4, 3, 1, 2, 4, 3, 3, 4, 4, 3, 4, 3, 3, 1, 3, 2, 3, 1, 4…
## $ agree1 <dbl> 2, 3, 5, 3, 3, 3, 5, 4, 3, 4, 2, 2, 4, 5, 1, 4, 4, 2, 3, 3…
## $ cons1 <dbl> 3, 5, 4, 3, 3, 4, 2, 4, 3, 5, 1, 4, 2, 2, 5, 4, 3, 2, 1, 5…
## $ neuro1 <dbl> 2, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 3, 5, 3, 5, 3, 3, 2, 3, 3…
## $ open1 <dbl> 4, 3, 5, 2, 3, 5, 4, 4, 5, 5, 2, 4, 5, 4, 3, 4, 4, 5, 4, 4…
## $ extra2 <dbl> 1, 4, 3, 2, 2, 4, 4, 3, 5, 5, 2, 4, 3, 4, 1, 4, 2, 3, 3, 4…
## $ agree2 <dbl> 4, 2, 5, 4, 3, 3, 4, 4, 2, 4, 2, 3, 4, 4, 2, 4, 4, 1, 2, 4…
## $ cons2 <dbl> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 5, 5, 5, 4, 4, 4…
## $ neuro2 <dbl> 4, 2, 3, 4, 4, 3, 4, 2, 3, 2, 2, 3, 4, 4, 5, 1, 4, 2, 5, 3…
## $ open2 <dbl> 4, 5, 5, 2, 3, 4, 5, 4, 5, 5, 4, 3, 2, 5, 5, 5, 5, 5, 2, 4…
## $ age <dbl> 20, 19, 21, 20, 20, 19, 20, 19, 20, 22, 22, 24, 18, 20, 21…
## $ sex <chr> "Mann", "Frau", "Mann", "Frau", "Frau", "Frau", "Frau", "F…
## $ comments <chr> NA, NA, NA, NA, "Bin schon geimpft.", NA, "Ich würde mich …
## $ extra <dbl> 1.5, 4.0, 3.0, 1.5, 2.0, 4.0, 3.5, 3.0, 4.5, 4.5, 2.5, 4.0…
## $ agree <dbl> 3.0, 2.5, 5.0, 3.5, 3.0, 3.0, 4.5, 4.0, 2.5, 4.0, 2.0, 2.5…
## $ neuro <dbl> 3.0, 3.0, 3.0, 4.0, 3.5, 3.5, 3.5, 2.5, 3.0, 3.0, 2.5, 3.0…
## $ cons <dbl> 3.5, 4.5, 4.0, 3.5, 3.5, 4.0, 3.0, 4.0, 3.5, 4.5, 2.0, 4.0…
## $ open <dbl> 4.0, 4.0, 5.0, 2.0, 3.0, 4.5, 4.5, 4.0, 5.0, 5.0, 3.0, 3.5…
2.3 Daten und Variablen
Über die Umfrage kann der Inhalt der Variablen eingesehen werden. Weiter ist eine grundlegende Datenaufbereitung hier dokumentiert.
Das Codebook findet sich im gleichen Repo.
2.4 Sind die Items schon umgepolt?
Ja, ein Blick in die Datenaufbereitung zeigt, dass die (ursprünglich) negativ gepolten Items bereits “richtig” (positiv) gepolt vorliegen.
2.5 Liegen Mittelwerte für die Persönlichkeits-Dimensionen vor?
Ja, es liegen bereits Mittelwerte (“Scores”) für die fünf Big-Five-Persönlichkeitsdimensionen vor.
3 Daten verstehen
3.1 Fehlende Werte
Summieren wir die fehlenden Werte über alle Spalten:
d %>%
summarise(across(everything(), ~ sum(is.na(.))))
## # A tibble: 1 x 23
## timestamp willingness health fear cases extra1 agree1 cons1 neuro1 open1
## <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
## 1 0 0 0 0 0 0 0 0 0 0
## # … with 13 more variables: extra2 <int>, agree2 <int>, cons2 <int>,
## # neuro2 <int>, open2 <int>, age <int>, sex <int>, comments <int>,
## # extra <int>, agree <int>, neuro <int>, cons <int>, open <int>
Nur bei den Kommentaren fehlen Werte. Das macht uns das Leben leichter: Wir müssen uns nicht um fehlende Werte kümmern.
Natürlich hätte man das einfacher schreiben können (aber mit mehr Tippaufwand):
d %>%
summarise(sum(is.na(willingness)))
## # A tibble: 1 x 1
## `sum(is.na(willingness))`
## <int>
## 1 0
Und entsprechend für alle anderen Variablen des Datensatzes.
3.2 Nominal skalierte Variablen in numerische umwandeln
Welche metrisch und welche nominal skalierten Variablen liegen vor? Gut, man könnte einen einfachen Blick in die Tabelle werfen, aber so ist es etwas cooler:
d %>%
select(where(is.numeric)) %>%
names()
## [1] "willingness" "health" "fear" "cases" "extra1"
## [6] "agree1" "cons1" "neuro1" "open1" "extra2"
## [11] "agree2" "cons2" "neuro2" "open2" "age"
## [16] "extra" "agree" "neuro" "cons" "open"
Gegenprobe, die nicht numerischen:
d %>%
select(where(~!is.numeric(.))) %>%
names()
## [1] "timestamp" "sex" "comments"
Geschlecht ist ein Kandidat, der – wenn es nur zwei Werte gibt – mit Nutzen in eine numerische Variable umgewandelt werden kann.
d %>%
count(sex)
## # A tibble: 3 x 2
## sex n
## <chr> <int>
## 1 Divers 1
## 2 Frau 129
## 3 Mann 34
Da es von einer Stufe (divers
) nur eine Beobachtung gibt, bietet es sich an, diese Beobachtung entweder der Gruppe (Frau oder Mann) zuzuordnen, für die die restlichen Werte typischer sind oder diese Beobachtung aufzugeben. Der Einfachheit halber entscheide ich mich für Lezteres.
d2 <-
d %>%
mutate(sex = case_when(
sex == "Divers" ~ NA_character_,
TRUE ~ sex
))
d2 %>%
count(sex)
## # A tibble: 3 x 2
## sex n
## <chr> <int>
## 1 Frau 129
## 2 Mann 34
## 3 <NA> 1
d2 <-
d2 %>%
mutate(is_female = case_when(
sex == "Frau" ~ 1,
sex == "Mann" ~ 0,
TRUE ~ NA_real_
)) %>%
select(-sex)
d2 %>%
count(is_female)
## # A tibble: 3 x 2
## is_female n
## <dbl> <int>
## 1 0 34
## 2 1 129
## 3 NA 1
Jetzt können wir is_female
z.B. für eine Korrelation heranziehen. Aber Achtung: Die Schiefe der Verteilung limitiert die Höhe der Korrelation!
3.3 Welche Variablen korrelieren mit der Impfbereitschaft?
d2 %>%
select(where(is.numeric)) %>%
select(-matches("[[:digit:]]")) %>%
correlate() %>%
focus(willingness) %>%
arrange(-abs(willingness)) # uns interessiert nur der Absolutwert der Korrelation
## # A tibble: 10 x 2
## term willingness
## <chr> <dbl>
## 1 fear 0.396
## 2 cons -0.200
## 3 extra -0.184
## 4 cases -0.160
## 5 health -0.121
## 6 neuro 0.101
## 7 open -0.0648
## 8 is_female 0.0178
## 9 agree 0.00516
## 10 age -0.00455
Hier haben wir Variablen, deren Namen eine Zahl beinhalteten, entfernt. Der Grund ist, dass diese Variablen (neuro1
, …) einzelne Items codieren, hier aber nur die ganze Skala einer Big-Five-Dimension betrachtet werden soll.
3.4 Korrelation der Items pro Big-Five-Dimension
Theoretisch müssten die Items jeweils einer Big-Five-Dimension hoch korrelieren. Wenn dem so ist, dann macht es Sinn, die Items (Fragen) einer Dimension zu einem Wert zusammenzufassen (ansonsten nicht). Überprüfen wir das mal.
d2 %>%
select(matches("[[:digit:]]")) %>% # Wählt spalten, die Zahlen enthalten
pivot_longer(everything()) %>%
mutate(dimension = str_extract(name, "\\D+")) %>% # alle Nicht-Ziffern
mutate(name = case_when(
str_detect(name, "1") ~ "i1", # Findet Elemente der Variablen `name`, die den Wert `1` enthalten
str_detect(name, "2") ~ "i2",
TRUE ~ NA_character_
)) %>%
pivot_wider(names_from = "name",
values_from = "value") %>%
mutate(cor_i1_i2 = map2_dbl(.x = i1,
.y = i2,
.f = cor))
## # A tibble: 5 x 4
## dimension i1 i2 cor_i1_i2
## <chr> <list> <list> <dbl>
## 1 extra <dbl [164]> <dbl [164]> 0.679
## 2 agree <dbl [164]> <dbl [164]> 0.262
## 3 cons <dbl [164]> <dbl [164]> 0.417
## 4 neuro <dbl [164]> <dbl [164]> 0.432
## 5 open <dbl [164]> <dbl [164]> 0.422
Puh, das war schon etwas Daten-Gymnastik. Man hätte das gleiche Ergebnis auch einfacher bekommen können, etwa so:
d2 %>%
select(extra1, extra2) %>%
correlate()
## # A tibble: 2 x 3
## term extra1 extra2
## <chr> <dbl> <dbl>
## 1 extra1 NA 0.679
## 2 extra2 0.679 NA
Und so weiter für jede der fünf Big-Five-Dimensionen.
Halten wir fest: Für Verträglichkeit (agree
) ist die Korrelation gering; ansonsten mittel bis gut.
4 Modell mit den Big-Five-Dimensionen als Prädiktoren
In meinem Kopf schwebt die Idee, die fünf Big-Five-Dimensionen als Prädiktoren der Impfbereitschaft zu präsentieren. Der Grund ist einfach: Die Forschungsfrage “Sagt die Persönlichkeit (voraus), wer sich impfen lassen wird und wer nicht?” ist bestechend und daher wert, analysiert zu werden.
lm1 <- lm(willingness ~ extra + neuro + agree + cons + open, data = d2)
tidy(lm1)
## # A tibble: 6 x 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) 11.0 1.73 6.35 0.00000000215
## 2 extra -0.413 0.228 -1.81 0.0721
## 3 neuro 0.0665 0.253 0.263 0.793
## 4 agree 0.116 0.241 0.483 0.630
## 5 cons -0.565 0.246 -2.30 0.0230
## 6 open -0.0338 0.232 -0.145 0.885
Das Ergebnis zeigt: Das Signal-Rausch-Verhältnis ist höchstens bei cons
akzeptabel, und das auch nur grenzwertig.
Reicht das für eine “Story”? Eher nicht.
5 Zur Datenqualität
Insgesamt muss die Datenqualität als eingeschränkt bezeichnet werden: Die Stichprobe ist nicht groß und die Repräsentativität unklar. Non-Response-Bias kann nicht abgeschätzt werden. Daher sollten die Ergebnisse nur mit großem Vorbehalt interpretiert werden.
Kausale Interpretationen der Daten sind kaum möglich.
6 Visualisierung 1
Diese Visualisierung soll eine sehr einfache, eingängige Visualisierung des Zusammenhangs von Persönlichkeit und Impfbereitschaft aufzeigen. Die Visualisierung ist unterkomplex. Mehr dazu weiter unten.
6.1 Median-Aufteilung
Für eine einfaches Verständnis für eine Zielgruppe, die wenig “datenkundig” (data literate) ist, soll hier eine Dichotomisierung der Daten (z.B. anhand des Medians) vollzogen werden.
Ausdrücklich sei gewarnt, dass eine Median-Aufteilung statistisch fahrlässig ist. Diese Methode wird hier nur illustrativ gezeigt.
main_vars <- c("willingness",
"extra",
"open",
"agree",
"neuro",
"cons")
median_split <- function(var) {
ifelse(var > median(var, na.rm = TRUE), 1, 0)
}
d3 <-
d2 %>%
select(all_of(main_vars)) %>%
mutate(across(.cols = all_of(main_vars),
.fns = median_split,
.names = "{.col}_bin" # "bin" wie "binär"
))
Mit names
kann man die Namen der neu erzeugten Spalten wählen: Hier soll der neue Name gleich dem Namen der alten Spalte (“col” wie Spalte) sein plus ein "_bin" hintendran.
Natürlich hätte man das auch wieder einfacher lösen können (aber mit mehr Tipp-Aufwand):
d2 %>%
mutate(willingness_bin = ifelse(willingness > median(willingness), 1, 0)) %>%
select(willingness, willingness_bin)
## # A tibble: 164 x 2
## willingness willingness_bin
## <dbl> <dbl>
## 1 10 1
## 2 10 1
## 3 3 0
## 4 7 0
## 5 10 1
## 6 10 1
## 7 9 0
## 8 10 1
## 9 9 0
## 10 5 0
## # … with 154 more rows
Die Verteilung von willingness
scheint sehr schief zu sein:
d2 %>%
ggplot(aes(x = willingness)) +
geom_density()
6.2 Dichotomisierung anhand des Skalen-Mittelpunkts
Anstelle des Medians soll hier der Skalen-Mittelpunkt zur Dichotomisierung gewählt werden:
scale01 <- function(x) {
rng <- range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
Zuerst skalieren wir auf 0 bis 100%, um die Skalierung anzugleichen:
d4 <-
d2 %>%
select(all_of(main_vars)) %>%
mutate(across(everything(),
scale01,
.names = "{.col}_01"))
head(d4)
## # A tibble: 6 x 12
## willingness extra open agree neuro cons willingness_01 extra_01 open_01
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 10 1.5 4 3 3 3.5 1 0.125 0.75
## 2 10 4 4 2.5 3 4.5 1 0.75 0.75
## 3 3 3 5 5 3 4 0.222 0.5 1
## 4 7 1.5 2 3.5 4 3.5 0.667 0.125 0.25
## 5 10 2 3 3 3.5 3.5 1 0.25 0.5
## 6 10 4 4.5 3 3.5 4 1 0.75 0.875
## # … with 3 more variables: agree_01 <dbl>, neuro_01 <dbl>, cons_01 <dbl>
Dann teilen wir in kleine und große Werte auf: Kleiner 50% soll “klein” sein, sonst “groß”.
bin_01 <- function(var) {
ifelse(var > .5, 1, 0)
}
Anstelle von ifelse
hätten wir auch mit case_when
arbeiten können. ifelse
ist eine einfache Wenn-Dann-Regel: wenndann(prüfung, wenn_prüfung_positiv_tue_dies, ansonsten_das)
.
d5 <-
d4 %>%
select(contains("_01")) %>%
mutate(across(everything(),
bin_01))
dim(d5)
## [1] 164 6
head(d5)
## # A tibble: 6 x 6
## willingness_01 extra_01 open_01 agree_01 neuro_01 cons_01
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 0 1 0 0 1
## 2 1 1 1 0 0 1
## 3 0 0 1 1 0 1
## 4 1 0 0 1 1 1
## 5 1 0 0 0 1 1
## 6 1 1 1 0 1 1
6.3 Anteile berechnen (eindimensional)
Wir berechnen die Anteile für geringe (value==0
) bzw. hohe (value==1
) Werte in den Big-Five-Dimensionen und Impfbereitschaft:
d6 <-
d5 %>%
select(contains("_01")) %>%
pivot_longer(everything()) %>%
count(name, value) %>%
group_by(name) %>%
mutate(prop = n / sum(n))
slice_head(d6)
## # A tibble: 6 x 4
## # Groups: name [6]
## name value n prop
## <chr> <dbl> <int> <dbl>
## 1 agree_01 0 91 0.555
## 2 cons_01 0 61 0.372
## 3 extra_01 0 72 0.439
## 4 neuro_01 0 94 0.573
## 5 open_01 0 45 0.274
## 6 willingness_01 0 27 0.165
6.4 Als Balkendiagramm
Die Tabelle von oben visualisieren wir als Balkendiagramm:
labels_auspraegung <- c("0" = "gering", "1" = "hoch")
labels_bigfive <- c("agree" = "Verträglichkeit",
"cons" = "Gewissenshaftigkeit",
"extra" = "Extraversion",
"neuro" = "Neurotizismus",
"open" = "Offenheit",
"willingness" = "Impfbereitschaft")
d6 %>%
select(name, prop) %>%
mutate(dimension = str_remove(name,"_01")) %>%
group_by(dimension) %>%
mutate(value = c(0,1)) %>%
ungroup() %>%
ggplot() +
aes(x = value, y = prop) +
geom_col() +
facet_wrap(~ dimension,
labeller = labeller(dimension = labels_bigfive)) +
scale_x_continuous(breaks = c(0,1),
labels = labels_auspraegung) +
labs(x = "",
y = "Anteil",
title = "Verteilung von gering bzw. hoch ausgeprägten Werten",
caption = "n=165 Personen, nicht repräsentativ") +
theme_ipsum_rc() +
geom_label(aes(label = round(prop, 2))) +
scale_y_continuous(limits = c(0, 1))
Das Diagramm zeigt allerdings noch keine Kontingenz/Korrespondenz (keinen Zusammenhang) zwischen Impfbereitschaft und den einzelnen Big-Five-Dimensionen. Also weiter.
6.5 Anteil hoher Big-Five-Werte bei Impfwilligen (zweidimensional)
d7 <-
d5 %>%
pivot_longer(extra_01:last_col()) %>%
group_by(name, willingness_01) %>%
count(value) %>%
mutate(prop = n / sum(n))
d7
## # A tibble: 20 x 5
## # Groups: name, willingness_01 [10]
## name willingness_01 value n prop
## <chr> <dbl> <dbl> <int> <dbl>
## 1 agree_01 0 0 18 0.667
## 2 agree_01 0 1 9 0.333
## 3 agree_01 1 0 73 0.533
## 4 agree_01 1 1 64 0.467
## 5 cons_01 0 0 5 0.185
## 6 cons_01 0 1 22 0.815
## 7 cons_01 1 0 56 0.409
## 8 cons_01 1 1 81 0.591
## 9 extra_01 0 0 7 0.259
## 10 extra_01 0 1 20 0.741
## 11 extra_01 1 0 65 0.474
## 12 extra_01 1 1 72 0.526
## 13 neuro_01 0 0 18 0.667
## 14 neuro_01 0 1 9 0.333
## 15 neuro_01 1 0 76 0.555
## 16 neuro_01 1 1 61 0.445
## 17 open_01 0 0 6 0.222
## 18 open_01 0 1 21 0.778
## 19 open_01 1 0 39 0.285
## 20 open_01 1 1 98 0.715
So ist z.B. der Anteil der gering Extrovertierten (=introvertiert) an den hoch Impfbereiten ca. 47%; der Anteil der hoch Extrovertierten an den hoch Impfbereiten ca. 53%. Wir summieren also pro Stufe von Impfbereitschaft auf.
name_order <-
d7 %>%
filter(value == 1, willingness_01 == 1) %>%
arrange(-prop) %>%
pull(name)
name_order
## [1] "open_01" "cons_01" "extra_01" "agree_01" "neuro_01"
name_order_de <- c(
"hohe Offenheit",
"hohe Gewissenhaftigkeit",
"hohe Extraversion",
"hohe Verträglichkeit",
"hohe Neurotizismus"
)
d8 <-
d7 %>%
mutate(name = str_extract(name, ".*[^_01]")) %>%
mutate(name = factor(name)) %>%
ungroup()
6.6 Kontingenzbalken 1
d8 %>%
glimpse()
## Rows: 20
## Columns: 5
## $ name <fct> agree, agree, agree, agree, cons, cons, cons, cons, ext…
## $ willingness_01 <dbl> 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1…
## $ value <dbl> 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0…
## $ n <int> 18, 9, 73, 64, 5, 22, 56, 81, 7, 20, 65, 72, 18, 9, 76,…
## $ prop <dbl> 0.6666667, 0.3333333, 0.5328467, 0.4671533, 0.1851852, …
d8a <-
d8 %>%
filter(value == 1)
d8a %>%
glimpse()
## Rows: 10
## Columns: 5
## $ name <fct> agree, agree, cons, cons, extra, extra, neuro, neuro, o…
## $ willingness_01 <dbl> 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
## $ value <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
## $ n <int> 9, 64, 22, 81, 20, 72, 9, 61, 21, 98
## $ prop <dbl> 0.3333333, 0.4671533, 0.8148148, 0.5912409, 0.7407407, …
d8a %>%
mutate(Impfbereitschaft = case_when(
willingness_01 == 0 ~ "geringer",
willingness_01 == 1 ~ "höher"
)) %>%
ggplot() +
aes(x = name, y = n, fill = as.factor(Impfbereitschaft)) +
geom_col(position = "dodge") +
scale_fill_manual(values = c("indianred2", "palegreen4")) +
coord_flip() +
scale_x_discrete(labels = labels_bigfive) +
labs(x = NULL,
y = "Anteil",
fill = "Impfbereitschaft",
title = "Menschen mit hohe Verträglichkeit\nzeigen hohe Impfbereitschaft",
subtitle = "Nur Menschen mit hoher Ausprägung pro Persönlichkeitsdimension",
caption = "n=164; nicht repräsentative Stichprobe. Nur eingeschränkt interpretierbar") +
theme_ipsum_rc() +
theme(legend.position = "bottom")
6.7 Kontingenzbalken 2
Oder besser vielleicht nicht nur die hohen Ausprägungen der Big-Five-Dimensionen, sondern auch die geringen.
Zuerst erstellen wir einen Hilfs-Datensatz für die Zahlen-Labels auf den Balken:
d8b <-
d8 %>%
filter(willingness_01 == 1)
d8b %>%
slice_head(n=10)
## # A tibble: 10 x 5
## name willingness_01 value n prop
## <fct> <dbl> <dbl> <int> <dbl>
## 1 agree 1 0 73 0.533
## 2 agree 1 1 64 0.467
## 3 cons 1 0 56 0.409
## 4 cons 1 1 81 0.591
## 5 extra 1 0 65 0.474
## 6 extra 1 1 72 0.526
## 7 neuro 1 0 76 0.555
## 8 neuro 1 1 61 0.445
## 9 open 1 0 39 0.285
## 10 open 1 1 98 0.715
Ok, los geht’s:
d8 %>%
#filter(willingness_01 == 1) %>%
mutate(Impfbereitschaft = case_when(
willingness_01 == 0 ~ "gering",
willingness_01 == 1 ~ "hoch"
)) %>%
ggplot() +
aes(x = name, y = prop) +
geom_col(aes(fill = as.factor(Impfbereitschaft)),
position = "fill") +
scale_fill_manual(values = c("indianred2", "palegreen4")) +
coord_flip() +
scale_x_discrete(labels = labels_bigfive) +
labs(x = NULL,
y = "Anteil",
fill = "Impfbereitschaft",
title = "Hat das Persönlichkeitsprofil einen Einfluss auf die Impfbereitschaft?",
subtitle = "Personen mit geringer (hoher) Ausprägung\nin den Persönlichkeitsdimensionen sind links (rechts) abgebildet",
caption = "n=164; nicht repräsentative Stichprobe. Nur eingeschränkt interpretierbar") +
theme_ipsum_rc() +
theme(legend.position = "bottom") +
facet_wrap(~ value,
labeller = labeller(value = labels_auspraegung)) +
scale_y_continuous(breaks = c(0, .5, 1))
#
# geom_label(data = d8b,
# aes(label = round(n, 2)))
Vielleicht wäre es interessanter, die Ungleichheit in den Ausprägungen der Persönlichkeitsdimensionen (Hohe Werte vs. niedrige Werte) in den Hoch- und Niedrig-Impfwilligen zu berechnen. Also nochmal weiter.
6.8 Größter Unterschied zwischen den Big-Five-Dimensionen bei den Hoch-Impfbereiten
d9 <-
d8 %>%
arrange(name, value) %>%
group_by(name, value) %>%
summarise(diff_hoch_vs_gering_impfwillig = prop[[2]] - prop[[1]]) %>%
filter(value == 1) %>%
ungroup() %>%
mutate(diff_abs = abs(diff_hoch_vs_gering_impfwillig)) %>%
mutate(is_high = percent_rank(diff_abs) > .7)
d9
## # A tibble: 5 x 5
## name value diff_hoch_vs_gering_impfwillig diff_abs is_high
## <fct> <dbl> <dbl> <dbl> <lgl>
## 1 agree 1 0.134 0.134 FALSE
## 2 cons 1 -0.224 0.224 TRUE
## 3 extra 1 -0.215 0.215 TRUE
## 4 neuro 1 0.112 0.112 FALSE
## 5 open 1 -0.0624 0.0624 FALSE
Der Effekt bei den Gewissenhaften ist am stärksten. Allerdings ist der Effekt bei den Extravertierten besser zu interpretieren: “Die Extrovertierten sind weniger impfbereit als die introvertierten”. Außerdem ist der Effekt fast genau so stark.
6.9 Plot Zur Differenz
d9 %>%
ggplot(aes(x = reorder(name, diff_abs), y = diff_abs)) +
geom_hline(color = "white", yintercept = 0, size = 3) +
geom_col(aes(fill = is_high)) +
coord_flip() +
scale_x_discrete(labels = labels_bigfive) +
scale_fill_manual(guide = FALSE,
values = c("grey50", "royalblue4")) +
labs(x = "",
y = "Absolutwert des Unterschieds",
subtitle = "Unterschied der Impfbereitschaft\nabhängig von der Ausprägung des Persönlichkeitsmerkmals",
title = "Bei Gewissenhaften und Extrovertierten \nhängt die Impfbereitschaft relativ stark \nvon der Ausprägung des Persönlichkeitsmerksmals ab",
is_high = "") +
theme_ipsum_rc()
6.10 Korrespondenz (Kontingenz) von Extraversion und Impfbereitschaft
d8 %>%
filter(name == "extra_01")
## # A tibble: 0 x 5
## # … with 5 variables: name <fct>, willingness_01 <dbl>, value <dbl>, n <int>,
## # prop <dbl>
d8 %>%
filter(name == "extra") %>%
rename(Extraversion = value,
Anteil = prop,
Impfbereitschaft = willingness_01) %>%
mutate(Extraversion = case_when(
Extraversion == 0 ~ "introvertiert",
Extraversion == 1 ~ "extravertiert",
)) %>%
mutate(Impfbereitschaft = case_when(
Impfbereitschaft == 0 ~ "gering",
Impfbereitschaft == 1 ~ "hoch",
)) %>%
ggplot(aes(x = Extraversion,
y = Anteil, fill = Impfbereitschaft)) +
geom_col(position = "fill") +
labs(title = "Introvertierte sind impfbereiter") +
scale_fill_brewer(type = "qual", palette = 6)
Das ist aus Kommunikationssicht eine passable Botschaft.
ACHTUNG Die wissenschaftliche Belastbarkeit dieser Aussage ist wackelig!
Übrigens: Hier findet sich ein Überblick an qualitativen Farbpaletten für ggplot2
.
6.11 Waffel-Diagramm
willingness_low <- c("geringe Extraversion" = 26,
"hohe Extraversion" = 74)
Zur Installation von Font Awesome:
library(extrafont)
library(waffle)
waffle::install_fa_fonts()
font_import(paths = "/Users/sebastiansaueruser/Rlibs/waffle/fonts/", prompt = F)
waffle(willingness_low,
flip = TRUE,
reverse = TRUE,
use_glyph = "syringe",
colors = c("grey60", "dodgerblue"),
size = .1,
title = "Persönlichkeitsmerkmal 'Extraversion' \nunter Personen mit geringer Impfbereitschaft") +
expand_limits(y = c(0, 4)) +
theme(legend.position = "bottom")
#ggsave(test.png)
6.12 Font-Awesome-Visualisierung (Pictogramm)
d8a <-
d8 %>%
filter(name == "extra") %>%
mutate(willingness_01 = factor(willingness_01)) %>%
ungroup() %>%
select(willingness_01, n, value, prop) %>%
mutate(n = as.numeric(n)) %>%
mutate(value = as.factor(value))
d8a
## # A tibble: 4 x 4
## willingness_01 n value prop
## <fct> <dbl> <fct> <dbl>
## 1 0 7 0 0.259
## 2 0 20 1 0.741
## 3 1 65 0 0.474
## 4 1 72 1 0.526
Facetten-Labels:
levels(d8a$value)
## [1] "0" "1"
levels(d8a$value) <- c("introvertiert ", "extrovertiert")
p_final1 <- d8a %>%
ggplot(aes(label = willingness_01,
values = n)) +
geom_pictogram(n_rows = 20,
size = 3,
aes(colour = willingness_01),
flip = TRUE,
make_proportional = TRUE) +
scale_color_manual(
name = NULL,
values = c("#a40000", "chartreuse4"),
labels = c("gering impfbereit", "hoch impfbereit")
) +
scale_label_pictogram(
name = NULL,
values = c("times", "syringe"),
labels = c("gering impfbereit", "hoch impfbereit")
) +
coord_equal() +
theme_ipsum_rc(grid="") +
theme_enhance_waffle() +
theme(legend.key.height = unit(1, "line")) +
theme(legend.text = element_text(size = 8,
hjust = 0, vjust = 0)) +
facet_wrap(~ value, nrow = 1) +
theme(legend.position = "bottom") +
theme(legend.text = element_text(hjust = 0, vjust = 0.5)) +
labs(title = "Introvertierte Menschen sind impfbereiter als extrovertierte",
caption = "N=164, nicht repräsentativ") +
theme(plot.title = element_text(size = 12))
p_final1
Bild speichern:
ggsave("impf1.png", dpi = 300)
Whitespace abschneiden:
img <- image_read("impf1.png")
img2 <- image_trim(img)
image_write(img2, path = "impf2.png" )
6.13 Was für ein Aufwand …
Der vergleichsweise hohe Aufwand für diese Visualisierung ist aufschlussreich. Der eine Grund ist in der Verwendung eines ungewöhnlichen Geoms – Pictogramm – zu suchen. Der zweite Grund, und das ist der Hauptgrund, ist, dass die Datenaufbereitung bzw. Datenumformung der eigentliche Zeitfresser gewesen ist. Vielleicht noch pointierter formuliert: Es hat gedauert, bis die Datenlage verstanden war, und verstanden war, was eigentlich dargestellt werden soll.
6.14 Fazit
Die wissenschaftliche Belastbarkeit der Studie ist fraglich. Mehr noch: Die Nützlichkeit eines Waffel-Diagramms (oder Pictogramms) ist ebenfalls fragwürdig. Wahrnehmungspsychologisch sind andere Darstellungsformen – etwa ein Balkendiagramme für Häufigkeiten – besser geeignet. Darüber hinaus: Die Dichotomisierung der Daten, also der Verlust von Skaleninformation ist statistisch zumeist nicht zu begründen. Alles in allem steht in dieser Analyse der didaktische Blick zur Frage “Wie erstelle ich ein Diagramm, das unterhält?” im Vordergrund. Die wissenschaftlichen “Kosten” eines solchen Vorgehens spielten hier keine Rolle. Entsprechend eingeschränkt ist die Interpretierbarkeit dieser Analyse bzw. dieses Diagramms. Nur mit äußerster Vorsicht zu interpretieren!
7 Visualisierung 2
7.1 Visualisierung der Regressionskoeffizienten
Die Werte der Regressionskoeffizienten (Betas) darzustellen, ist eine wissenschaftlich nützliche Repräsentation der Analyse der Prädiktoren der Impfbereitschaft – wenn auch für ein Laien-Publikum weniger geeignet.
vars_lm2 <- c("willingness",
"extra",
"neuro",
"agree",
"cons",
"open")
preds_lm2 <- setdiff(vars_lm2, "willingness")
preds_lm2
## [1] "extra" "neuro" "agree" "cons" "open"
d2a <-
d2 %>%
select(all_of(vars_lm2)) %>%
mutate(across(.cols = all_of(preds_lm2),
.fns = scale) )
d2a %>%
slice_head()
## # A tibble: 1 x 6
## willingness extra[,1] neuro[,1] agree[,1] cons[,1] open[,1]
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 10 -1.87 -0.0678 -0.136 0.0811 0.164
lm2 <- lm(willingness ~ ., data = d2a)
tidy(lm2)
## # A tibble: 6 x 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) 8.10 0.206 39.3 2.01e-83
## 2 extra -0.414 0.229 -1.81 7.21e- 2
## 3 neuro 0.0598 0.227 0.263 7.93e- 1
## 4 agree 0.102 0.211 0.483 6.30e- 1
## 5 cons -0.489 0.213 -2.30 2.30e- 2
## 6 open -0.0308 0.212 -0.145 8.85e- 1
7.2 Variante A
ggcoef(lm2,
exclude_intercept = TRUE,
color = "blue", sort = "ascending") +
scale_y_discrete(labels = labels_bigfive) +
labs(y = "",
x = "Relevanz für die Impfbereitschaft",
title = "Für Gewissenshaftigkeit fand sich ein Einfluss auf die Impfbereitschaft",
caption = "N=164; nicht repräsentativ.\nDargestellt sind die Regressionskoeffizienten (z-skalierte Prädiktoren) zur Impfbereitschaft. Die horizontalen Balken zeigen 95%-Konfidenzintervalle.") +
theme_minimal()
7.3 Variante B
p_final2 <- ggcoef_model(lm2,
conf.int = TRUE,
variable_labels = labels_bigfive,
show_p_values = FALSE,
facet_row = NULL,
signif_stars = FALSE) +
labs(title = "Für Gewissenshaftigkeit fand sich ein Einfluss auf die Impfbereitschaft",
caption = "N=164; nicht repräsentativ.\nDargestellt sind die Regressionskoeffizienten (z-skalierte Prädiktoren) zur Impfbereitschaft.\nDie horizontalen Balken zeigen 95%-Konfidenzintervalle.")
p_final2
7.4 Variante C: Mit Bordmitteln
Man hätte das Diagramm auch ohne Extra-Paket erstellen können, wenn auch etwas weniger poliert (wenn man Extraufwand meiden möchte):
lm2 %>%
tidy()
## # A tibble: 6 x 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) 8.10 0.206 39.3 2.01e-83
## 2 extra -0.414 0.229 -1.81 7.21e- 2
## 3 neuro 0.0598 0.227 0.263 7.93e- 1
## 4 agree 0.102 0.211 0.483 6.30e- 1
## 5 cons -0.489 0.213 -2.30 2.30e- 2
## 6 open -0.0308 0.212 -0.145 8.85e- 1
7.5 Fazit
Diese Art der Darstellung ist wissenschaftlich gut geeignet, verlangt aber höhere Fachkentnisse von den Betrachtis.
8 Finale Visualisierungen
8.1 Piktogramm
p_final1
8.2 Beta-Diagramm
p_final2
```