'How do i show values for a horizontal stacked barplot inside the bar?
I have a stacked bar plot i created with this code
p<- ggplot(data = df21[1:20,], aes(x =Initiative, y = Frequency, fill= Topic)) +
geom_col(aes(fill = Topic), width = 0.5)+
geom_text(aes(label = Frequency, #paste0(round(100*value/sum(value),2),"%")
),
vjust = -1.5,
hjust = 6,
color = "black",
position ="stack", #position_dodge(preserve = "single"),
size = 5)+
theme_classic()+
coord_flip()+
scale_y_continuous(breaks=seq(0, 100, by=10))+
geom_bar(position="stack", stat="identity", width = .5)+
scale_x_discrete(labels = function(x) str_wrap(x, width = 35))+
theme(legend.position="bottom",
axis.text.x = element_text( face = "plain", size = 15, vjust= 0.5),
axis.text.y = element_text( face = "plain", size = 15, vjust= 0.5),
plot.title = element_text(family = "Arial", face = "bold", size = (18),colour = "steelblue4"),
legend.title = element_text( face = "bold", size = 15, vjust= 1, hjust =-3),
legend.text = element_text(face = "bold",
colour = "steelblue4", family = "Arial", size = 8),
legend.key.size = unit(0.8, 'cm'), #change legend key size
legend.key.height = unit(0.8, 'cm'), #change legend key height
legend.key.width = unit(0.8, 'cm'),
axis.title = element_text(face = "bold",family = "Arial", size = 15, colour = "steelblue4") )+
labs(title = "Topics per Initiative", y = "Counts per Topic", x = "Initiative")+
guides(fill=guide_legend(nrow=6, byrow=TRUE,
shape = guide_legend(override.aes = list(size = 6)),
))
it creates this plot with the number of counts for each stack on top.

i have been playing with hjust and vjust and manage to get the numbers on top, otherwise they appear behind the plot like this.
I've been looking for solutions and playing around with the code! i need help! i really would like to have the numbers on top and in the center of each " stacked" bar. thanks!
Solution 1:[1]
Instead of using vjust or hjust when drawing text I recommend you calculate the exact position of each label and drawing them using that as the y aesthetic. This will make the graph look more elegant and doesn't need manual adjustment when values change.
library(dplyr)
library(ggplot2)
# create a sample data with some random
groups <- c("1", "2", "3", "4", "5")
colors <- c("a", "b", "c", "e", "f")
set.seed(100) # for reproducible examples
df <- tibble(
Initiative = sample(groups, 20, replace = TRUE),
Topic = sample(colors, 20, replace = TRUE),
Frequency = round(runif(20, min = 1, max = 20), digits = 0)
)
# a sample df which will need to summarized by Initiative, Topic
df
#> # A tibble: 20 x 3
#> Initiative Topic Frequency
#> <chr> <chr> <dbl>
#> 1 2 f 19
#> 2 3 c 14
#> 3 1 a 13
#> 4 2 c 17
#> 5 4 e 16
#> 6 4 b 17
#> 7 2 f 3
#> 8 3 f 10
#> 9 2 f 12
#> 10 5 a 18
#> 11 4 f 20
#> 12 3 e 2
#> 13 3 c 12
#> 14 2 a 15
#> 15 1 a 6
#> 16 2 a 7
#> 17 3 c 15
#> 18 4 a 18
#> 19 4 a 5
#> 20 4 e 8
# Change the fill color to Factor for easiser control orders of the variables
# this is useful for arrange the text position as well as order of fill
# variables when drawing plot.
df$Topic <- factor(df$Topic, levels = colors)
# summarized the df
summary_df <- df %>%
group_by(Initiative, Topic) %>%
summarize(Frequency = sum(Frequency), .groups = "drop") %>%
group_by(Initiative) %>%
# arrange the variables is needed as it related to how variables will
# be displayed on graphs which decide where position of label would be.
arrange(Initiative, Topic) %>%
# here I calculate the label position using cumsum
mutate(label_pos = cumsum(Frequency) - (Frequency / 2))
summary_df
#> # A tibble: 12 x 4
#> # Groups: Initiative [5]
#> Initiative Topic Frequency label_pos
#> <chr> <fct> <dbl> <dbl>
#> 1 1 a 19 9.5
#> 2 2 a 22 11
#> 3 2 c 17 30.5
#> 4 2 f 34 56
#> 5 3 c 41 20.5
#> 6 3 e 2 42
#> 7 3 f 10 48
#> 8 4 a 23 11.5
#> 9 4 b 17 31.5
#> 10 4 e 24 52
#> 11 4 f 20 74
#> 12 5 a 18 9
ggplot(data = summary_df, aes(x = Initiative)) +
# I only use geom_bar here - in OP you use both geom_bar & geom_col
# that just created duplicated layer and doesn't need to be there.
geom_bar(aes(y = Frequency, fill = Topic),
# I use position_stack with reverse = TRUE here to control
# the order of the plot matched with my label_pos calculated
position = position_stack(reverse = T),
stat="identity", width = .5)+
# drawing the text label using label_pos which will be
# consistent and auto adjust to midpoint when your values changes
geom_text(aes(y = label_pos, label = Frequency),
color = "black", size = 5) +
coord_flip() + theme_classic()

Created on 2022-05-23 by the reprex package (v2.0.1)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Sinh Nguyen |

