Search code examples
rggplot2visualizationpie-chartdonut-chart

Add lines connecting segments to values outside a donut chart


Adding lines to the donut chart in ggplot2

I've been trying to create a chart that displays values outside its perimeter for a long time. I would like to connect these values to the corresponding segments on the chart using dashed lines that should start at the centers of the segments. my plot I was able to create a chart that met the requirements except for the (seemingly) trivial lines using this code:

library(ggplot2)
library(dplyr)

COUNTRY <- c("Sweden", "France", "USA", "Britain", "Argentina", "Brazil")
Revenue <- c(190, 146, 131, 129, 121, 937)
Percent <- c(0.11487304, 0.08827086, 0.07920193, 0.07799274, 0.07315599, 0.56650544)
color <- c('A', 'B', 'C', 'D', 'E', 'F')

TOP_5 <- data.frame(COUNTRY, Revenue, Percent, color)

colors <- c('A'='#D9553B', 'B'='#E17A65', 'C'='#EBA799', 'D'='#F2C8C0', 'E'='#F9E3DF', 'F'='#A7A8A9')

PLOT <- ggplot(data = TOP_5, aes(x = 2, y = Percent, fill = color)) +
  geom_col(color = "white") +
  coord_polar("y", start = 1) +
  geom_text(aes(x = 3, label = paste0(" ", round(Percent * 100), "%")), 
            position = position_stack(vjust = 0.5), 
            hjust = 0,
            angle = 0,
            color = "black") +
  geom_text(aes(x = 3, label = COUNTRY), 
            position = position_stack(vjust = 0.5), 
            hjust = 1, 
            angle = 0,
            color = "#B43C23") +
  geom_point(position = position_stack(vjust = 0.5), 
             color = "#B43C23", 
             size = 3) +
  theme(panel.background = element_blank(),
        axis.line = element_blank(),
        axis.text = element_blank(),
        axis.ticks = element_blank(),
        axis.title = element_blank(),
        plot.title = element_text(hjust = 0.5, size = 18)) +
  scale_fill_manual(values = colors) +
  theme(legend.position = "none") +
  xlim(0.5, 3.2)  

The expected result would be something like this: desired plot I tried to achieve this using geom_segment but without success. I would appreciate your help in convicting a possible solution. Ideally, I would like to limit myself to just these two libraries, but of course I am open to additional solutions


Solution

  • One option to achieve your desired result would be to use a geom_segment to connect the labels and the points. To this end I computed the y positions manually. Additionally I switched to geom_label as it allows for "background" box around the labels:

    library(ggplot2)
    library(dplyr)
    
    TOP_5 <- TOP_5 %>%
      arrange(desc(color)) %>%
      mutate(
        Percent_cum = cumsum(Percent),
        y = .5 * (Percent_cum + lag(Percent_cum, default = 0))
      )
    
    ggplot(data = TOP_5, aes(x = 2, y = Percent, fill = color)) +
      geom_col(color = "white") +
      coord_polar("y", start = 1, clip = "off") +
      geom_segment(aes(x = 2.1, xend = 3, y = y, yend = y, group = color)) +
      geom_label(aes(x = 3, label = paste0(" ", round(Percent * 100), "%")),
        position = position_stack(vjust = 0.5),
        fill = "white",
        label.size = 0,
        hjust = 0,
        angle = 0,
        color = "black"
      ) +
      geom_label(aes(x = 3, label = COUNTRY),
        position = position_stack(vjust = 0.5),
        fill = "white",
        label.size = 0,
        hjust = 1,
        angle = 0,
        color = "#B43C23"
      ) +
      geom_point(
        position = position_stack(vjust = 0.5),
        color = "#B43C23",
        size = 3
      ) +
      theme(
        panel.background = element_blank(),
        axis.line = element_blank(),
        axis.text = element_blank(),
        axis.ticks = element_blank(),
        axis.title = element_blank(),
        plot.title = element_text(hjust = 0.5, size = 18)
      ) +
      scale_fill_manual(values = colors) +
      theme(legend.position = "none") +
      xlim(0.5, 3.2)
    

    enter image description here