how to replicate this graph in ggplot2?

Has anyone done something similar to this using ggplot2?

Here is one way to start the process.

library(ggplot2)
DF <- data.frame(Party = rep(c("Dem", "Rep"), each = 100),
                 Value = c(rnorm(100, 45, 10), rnorm(100, 63, 12)),
                 YMin = rep(c(0,1), each = 100),
                 YMax = rep(c(0.5,1.5), each = 100))
ggplot(DF, aes(x = Value, color = Party, ymin = YMin, ymax = YMax)) + geom_linerange()

Created on 2020-07-08 by the reprex package (v0.2.1)

2 Likes

going further:
Some thing might need some post-processing, not sure if you can move the x axis labels between the two.

ggplot(DF, aes(x = Value, color = Party, ymin = YMin, ymax = YMax)) + 
  geom_linerange(alpha = 0.5) + 
  scale_x_continuous(labels = function(x) paste0(x, "%"),
                     breaks = seq(0,100,20),
                     limits = c(0,100))   +
  scale_colour_manual(values = c("red3", "royalblue3")) +
  theme_minimal(base_size = 14) +
  theme(legend.position = "none",
        axis.text.x = element_text(),
        aspect.ratio = 0.2,
        plot.title = element_text(hjust = 0.5),
        # somehow theme_void isn't working, so switch off everything we don't need
        panel.grid = element_blank(),
        axis.text.y = element_blank(), 
        axis.title.x = element_blank()
        ) +
  labs(title = "Republican landslide counties are \ngenerally whiter than Democratic ones")

2 Likes

You can do that manually via geom_text() for example:

library(tidyverse)

DF <- data.frame(Party = rep(c("Dem", "Rep"), each = 100),
                 Value = c(rnorm(100, 45, 10), rnorm(100, 63, 12)),
                 YMin = rep(c(0,1), each = 100),
                 YMax = rep(c(0.5,1.5), each = 100))

ggplot(DF, aes(x = Value, color = Party, ymin = YMin, ymax = YMax)) + 
  geom_linerange(alpha = 0.5) + 
  geom_text(data = tibble(x = seq(0, 100, by = 20), y = .75, 
                          label = glue::glue("{seq(0, 100, by = 20)}%")),
            aes(x = x, y = y, label = label),
            inherit.aes = F,
            color = "grey40",
            size = 3.5) +
  scale_colour_manual(values = c("red3", "royalblue3")) +
  theme_minimal(base_size = 14) +
  theme(legend.position = "none",
        aspect.ratio = 0.2,
        plot.title = element_text(hjust = 0.5),
        # somehow theme_void isn't working, so switch off everything we don't need
        panel.grid = element_blank(),
        axis.text = element_blank(), 
        axis.title = element_blank()
  ) +
  labs(title = "Republican landslide counties are \ngenerally whiter than Democratic ones")

Created on 2020-07-09 by the reprex package (v0.3.0)

And going one step further, adding the averages:

library(tidyverse)

DF <- data.frame(Party = rep(c("Dem", "Rep"), each = 100),
                 Value = c(rnorm(100, 45, 10), rnorm(100, 63, 12)),
                 YMin = rep(c(0,1), each = 100),
                 YMax = rep(c(0.5,1.5), each = 100))

DF_avg <- DF %>% 
  mutate(avg = mean(Value)) %>% 
  group_by(Party, avg, YMin, YMax) %>% 
  summarize(avg_party = mean(Value))
#> `summarise()` regrouping output by 'Party', 'avg', 'YMin' (override with `.groups` argument)

ggplot(DF, aes(x = Value, color = Party, ymin = YMin, ymax = YMax)) + 
  geom_linerange(alpha = 0.5) + 
  geom_linerange(data = DF_avg,
                 aes(x = avg_party, color = Party, ymin = YMin - .1, ymax = YMax + .1),
                 size = 2) + 
  geom_vline(data = DF_avg,
             aes(xintercept = DF_avg$avg), size = 1) +
  geom_text(data = tibble(x = seq(0, 100, by = 20), y = .75, label = glue::glue("{seq(0, 100, by = 20)}%")),
            aes(x = x, y = y, label = label),
            inherit.aes = F,
            color = "grey40",
            size = 3.5) +
  scale_colour_manual(values = c("red3", "royalblue3")) +
  theme_minimal(base_size = 14) +
  theme(legend.position = "none",
        aspect.ratio = 0.2,
        plot.title = element_text(hjust = 0.5),
        # somehow theme_void isn't working, so switch off everything we don't need
        panel.grid = element_blank(),
        axis.text = element_blank(), 
        axis.title = element_blank()
  ) +
  labs(title = "Republican landslide counties are \ngenerally whiter than Democratic ones")

Created on 2020-07-09 by the reprex package (v0.3.0)

6 Likes

A note on the original visualization: I would prefer to add the unit to the axis, either to the 0% or 100% label. It takes a while otherwise to figure out what the percentages represent IMO.

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.