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

admin2025-03-19  0

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))

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))

Share Improve this question edited Nov 20, 2024 at 6:49 Homer Jay Simpson asked Nov 20, 2024 at 6:44 Homer Jay SimpsonHomer Jay Simpson 1,2488 silver badges35 bronze badges 5
  • Can you be a little more specific? "automatically adjust the size of the text" -- so the 0.75% slice should have super tiny text that doesn't extend past it? – Jon Spring Commented Nov 20, 2024 at 7:04
  • For instance, you could use geom_text(aes(label = scales::percent(round(per,2)), size = pmin(10, per*100)/.pt), position = position_stack(reverse = TRUE, vjust = .5)) + scale_size_identity() but that looks a little silly to me. – Jon Spring Commented Nov 20, 2024 at 7:08
  • @JonSpring Wrap it as an answer please. – Homer Jay Simpson Commented Nov 20, 2024 at 7:33
  • You should use dodging instead. – Roland Commented Nov 20, 2024 at 7:35
  • @Roland Can you show me how?i am very interested. – Homer Jay Simpson Commented Nov 20, 2024 at 7:44
Add a comment  | 

2 Answers 2

Reset to default 3

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)

Unpopular opinion: if you need to add labels to make your plot understood, you may be using the wrong plot type. Ask yourself if the actual values of the categories are really important for your purpose or if the relative comparison is already enough? In the latter case you may not need any labels at all; in the former case a tabular representation of the figures serves the purpose much better.

Now to your problem at hand. You have several options:

  1. Nudge the labels by hand (@Roland gave one example)
  2. Rotate the labels (geom_text(aes(label = scales::percent(round(per,2))), position = position_stack(reverse = TRUE, vjust = .5), angle = 90))
  3. Use plotly and add tooltips instead of the labels (remove the geom_text and use plotly::ggplotly(<yourplot>) instead and you get tooltips.
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1742376191a210457.html

最新回复(0)