rggplot2geom-bargeom-segment

Add leading lines / geom segment to pie graph with ggplot2


I have a pie graph that I've generated with ggplot2. However, some of the wedges are quite thin, so I wanted to place the annotations outside the pie graph and add leading lines. I've succeeded in placing the annotations outside of the graph, but I cannot seem to add a straight line from the graph to the annotation. I suspect the problem comes from the polar coordinate system, but I'm not sure how to overcome this. Below is a reproducible example:

set.seed(123)

df <- data.frame(letters = character(100))

df$letters <- sample(c("Reported", "Not Reported"), size = 100, replace = TRUE, prob = c(0.94, 0.06))


create_counts_dataframe <- function(df, column_name) {
  # Create summary table with counts
  counts <- table(df[, column_name])
  dat_counts <- data.frame(counts)
  
  # Calculate percentages
  dat_counts$percent <- round(100 * dat_counts$Freq / sum(dat_counts$Freq), 2)
  
  # Order factor levels based on percentages
  dat_counts$Var1 <- factor(dat_counts$Var1, levels = dat_counts$Var1[order(dat_counts$percent, decreasing = TRUE)])
  
  return(dat_counts)
}

create_pie_chart <- function(data, legend_label, label_position, label_vjust, colour_mapping) {
  # Create pie chart with ggplot2
  pie_chart <- ggplot(data, aes(x = "", y = percent, fill = Var1)) +
    geom_bar(width = 1, stat = "identity") +
    coord_polar(theta = "y") +
    labs(fill = legend_label) +
    scale_fill_manual(values = colour_mapping) +
    theme_void() +
    geom_text(aes(label = paste0(percent, "%")), position = label_position, vjust = label_vjust) +
    theme(legend.title = element_text(face = "bold"))
  
  return(pie_chart)
}

colour_mapping2 <- c("Reported" = "#F4A582",
                   "Not Reported" = "#B2182B")

gg_letters <- create_counts_dataframe(df, "letters")

gg_letters_p <- create_pie_chart(gg_letters, "letters", position_stack(vjust = 0.7), label_vjust = -0.5, colour_mapping = colour_mapping2)
gg_letters_p

gg_letters_p$layers <- gg_letters_p$layers[-2]

gg_letters_p <- gg_letters_p +
  geom_text(data = subset(gg_letters, Var1 == "Reported"), aes(label = paste0(percent, "%"), x = 1, y = 0, vjust = 14)) +
  geom_text(data = subset(gg_letters, Var1 == "Not Reported"), aes(label = paste0(percent, "%"), x = 1.1, y = 15, vjust = -10))


radius <- 1.0
angle <- pi / 4 

x <- radius * cos(angle)
y <- radius * sin(angle)

df_coord <- data.frame(x = 0, y = 0, xend = x, yend = y)

gg_letters <- merge(gg_letters, df_coord, by = NULL)

plot <- gg_letters_p +
  geom_segment(data = gg_letters, aes(x = x, y = y + 5, xend = xend, yend = yend))

plot

Basically I started with geom_segment, but could not figre out how to adjust the parameters to get the line I want. I also tried converting from polar to cartesian coordinates, but this did not work. Is there something else I am missing?

I've included the functions I'm using as I have several variables that I aim to make pie graphs of. I also know that including the labels in the function only to remove them is redundant, but it will only be necessary in cases where the wedges are too thin, otherwise the labels are fine.

I have read some related threads: ggplot2 pie chart : Repositioning ggrepel slice labels by moving them toward circumference of the pie possible?

centering the label segment starting points for labelled pie charts

Add Leading Lines to ggplot2 Pie Chart

ggplot pie chart labeling

Which maybe point towards geom_label_repel as an option, but I don't know how to apply it here, as I only want to put one of the annotations outside, which I have already succeeded in, and now just want to add the line.


Solution

  • Since this isn't built-in, it might be easier to to some of the calculations ourselves.

    Here, I calculate the cumulative percent, so we can skip position_stack and define the y in terms of that cumulative percent, and x based on aesthetic preference.

    create_pie_chart <- function(data, legend_label, nudge_label, label_spacing, colour_mapping) {
      data |>
        transmute(Var1, percent, cuml_pct = cumsum(percent)) |>
      ggplot(aes(x = 0, y = percent, fill = Var1)) +
        geom_col(width = 1) +
        coord_polar(theta = "y") +
        labs(fill = legend_label) +
        scale_fill_manual(values = colour_mapping) +
        geom_segment(aes(y = cuml_pct - percent/2, 
                         yend = cuml_pct - percent/2,
                         x = 0.2,
                         xend = nudge_label * (1-label_spacing))) +
        geom_text(aes(y = cuml_pct - percent/2,
                      label = paste0(percent, "%")), 
                  nudge_x = nudge_label) +
    
        theme_void() +
        theme(legend.title = element_text(face = "bold"))
    }
    
    create_pie_chart(
      data.frame(Var1 = c("Reported","Not Reported"), percent = c(10,90)) , 
      "letters", nudge_label = 0.7, label_spacing = 0.2,
      colour_mapping = colour_mapping2)
    

    enter image description here