0

in the plot below I have data distributed into four groups separated via facet_wrap(). However, these groups have an uneven distribution of the data, hence, all groups have different bar sizes.

How can I avoid cut the geom_label() of groups one and two ?

img

Here is my code:

library(tidyverse)
library(RColorBrewer)

###

cor12 <- brewer.pal(12, "Paired")
cor5 <- brewer.pal(7, "Pastel1")

myColors <- c(cor12, cor5)
names(myColors) <- c('Não oferta',
                     '1h - 3h',
                     '4h - 5h',
                     '6h - 8h',
                     '9h - 10h',
                     'Mais de 10h', 'Mais de 15h',
                     '20h', 'Mais de 20h', 'Mais de 30h',
                     '50% em LA', '100% em LA')

### plot:

df %>% 
  ggplot(aes(y = prop, x = fct_rev(CH), fill = CH, pattern = Q9)) + 
  geom_bar(stat = "identity", width = 0.5) +
  scale_fill_manual(values = myColors, name = NULL) +
  geom_label(aes(label = str_glue('{round(prop, 1.5)}%')),
             nudge_y = 15, #15
             nudge_x = 0.3, #0.05
             colour = 'black',
             fill = "yellow",
             label.padding = unit(0.125, "lines"),
             size = 3.5) + 
  
  ### FLIP IT:
  
  coord_flip(clip = "off") +
  facet_wrap(~Q9, scales = "free_y") +
  
  
  ### SHOW N
  
  geom_label(aes(label= str_glue('n = {n}')),
             size = 2,
             nudge_x = 0.3,
             nudge_y = 30,
             fontface = "italic",
             show.legend = FALSE) +
  
  ### SHOW % IN THE X-AXIS:
  
  scale_y_continuous(limits = c(0,100),
                     labels=scales::percent_format(scale = 1)) +
  
  ### LABELS: 
  
  labs(x = "times",
       y = '%',
       title = "a title") +
  
  ### THEME:
  cowplot::theme_half_open() +
  theme( 
    
    text = element_text(family = "sans",
                        size = 14,
                        hjust = 0.5),
    
    #plot.margin = margin(t = 0, r = 15, b = 0, l = 0),
    
    
    legend.position = "bottom",
    legend.justification = "center",
    legend.background = element_rect(color = "black"),  #bloco com legendas
    legend.margin = margin(t = 5, r = 5, b = 3, l = 3),
    
    legend.text = element_text(size = 12), 
    
    axis.text.x = element_text(hjust = 0.5,
                               size = 12), # legenda de baixo
    axis.title.x = element_text(size = 13.5,
                                face = "bold",
                                margin = margin(t = 20, r = 0, b = 0, l = 0)),
    axis.text.y = element_text(size = 12),
    axis.title.y = element_text(size = 13.5,
                                face = "bold"),
    
    ### Facets: 
    
    strip.text.x = element_text(face = "bold",
                                family = "sans",
                                size = 18,
                                colour = 'green'),
    strip.background = element_rect(size = 3,
                                    fill = 'yellow'),
    strip.switch.pad.grid = unit('10', "cm"),
    
    strip.clip = "off")

QUESTIONS:

1: How can I avoid cut the geom_label() of groups one and two ?

