Nicosc
Nicosc

Reputation: 323

Density chart with several labelled geom_vline() as means in ggplot

I want to create a density chart showing the distribution of the variable "approval_gov". Now I want to have dashed lines showing the mean of all respondents, plus the means of different independent variables like gender, age cohorts and income levels. The labels should include the mean and the name of the group (e.g. Male: mean). Or they should include only the means and the group name in a legend. I tried several approchaes, but I only managed to get a dashed line for the mean in general, but without any label.

These are the first 20 rows:

lebanon <- structure(list(gender = structure(c(2L, 1L, 1L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 1L, 2L, 2L, 1L, 2L, 1L, 1L, 2L, 1L, 1L), .Label = c("Female", 
"Male"), class = "factor"), approval_gov = c(9L, 7L, 8L, 6L, 
6L, 4L, 0L, 0L, 0L, 1L, 5L, 2L, 4L, 2L, 4L, 0L, 0L, 4L, 2L, 2L
), age_cat = structure(c(3L, 2L, 2L, 1L, 1L, 2L, 2L, 3L, 5L, 
1L, 1L, 1L, 1L, 3L, 2L, 2L, 1L, 3L, 1L, 4L), .Label = c("18-29", 
"30-39", "40-49", "50-59", "60 and above"), class = "factor"), 
    income_recoded = structure(c(2L, 1L, 1L, 1L, 1L, 1L, 2L, 
    2L, 1L, 3L, 2L, 2L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 2L), .Label = c("Low income", 
    "Middle income", "High income", "Don't know"), class = "factor")), row.names = c(NA, 
20L), class = "data.frame")

I managed to get this far, but then everything messes up when I try to add labels and group means of several columns in addition the overall mean:

lebanon %>%
  filter(!is.na(approval_gov)) %>%
  ggplot(aes(approval_gov)) +
  geom_density(fill = "steelblue", alpha = 0.5) +
  geom_vline(aes(xintercept=mean(approval_gov), color = approval_gov),
             color="blue", linetype="dashed", size=1) +
  scale_y_continuous(labels = scales::percent) +
  scale_x_continuous(breaks = c(0:10)) +
  labs(title = " To what extent are you satisfied with the current government's performance?", 
       x = "", y = "", fill = "") +
  theme_minimal() +
  theme(legend.text = element_text(size = 20),
      plot.title = element_text(size = 32, hjust = 0),
      axis.title = element_blank(),
      axis.text = element_text(size = 20, colour = "black"),
      axis.ticks.y = element_line(),
      panel.border = element_rect(colour = "black", fill = NA),
      panel.grid.minor = element_blank(),
      panel.grid.major = element_blank())

enter image description here

Any suggestions? :)

Greetings

Upvotes: 0

Views: 499

Answers (1)

stefan
stefan

Reputation: 124013

This can be achieved like so. Compute the wanted means and put them in one dataframe together with the group_labels. This dataframe can then be used both with geom_vline and geom_text (or ggrepel::geom_label_repel) to draw the lines and add the labels. Try this:

library(dplyr)
library(ggplot2)

means <- list(
  all = summarise(lebanon, value = mean(approval_gov, na.rm = TRUE)) %>% mutate(group = "all"),
  gender = group_by(lebanon, gender) %>% summarise(value = mean(approval_gov, na.rm = TRUE)) %>% rename(group = gender),
  age_cat = group_by(lebanon, age_cat) %>% summarise(value = mean(approval_gov, na.rm = TRUE)) %>% rename(group = age_cat)) %>% 
  bind_rows()

lebanon %>%
  filter(!is.na(approval_gov)) %>%
  ggplot(aes(approval_gov)) +
  geom_density(fill = "steelblue", alpha = 0.5) +
  geom_vline(data = means, aes(xintercept=value, color = group),
             linetype="dashed", size=1, show.legend = FALSE) +
  ggrepel::geom_label_repel(data = means, aes(x = value, y = .05, label = group)) +
  scale_y_continuous(labels = scales::percent) +
  scale_x_continuous(breaks = c(0:10)) +
  labs(title = " To what extent are you satisfied with the current government's performance?", 
       x = "", y = "", fill = "") +
  theme_minimal() +
  theme(legend.text = element_text(size = 20),
        plot.title = element_text(size = 32, hjust = 0),
        axis.title = element_blank(),
        axis.text = element_text(size = 20, colour = "black"),
        axis.ticks.y = element_line(),
        panel.border = element_rect(colour = "black", fill = NA),
        panel.grid.minor = element_blank(),
        panel.grid.major = element_blank())

Upvotes: 1

Related Questions