Search code examples
rggplot2shinylabelgeom-text

geom_label() puts label in wrong location on a donut plot in R Shiny


I'm trying to build a donut plot in my R Shiny app that labels the number and percentage that corresponds with the portion of the plot. However, when I use geom_label(), it does two undesirable things.

  1. It adds an "a" to the legend.
  2. It puts the labels in random places that don't correspond with where they should go.

The result looks like this:

enter image description here

I'm flexible with where the labels go as long as its logical, but in an ideal world, the end result would look something like this (note: this is a sample plot, not using the exact data in the code below):

enter image description here

And, of course, that "a" from the label would also be removed.

Reproducible example:

library(shiny)
library(dplyr)
library(ggplot2)
library(Cairo)
options(shiny.usecairo=T) #used to get the plot to look crisp in Shiny

my_colors <- c("#00A3AD", "#FF8200", "#753BBD", "#6CC24A")

ui <- fluidPage(
    sidebarLayout(
        sidebarPanel(
            
        ),
        mainPanel(
            fluidRow(
                plotOutput("count_by_person")

            ))
    ))
server <- function(input, output) {
    
    
    output$count_by_person <- renderPlot({
        
        data <- tibble(name = c("Justin", "Corey", "Sibley", "Kate"),
                       n = c(10, 30, 59, 1),
                       prop = c(10, 30, 59, 1))

        ggplot(data, aes(x = 2, y = prop, fill = name)) +
            geom_bar(stat = "identity", color = "white") +
            coord_polar(theta = "y", start = 0)+
            geom_label(aes(label = paste0(n, "\n", prop, "%"))) +
            scale_fill_manual(values = my_colors) +
            theme_void() +
            xlim(.5, 2.5)
    })
    
}
shinyApp(ui, server)

Edit with geom_label_repel()

As suggested below, I tried running this same code with geom_label_repel() from the ggrepel package instead of geom_label(). Though it gets the line, it doesn't change the fact that the labels are in the wrong place.

library(shiny)
library(dplyr)
library(ggplot2)
library(Cairo)
library(ggrepel)
options(shiny.usecairo=T) #used to get the plot to look crisp in Shiny

my_colors <- c("#00A3AD", "#FF8200", "#753BBD", "#6CC24A")

ui <- fluidPage(
    sidebarLayout(
        sidebarPanel(
            
        ),
        mainPanel(
            fluidRow(
                plotOutput("count_by_person")

            ))
    ))
server <- function(input, output) {
    
    
    output$count_by_person <- renderPlot({
        
        data <- tibble(name = c("Justin", "Corey", "Sibley", "Kate"),
                       n = c(10, 30, 59, 1),
                       prop = c(10, 30, 59, 1)) %>%
            dplyr::arrange(prop)

        ggplot(data, aes(x = 2, y = prop, fill = name)) +
            geom_bar(stat = "identity", color = "white") +
            coord_polar(theta = "y", start = 0)+
            geom_label_repel(aes(label = paste0(n, "\n", prop, "%")), force_pull = 100, nudge_x = 1) +
            scale_fill_manual(values = my_colors) +
            theme_void() +
            xlim(.5, 2.5)
    })
    
}
shinyApp(ui, server)

Picture of what it looks like with geom_label_repel()

enter image description here


Solution

  • As is explained in the very nice answer by Andrey Kolyadin I linked in the comments, you need to precompute the cumulative position of the text:

    library(ggrepel)
    data %>% 
      arrange(desc(name)) %>%
      mutate(text_y = cumsum(prop)-prop/2) %>%
    ggplot(aes(x = 2, y = prop, fill = name)) +
       geom_bar(stat = "identity", color = "white") +
       coord_polar(theta = "y", start = 0)+
       geom_label_repel(aes(y = text_y, label = paste0(n, "\n", prop, "%")), force_pull = 100, nudge_x = 1) +
       scale_fill_manual(values = my_colors) +
       theme_void() +
       xlim(.5, 2.5)
    

    enter image description here