# How to plot stacked bar chart using R showing mean with range and labelled values?

I am trying to plot a stacked bar graph with mean values from multiple repeats, also showing the range of the data and values labelled.

My data frame is below:

structure(list(Average.DB.per.FA = c(0, 0.5, 1, 1.5, 2, 0, 0.5,
1, 1.5, 2, 0, 0.5, 1, 1.5, 2, 0, 0.5, 1, 1.5, 2, 0, 0.5, 1, 1.5,
2, 0, 0.5, 1, 1.5, 2), condition = c("CDD", "CDD", "CDD", "CDD",
"CDD", "CDD", "CDD", "CDD", "CDD", "CDD", "CDD", "CDD", "CDD",
"CDD", "CDD", "STD", "STD", "STD", "STD", "STD", "STD", "STD",
"STD", "STD", "STD", "STD", "STD", "STD", "STD", "STD"), percentage = c(0,
0.807218011, 0.192781989, 0, 0, 0, 0.737968015, 0.262031985,
0, 0, 0, 0.739096101, 0.260903899, 0, 0, 0, 0.466751289, 0.346156739,
0.170617491, 0.016474482, 0, 0.462997344, 0.331833715, 0.183359127,
0.021809813, 0.012053568, 0.47623097, 0.307175237, 0.182459607,
0.022080618)), row.names = c(NA, -30L), class = "data.frame")


I tried to use the following code:

    ggplot(long_DF, aes(fill=Average.DB.per.FA, y=percentage, x=condition)) +
geom_bar(position=position_dodge(), stat="identity")


But the graph I obtained does not have bars add up to 1:

I would like to have graphs like below (which I created by Prism using the same set of data but Prism does not allow me to label bars with numbers):

Is there any way to reproduce the graph by Prism using R and add values to each stacked bar?

• Hi its a great question. Could you kindly explain the biological aspect of the code, i.e. why is this bioformatics rather than pure statistics?
– M__
Oct 9, 2022 at 9:58

It seems like this is missing a grouping variable in the aesthetics definition. The R help page for position_dodge mentions the following:

Position_dodge() requires the grouping variable to be be specified in the global or ⁠geom_*⁠ layer. Unlike position_dodge(), position_dodge2() works without a grouping variable in a layer. position_dodge2() works with bars and rectangles, but is particulary useful for arranging box plots, which can have variable widths.

Bearing this in mind, setting group=Average.DB.per.FA seems to give the result you want:

long_DF %>%
ggplot() +
aes(fill=Average.DB.per.FA,
y=percentage, x=condition, group=Average.DB.per.FA) +
geom_col()


... although this is adding up to 3 rather than 1, as would be expected from looking at the category counts:

> long_DF %>%
+   group_by(condition) %>%
+   summarise(sumPct = sum(percentage))
# A tibble: 2 × 2
condition sumPct
<chr>      <dbl>
1 CDD            3
2 STD            3


Note: as seen above, I'm using geom_colinstead of geom_bar with a different statistical summary method. If you're using stat=%something%, there's probably a better way to approach the task. The following is from the first paragraph of the R help page for geom_bar/geom_col:

There are two types of bar charts: geom_bar() and geom_col(). geom_bar() makes the height of the bar proportional to the number of cases in each group (or if the weight aesthetic is supplied, the sum of the weights). If you want the heights of the bars to represent values in the data, use geom_col() instead. geom_bar() uses stat_count() by default: it counts the number of cases at each x position. geom_col() uses stat_identity(): it leaves the data as is.