0

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

J.Sabree
  • 2,280
  • 19
  • 48
  • 1
    This question doesn't appear to have anything to do with shiny, right? The graph doesn't plot as you expect from the console. This answer should help: [ggplot2 pie plot with geom_text_repel](https://stackoverflow.com/questions/46277894/ggplot2-pie-plot-with-geom-text-repel) – Ian Campbell Jun 16 '21 at 20:56
  • @IanCampbell, thanks for the suggestion! It adds the line that I wanted. Unfortunately, the data is still pointing to the wrong areas. I tried arrange the data as well, but that still didn't change anything. I'm not sure if this Shiny has any effect on this, but this is the context that I noticed it happening in, which is why I included it. I know the adding things in a render command can affect output. Do you have any other suggestions? – J.Sabree Jun 16 '21 at 22:14

1 Answers1

1

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

Ian Campbell
  • 23,484
  • 14
  • 36
  • 57
  • thank you! One edit in your answer: it has to be arrange(desc(name) and then it works. Without desc(), it jumbles up the locations, but the answer that you shared showed to do desc(). If you can edit your answer (SO won't let me), then I'll accept yours as correct. – J.Sabree Jun 18 '21 at 12:27
  • You should be able to suggest an edit by clicking [edit](https://stackoverflow.com/posts/68020445/edit) at the end. Be sure the *very* clearly explain why the edit is necessary, otherwise this sort of edit might be rejected by reviewers. – Ian Campbell Jun 18 '21 at 20:22