rggplot2bar-chart

Automatically adjust the size of the text inside each bar on a bar plot using ggplot2 in R


i have a data frame in R called df and looks like this:

 df
# A tibble: 15 × 3
# Groups:   var1 [3]
   var1   var2                       per
   <chr>  <fct>                    <dbl>
 1 My own "Very \n Dissatisfied" 0.00751
 2 My own "Dissatisfied"         0.0641 
 3 My own "Neutral"              0.353  
 4 My own "Satisfied"            0.384  
 5 My own "Very \n Satisfied"    0.192  
 6 No     "Very \n Dissatisfied" 0.0445 
 7 No     "Dissatisfied"         0.135  
 8 No     "Neutral"              0.417  
 9 No     "Satisfied"            0.273  
10 No     "Very \n Satisfied"    0.130  
11 Yes    "Very \n Dissatisfied" 0.0233 
12 Yes    "Dissatisfied"         0.0639 
13 Yes    "Neutral"              0.280  
14 Yes    "Satisfied"            0.340  
15 Yes    "Very \n Satisfied"    0.293  

i want to plot it like the picture below but i want inside each plot to automatically adjust the percentages in the middle of each bar. But here are overlapping .

ggplot(df,aes(x=var1,y=per,fill=var2))+
  geom_col(position = position_fill(reverse = TRUE))+
  theme(axis.title.y=element_blank(),axis.title.x=element_blank())+
  coord_flip()+
  scale_fill_brewer(palette ="RdYlGn",direction = 1,type="div")+
  theme(axis.text.y=element_text(size=12, angle=0,hjust=0,vjust=0))+
  theme(axis.text.x=element_text(size=12, angle=0,hjust=0,vjust=0)) +
  theme(legend.text=element_text(size = 15), 
        legend.title = element_blank()  )+
  scale_y_continuous(labels = scales::percent)+
  geom_text(aes(label = scales::percent(round(per,2))), position = position_stack(reverse = TRUE, vjust = .5))

How can i do it in R ?

Data

structure(list(var1 = c("My own", "My own", "My own", "My own", 
"My own", "No", "No", "No", "No", "No", "Yes", "Yes", "Yes", 
"Yes", "Yes"), var2 = structure(c(1L, 2L, 3L, 4L, 5L, 1L, 2L, 
3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L), levels = c("Very \n Dissatisfied", 
"Dissatisfied", "Neutral", "Satisfied", "Very \n Satisfied"), class = "factor"), 
    per = c(0.00751285092922104, 0.0640569395017794, 0.352708580466588, 
    0.383550810597074, 0.192170818505338, 0.0445205479452055, 
    0.134703196347032, 0.417237442922374, 0.273401826484018, 
    0.13013698630137, 0.0233160621761658, 0.0639032815198618, 
    0.27979274611399, 0.340241796200345, 0.292746113989637)), class = c("grouped_df", 
"tbl_df", "tbl", "data.frame"), row.names = c(NA, -15L), groups = structure(list(
    var1 = c("My own", "No", "Yes"), .rows = structure(list(1:5, 
        6:10, 11:15), ptype = integer(0), class = c("vctrs_list_of", 
    "vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -3L), .drop = TRUE))

enter image description here


Solution

  • I think it is better to move overplotting labels to different heights. I don't know a quick way of using position_dodge nicely, so I suggest a more manual approach.

    library(ggplot2)
    library(dplyr)
    
    df <- df |> group_by(var1) |> mutate(cper = cumsum(per) - per/2)
    
    ggplot(df,aes(x=var1,y=per,fill=var2, group = var2))+
      geom_col(position = position_fill(reverse = TRUE))+
      theme(axis.title.y=element_blank(),axis.title.x=element_blank())+
      coord_flip()+
      scale_fill_brewer(palette ="RdYlGn",direction = 1,type="div")+
      theme(axis.text.y=element_text(size=12, angle=0,hjust=0,vjust=0))+
      theme(axis.text.x=element_text(size=12, angle=0,hjust=0,vjust=0)) +
      theme(legend.text=element_text(size = 15), 
            legend.title = element_blank()  )+
      scale_y_continuous(labels = scales::percent)+
      geom_text(data = df[df$var2 != "Very \n Dissatisfied",], 
                 aes(y = cper, label = scales::percent(round(per,2)))) +
      geom_label(data = df[df$var2 == "Very \n Dissatisfied",], 
                 aes(y = cper, label = scales::percent(round(per,2))), 
                 nudge_x = 0.15, show.legend = FALSE)
    

    resulting plot