Bonus : is there a way to dynamically set geom_label() so that the labels always get stuck to a certain position according to each bar size? (hence, I don't have to set them manually and groups with different amount of data get the labels at the same place) ?

Edits:

I need a solution to achieve this:

d ds

Data:

structure(list(CH = structure(c(3L, 2L, 3L, 2L, 3L, 4L, 5L, 6L, 
7L, 6L, 7L), .Label = c("Não oferta", "1h - 3h", "4h - 5h", "6h - 8h", 
"9h - 10h", "Mais de 10h", "50% em LA"), class = "factor"), Q9 = structure(c(1L, 
2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L), .Label = c("one", "two", 
"three", "four"), class = "factor"), n = c(1L, 12L, 1L, 8L, 2L, 
2L, 2L, 1L, 3L, 2L, 2L), prop = c(100, 92.31, 7.69, 44.44, 11.11, 
11.11, 11.11, 5.56, 16.67, 50, 50), sd = c(NA, 0.6, 0.6, 0.14, 
0.14, 0.14, 0.14, 0.14, 0.14, 0, 0)), class = c("grouped_df", 
"tbl_df", "tbl", "data.frame"), row.names = c(NA, -11L), groups = structure(list(
    Q9 = structure(1:4, .Label = c("one", "two", "three", "four"
    ), class = "factor"), .rows = structure(list(1L, 2:3, 4:9, 
        10:11), ptype = integer(0), class = c("vctrs_list_of", 
    "vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -4L), .drop = TRUE))
M--
  • 25,431
  • 8
  • 61
  • 93
Larissa Cury
  • 806
  • 2
  • 11
  • my issue is similar to this: https://stackoverflow.com/questions/17241182/how-to-make-geom-text-plot-within-the-canvass-bounds , but the solution isn't solving my problem... – Larissa Cury May 21 '23 at 18:28

1 Answers1

1

Update after clarification: It is not the best solution but it should work for this example. To avoid losing one label because of moving the other to the left, we could stack the labels by changing both (% and n) nudge_x and nudge_y and then we can see all of them:

df %>% 
  ggplot(aes(y = prop, x = fct_rev(CH), fill = CH, pattern = Q9)) + 
  geom_bar(stat = "identity", width = 0.5) +
  scale_fill_manual(values = myColors, name = NULL) +
  geom_label(aes(label = str_glue('{round(prop, 1.5)}%')),
             nudge_y = -2, #15
             nudge_x = 0.05, #0.05
             colour = 'black',
             fill = "yellow",
             label.padding = unit(0.125, "lines"),
             size = 3.5) + 
  
  ### FLIP IT:
  
  coord_flip(clip = "off") +
  facet_wrap(~Q9, scales = "free_y") +
  
  
  ### SHOW N
  
  geom_label(aes(label= str_glue('n = {n}')),
             size = 2,
             nudge_x = -0.2,
             nudge_y = -2,
             fontface = "italic",
             show.legend = FALSE) +
  
  ### SHOW % IN THE X-AXIS:
  
  scale_y_continuous(limits = c(0,100),
                     labels=scales::percent_format(scale = 1)) +
  
  ### LABELS: 
  
  labs(x = "times",
       y = '%',
       title = "a title") +
  
  ### THEME:
  cowplot::theme_half_open() +
  theme( 
    
    text = element_text(family = "sans",
                        size = 14,
                        hjust = 0.5),
    
    #plot.margin = margin(t = 0, r = 15, b = 0, l = 0),
    
    
    legend.position = "bottom",
    legend.justification = "center",
    legend.background = element_rect(color = "black"),  #bloco com legendas
    legend.margin = margin(t = 5, r = 5, b = 3, l = 3),
    
    legend.text = element_text(size = 12), 
    
    axis.text.x = element_text(hjust = 0.5,
                               size = 12), # legenda de baixo
    axis.title.x = element_text(size = 13.5,
                                face = "bold",
                                margin = margin(t = 20, r = 0, b = 0, l = 0)),
    axis.text.y = element_text(size = 12),
    axis.title.y = element_text(size = 13.5,
                                face = "bold"),
    
    ### Facets: 
    
    strip.text.x = element_text(face = "bold",
                                family = "sans",
                                size = 18,
                                colour = 'green'),
    strip.background = element_rect(size = 3,
                                    fill = 'yellow'),
    strip.switch.pad.grid = unit('10', "cm"),
    
    strip.clip = "off")

enter image description here First answer: This should do what you are after:

To avoid expanding y axis over 100%, we could position the labels in the chart area.

Here we use prop in a condition to set y position of the labels:

df %>% 
  ggplot(aes(y = prop, x = fct_rev(CH), fill = CH, pattern = Q9)) + 
  geom_bar(stat = "identity", width = 0.5) +
  scale_fill_manual(values = myColors, name = NULL) +
  geom_label(aes(label = str_glue('{round(prop, 1.5)}%'), y = ifelse(prop < 10, prop + 5, prop - 5)),
             colour = 'black',
             fill = "yellow",
             label.padding = unit(0.125, "lines"),
             size = 3.5) + 
  coord_flip(clip = "off") +
  facet_wrap(~Q9, scales = "free_y") +
  scale_y_continuous(limits = c(0,100), labels=scales::percent_format(scale = 1)) +
  theme(
    text = element_text(family = "sans", size = 14, hjust = 0.5),
    legend.position = "bottom",
    legend.justification = "center",
    # Other theme elements...
  )+
  ### LABELS: 
  
  labs(x = "times",
       y = '%',
       title = "a title") +
  
  ### THEME:
  cowplot::theme_half_open() +
  theme( 
    
    text = element_text(family = "sans",
                        size = 14,
                        hjust = 0.5),
    
    #plot.margin = margin(t = 0, r = 15, b = 0, l = 0),
    
    
    legend.position = "bottom",
    legend.justification = "center",
    legend.background = element_rect(color = "black"),  #bloco com legendas
    legend.margin = margin(t = 5, r = 5, b = 3, l = 3),
    
    legend.text = element_text(size = 12), 
    
    axis.text.x = element_text(hjust = 0.5,
                               size = 12), # legenda de baixo
    axis.title.x = element_text(size = 13.5,
                                face = "bold",
                                margin = margin(t = 20, r = 0, b = 0, l = 0)),
    axis.text.y = element_text(size = 12),
    axis.title.y = element_text(size = 13.5,
                                face = "bold"),
    
    ### Facets: 
    
    strip.text.x = element_text(face = "bold",
                                family = "sans",
                                size = 18,
                                colour = 'green'),
    strip.background = element_rect(size = 3,
                                    fill = 'yellow'),
    strip.switch.pad.grid = unit('10', "cm"),
    
    strip.clip = "off")

enter image description here

TarJae
  • 72,363
  • 6
  • 19
  • 66
  • 1
    Hi, @TarJae, thank you, I thought of this, but my y-axis displays proportions, hence it becomes a bit weird to go past 100% – Larissa Cury May 21 '23 at 18:31
  • Please have a look at my update and tell me is this what you are looking for? – TarJae May 21 '23 at 18:37
  • almost there, @TarJae ! the problem is that now it's overlapping with *n* , I'm trying to avoid that, but I'm failing to do so. – Larissa Cury May 21 '23 at 18:54
  • 1
    a solution could be to display *n* inside the bars and the % on the right of each bar (I added a picture to the post to illustrated it!) – Larissa Cury May 21 '23 at 18:57