I am trying to create a stacked bar chart with percentages in R. The problem that I have is that some of the labels are overlapping in the bar (see the two 3%-labels in the bar at the bottom position):

This is my reproducible code:
Position <- rep(c("LeiterIn", "AssistentIn", "ElementarpädagogIn"), each = 30)
Subjektiver_Gesundheitszustand <- c(1,3,4,5,2,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,
3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,3,4,5,
3,4,5,3,4,5)
dat <- data.frame(Position, Subjektiver_Gesundheitszustand)
dat %>%
dplyr::count(Position, Subjektiver_Gesundheitszustand) %>%
group_by(Position) %>%
mutate(Pct = n / sum(n)) %>%
ggplot(aes(fill = factor(Subjektiver_Gesundheitszustand), x = Pct, y = fct_rev(Position))) +
geom_bar(position = position_fill(reverse = TRUE), stat = "identity") +
geom_text(aes(label = paste0(sprintf("%1.0f", Pct * 100), "%")),
position = position_stack(vjust = 0.5, reverse =TRUE), size = 5)+
scale_x_continuous(labels = scales::percent) +
facet_wrap(vars(Position), ncol = 1, scales = "free_y") +
scale_fill_manual(labels = c("sehr gut", "gut", "mittelmäßig", "schlecht", "sehr schlecht"),
values = c("#A3E6B4","#86BD94", "#658F70","#4C6B54", "#2F4234")) +
labs(title = "", y = "Position", x = "Percentage") +
theme(legend.position = "bottom",
legend.title = element_blank(),
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.title.x = element_blank(),
axis.ticks.y = element_blank(),
strip.background = element_rect(fill = NA),# remove facet strip background
strip.text = element_text(hjust = 0, size = 30))type here
I tried geom_text_repel() but I don't like that it breaks with the horizontal aligning of the labels. In my opinion, it just looks chaotic:
I'd like to move only the small labels (i.e. the 3% labels) outside of the bar (ideally with a line/arrow connecting the label with the corresponding section in the bar) and keep all other labels aligned. Any idea how this is possible? Thanks in advance!

I made an attempt using
facet_wrap()but struggled to find an adequate solution so here's a workflow I use in these situations. It involves:row_number()to reduce likelihood of adjacent low value labels overlappingcoord_flip(clip = "off")to switch axesgeom_rect()to improve label readability (omit if not wanted)Note that because
coord_flip(clip = "off"), setting the locations for the labels will seem 'backwards' e.g. x values become y and vice versa. To keep the workflow intuitive, I have named the label location variables so they mirror where they are declared inaes(). For example, xlabloc and dummyX will end up on the y-axis.It can take a lot of fine-tuning in regards to getting the label placements how you want them, but an advantage of this method is that it is fully customisable. I have arbitrarily aligned the labels < 5% to the centre of their associated bar segment. If this doesn't suit, there's nothing stopping you using the same workflow principles to shift them to somewhere else more preferable.
Plot aesthetics are dependent on output plot dimensions. As such, some values such as ymin and ymax in
geom_rect()will need adjusting if you change yourggsave()dimensions. The example images below usewidth = 6andheight = 5. It is possible to create and use a plot scale variable e.g.scalevar <- 6 / 5and use it to scaleggplot()aesthetics and this can reduce the amount of fine-tuning required.OPTION 1: values < 5% outside bar:
OPTION 2: values < 5% inside bar: note that this uses the same
ggplot()code as above, butgeom_bar(width = 0.6):