Search code examples
rggplot2ggrepel

centering the label segment starting points for labelled pie charts


I am trying to create a labelled pie chart using ggrepel based on a StackOverflow answer, but I can't seem to get the position of the labels correctly.

Ideally, I would like the segments that connect the labels to the portion of the pies to start in the middle of the pie for it to be more aesthetically pleasing. But this is not what I get, and I am not sure what I should be changing. I have tried setting position = ggplot2::position_fill(vjust = 0.5) in ggrepel call, but that doesn't help either.

data

# setup
set.seed(123)
library(ggplot2)
library(ggrepel)
library(forcats)

# data
(df <- structure(
  list(
    cyl = structure(
      c(1L, 3L, 2L),
      .Label = c("8", "6", "4"),
      class = "factor"
    ),
    counts = c(14L, 11L, 7L),
    perc = c(43.75, 34.375, 21.875),
    label = c("44%", "34%", "22%")
  ),
  row.names = c(NA, -3L),
  class = c("tbl_df", "tbl", "data.frame")
))
#> # A tibble: 3 x 4
#>   cyl   counts  perc label
#>   <fct>  <int> <dbl> <chr>
#> 1 8         14  43.8 44%  
#> 2 4         11  34.4 34%  
#> 3 6          7  21.9 22%

function

# function
foo <- function(df, x) {
  ggplot2::ggplot(
    data = df,
    mapping = ggplot2::aes(x = "", y = counts, fill = forcats::fct_inorder({{ x }}))
  ) +
    ggplot2::geom_bar(
      stat = "identity",
      color = "black",
      width = 1,
      na.rm = TRUE
    ) +
    coord_polar("y", start = 0) +
    ggrepel::geom_label_repel(
      aes(label = label),
      show.legend = FALSE,
      nudge_x = 1
    ) +
    guides(fill = guide_legend(title = rlang::as_name(rlang::ensym(x))))
}

plot

# plot
foo(df, cyl)

plot


Solution

  • You can calculate the labels' stacked positions manually, instead of relying on ggplot. See if the following works for you:

    foo <- function(df, x) {
      df$y.label <- 0.5 * df$counts + rev(cumsum(dplyr::lag(rev(df$counts), default = 0)))
      ggplot2::ggplot(
        data = df,
        mapping = ggplot2::aes(x = "", y = counts, fill = forcats::fct_inorder({{ x }}))
      ) +
        ggplot2::geom_col(
          color = "black",
          width = 1,
          na.rm = TRUE
        ) +
        coord_polar("y", start = 0) +
        ggrepel::geom_label_repel(
          aes(label = label, y = y.label),
          show.legend = FALSE,
          nudge_x = 1
        ) +
        guides(fill = guide_legend(title = rlang::as_name(rlang::ensym(x))))
    }
    
    foo(df, cyl)
    

    plot