Kapitel 70 ggplot Balkengraphiken mit N und Prozenten
Das Erstellen von Balkengraphiken ist im Prinzip relativ einfach, doch die korrekte Beschriftung der Balken mit absoluten und relativen Häufigkeiten kann etwas komplexer sein.
Achtung: Wir erstellen hier nur einfache Graphiken, wir verzichten hier auf das Verschönern der Graphiken - was wir für eine Veröffentlichung jeweils sicher noch tun müssten.
Zum Thema Verschönern von Graphiken gibt es später ein eigenes Kapitel.
70.0.1 Pakete
library(ggplot2)
library(dplyr)
library(stringr)
library(tidyr)
library(arsenal)
library(questionr)
70.0.2 Daten aus veröffentlichtem Artikel
Für dieses Beispiel nutzen wir Daten aus folgenden veröffentlichten Artikel: Sullivan, A., Murray, E. J., & Corlin, L. (2022). Academic training of authors publishing in high-impact epidemiology and clinical journals. PloS one, 17(7), e0271159..
<-rio::import("https://doi.org/10.1371/journal.pone.0271159.s002", format="csv")
data::datatable(data, filter='top',options = list(
DTpageLength=10, scrollX='800px'))
70.1 Barpplot mit nur einer möglichen Antwortskategorie
Wenn wir eine Frage haben, bei der die Teilnehmenden nur eine Antwort ankreuzen konnten, ist die Darstellung relativ einfach.
Zuerst sollten wir uns einen Überblick über die Daten erschaffen.
::freq(data$journal_type) summarytools
## Frequencies
## data$journal_type
## Type: Character
##
## Freq % Valid % Valid Cum. % Total % Total Cum.
## ------------------------ ------ --------- -------------- --------- --------------
## Epidemiology 36 34.95 34.95 34.95 34.95
## General clinical 42 40.78 75.73 40.78 75.73
## Specialty clinical 25 24.27 100.00 24.27 100.00
## <NA> 0 0.00 100.00
## Total 103 100.00 100.00 100.00 100.00
ggplot(data, aes(x=journal_type, y=""))+
geom_col(stat='count')
## Warning in geom_col(stat = "count"): Ignoring unknown parameters: `stat`
Das Hinzufügen der Anzahl pro Balken ist auch einfach, wir müssen dazu jedoch den Code etwas umschreiben. Lösung von hier: https://r-graphics.org/recipe-bar-graph-labels.
ggplot(data, aes(x=journal_type))+
geom_bar()+
geom_text(aes(label = ..count..), stat = "count", vjust = 1.5, colour = "white")
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
Wollen wir absolute Häufigkeit und relative Häufigkeit, gibt es verschiedene Lösungen
Variante 1 https://stackoverflow.com/questions/70258531/adding-the-text-of-count-and-percentage-by-the-group-in-a-bar-chart. Bei dieser Variante benutzen wir die dplyr Funktion count.
<- data %>%
df_bar1 count(journal_type) %>%
mutate(
perc = round(proportions(n) * 100, 1),
res = str_c(n, "(", perc, "%)"),
journal_type = as.factor(journal_type)
)
ggplot(df_bar1, aes(journal_type, n, fill = journal_type)) +
geom_col() +
geom_text(aes(label = res), vjust = -0.5)
Variante 2 In dieser variante erstellen wir zuerst ein summary Dataframe, in dem wir alle Statistiken, die wir für die Graphiken möchten, selber berechnen und auch die Labels so erstellen, wie wir sie möchten. Wir können so zum Beispiel unterschiedliche Beschriftungen erstellen, hier ein langes und ein kurzes Label.
<-data %>%
summary_journal_typegroup_by(journal_type) %>%
::summarise(absoluteFreq=n()) %>%
dplyrmutate(relativeFreq = round(absoluteFreq / sum(absoluteFreq)*100))
$label = paste(summary_journal_type$journal_type,": ", summary_journal_type$absoluteFreq," (", summary_journal_type$relativeFreq,"%)", sep = "")
summary_journal_type$label_kurz = paste(summary_journal_type$absoluteFreq," (", summary_journal_type$relativeFreq,"%)", sep = "") summary_journal_type
Hier nun die Balkengraphik:
ggplot(summary_journal_type, aes(x=journal_type, y=absoluteFreq,))+
geom_col()+
geom_text(aes(label = label_kurz), vjust = -0.5)+
theme_classic()
70.2 Balkengraphik mit Fragen mit mehr als einer möglichen Antwort
Bei Fragen mit mehreren möglichen Antworten müssen wir beim Berechnen der relativen Häufigkeiten vorsichtig sein (was ist genau der Nenner?).
Am besten erstellen wir ein Datenframe im Long Format und berechnen die absoluten und relativen Häufigkeiten.
Zuerst sollten wir uns wieder einen Überblick über die Daten beschaffen. Am einfachsten geht dies mit dem tableby Befehl - aber auch hier gibt es tausend andere Möglichkeiten.
::lookfor(data, "high") questionr
pos variable label col_type values
3 highdegree_PhD — int
4 highdegree_masters — int
5 highdegree_epi — int
6 highdegree_biostats — int
7 highdegree_clinical — int
8 highdegreecountry_US — int
9 highdegreecountry_Europe — int
27 highdegree_MDorMDPhD — int
28 highdegree_clincialORmdORmdphd — int
<-data %>%
data_highdegree::select(randomID, contains("high")) %>%
dplyrmutate(across(contains("high"), factor))
<-arsenal::tableby(~highdegree_PhD+highdegree_masters+highdegree_epi+highdegree_biostats+highdegree_clinical+highdegreecountry_US+highdegree_MDorMDPhD+highdegree_clincialORmdORmdphd, data=data_highdegree)
table_highdegreessummary(table_highdegrees,cat.simplify=FALSE)
Overall (N=103) | |
---|---|
highdegree_PhD | |
0 | 61 (59.2%) |
1 | 42 (40.8%) |
highdegree_masters | |
0 | 92 (89.3%) |
1 | 11 (10.7%) |
highdegree_epi | |
0 | 55 (53.4%) |
1 | 48 (46.6%) |
highdegree_biostats | |
0 | 93 (90.3%) |
1 | 10 (9.7%) |
highdegree_clinical | |
0 | 66 (64.1%) |
1 | 37 (35.9%) |
highdegreecountry_US | |
N-Miss | 3 |
0 | 32 (32.0%) |
1 | 68 (68.0%) |
highdegree_MDorMDPhD | |
0 | 65 (63.1%) |
1 | 38 (36.9%) |
highdegree_clincialORmdORmdphd | |
0 | 59 (57.3%) |
1 | 44 (42.7%) |
Wir können diese Tabelle auch noch etwas kürzer machen, indem wir nur die jeweils zweite Antwortsoption (hier die 1) ausdrucken (mit der Option cat.simplify=TRUE).
summary(table_highdegrees,cat.simplify=TRUE)
Overall (N=103) | |
---|---|
highdegree_PhD | 42 (40.8%) |
highdegree_masters | 11 (10.7%) |
highdegree_epi | 48 (46.6%) |
highdegree_biostats | 10 (9.7%) |
highdegree_clinical | 37 (35.9%) |
highdegreecountry_US | |
N-Miss | 3 |
0 | 32 (32.0%) |
1 | 68 (68.0%) |
highdegree_MDorMDPhD | 38 (36.9%) |
highdegree_clincialORmdORmdphd | 44 (42.7%) |
<-data %>%
df_highdegree_long::select(randomID, contains("high")) %>%
dplyrmutate(across(contains("high"), factor)) %>%
pivot_longer(cols=-randomID)
<-df_highdegree_long %>%
summary_highdegreegroup_by(name, value) %>%
::summarise(absoluteFreq=n()) %>%
dplyrmutate(relativeFreq = round(absoluteFreq / sum(absoluteFreq)*100)) %>%
::filter(value==1) %>%
dplyrmutate(label=paste(name,": ", absoluteFreq," (", relativeFreq,"%)", sep = "")) %>%
mutate(label_kurz=paste(absoluteFreq," (", relativeFreq,"%)", sep=""))
## `summarise()` has grouped output by 'name'. You can override using the
## `.groups` argument.
%>%
summary_highdegree mutate(Rating_Skala_1 = forcats::fct_reorder(name, desc(relativeFreq))) %>%
ggplot(aes(x=name, y=absoluteFreq,))+
geom_col(width=0.5)+
geom_text(aes(label = label_kurz), hjust = -0.1)+
theme_classic()+
scale_y_continuous(breaks=seq(0,100,10), limits=c(0,100))+
labs(y="Prozente", x="Item High Degree (Multiple responses possible)")+
theme(axis.title.x = element_text(margin = margin(t = 10, r = 0, b = 5, l = 0)))+
theme(axis.title.y = element_text(margin = margin(t = 0, r = 0, b = 0, l = 5)))+
coord_flip()
Wir könnten als Lösung die length2 Funktion benutzen http://www.cookbook-r.com/Manipulating_data/Summarizing_data/.
<- function (x, na.rm=FALSE) {
length2 if (na.rm) sum(!is.na(x))
else length(x)
}
Hiermit können wir dann für jede Variable die Anzahl Teilnehmenden mit nichtfehlenden Werten zählen. Der folgende Code ist nicht sehr elegant, wer einen besseren Vorschlag hat, soll ihn doch bitte unten rechts im Chat hinterlassen.
<-df_highdegree_long %>%
summary_highdegreegroup_by(name) %>%
mutate(total_per_name=length2(value,na.rm=TRUE)) %>%
group_by(name, value) %>%
::summarise(absoluteFreq=n(),
dplyrtotal_per_name=max(total_per_name)) %>%
mutate(relativeFreq = round(absoluteFreq / total_per_name*100)) %>%
::filter(value==1) %>%
dplyrmutate(label=paste(name,": ", absoluteFreq," (", relativeFreq,"%)", sep = "")) %>%
mutate(label_kurz=paste(absoluteFreq," (", relativeFreq,"%)", sep=""))
## `summarise()` has grouped output by 'name'. You can override using the
## `.groups` argument.
%>%
summary_highdegree mutate(Rating_Skala_1 = forcats::fct_reorder(name, desc(relativeFreq))) %>%
ggplot(aes(x=name, y=absoluteFreq,))+
geom_col(width=0.5)+
geom_text(aes(label = label_kurz), hjust = -0.1)+
theme_classic()+
scale_y_continuous(breaks=seq(0,100,10), limits=c(0,100))+
labs(y="Prozente", x="Item High Degree (Multiple responses possible)")+
theme(axis.title.x = element_text(margin = margin(t = 10, r = 0, b = 5, l = 0)))+
theme(axis.title.y = element_text(margin = margin(t = 0, r = 0, b = 0, l = 5)))+
coord_flip()
Ind diesem Fall wird es schwieriger, die totale Anzahl an Personen zu bestimmen, die die Frage beantwortet haben. Wenn zum Beispiel im Online-Fragebogen eine branching-logik benutzt wurde, könnte man diese Variablen der branching logic benutzen zum Bestimmen der Teilnehmenden, die diese Frage beantworten hätten sollen. Man könnte auch berechnen, wie viele mindestens eine Option gewählt haben - dies macht allerdings nur Sinn, wenn die Frage so gestellt wurde, dass alle eine Antwortsoption wählen sollten und können.