3
votes

I am trying to create a barplot where for each category, two bars are plotted (side by side): one is for the "total", the other is stacked by subgroups. For example, in the following data frame, 'names' will be shown on x-axis. For each category in 'names', one bar will represent the value of 'total', the other will be a stacked bar representing values from 'aaa', 'bbb' and 'ccc'. I managed to get a 'back-to-back' plot but I don't know how to apply the 'dodge' position to this case to make the bars side-by-side.

df = data.frame(names = rep(LETTERS[1:3], each=4), 
                num = c(rep(c("aaa","bbb","ccc","total"), 3)), 
                values = c(1,2,3,7,2,2,5,10,3,4,2,9)))
p = ggplot(df, aes(x=factor(names))) + 
    geom_bar(data=subset(df,num=="total"), aes(y=values), stat="identity",width=.5) +
    geom_bar(data=subset(df,num!="total"), aes(y=-values,fill=factor(num)), stat="identity",width=.5) 
print(p)
3
Are you trying to make a figure where the subgroups and total bars are adjacent or, as you describe in what you have so far, back to back?LJW
I hope to have them adjacent to each other...back-to-back is what I can get for now (without using facets)...sorry about the confusion...VincentShen

3 Answers

3
votes

You can use facets. It seems you cannot stack and dodge at the same time (see related posts below). You can add another factor to your data for the x variable and facet on your names variable to come up with something like this:

Edit: Adjusted width of bars to have bars touching as per comments. See here: Remove space between bars ggplot2.

library(ggplot2)
p <- ggplot(data = df, aes(x = place, y = values, colour = num, fill = num))
p <- p + geom_bar(stat = "identity", width = 1, position = "stack")
p <- p + facet_grid(. ~ names)
p

enter image description here

It looks like you can adjust the margins of the facets to make the ABC groups look closer together if you're interested. For some examples, see these related posts:

ggplot2 - bar plot with both stack and dodge

ggplot2 geom_bar position = "dodge" does not dodge

Plotting a stacked bar plot?

Edited data with added "place" factor:

df <-    structure(list(names = structure(c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 
2L, 3L, 3L, 3L, 3L), .Label = c("A", "B", "C"), class = "factor"), 
    num = structure(c(1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 
    3L, 4L), .Label = c("aaa", "bbb", "ccc", "total"), class = "factor"), 
    values = c(1, 2, 3, 7, 2, 2, 5, 10, 3, 4, 2, 9), position = structure(c(1L, 
    1L, 1L, 2L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 2L), .Label = c("nums", 
    "total"), class = "factor")), .Names = c("names", "num", 
"values", "place"), row.names = c(NA, -12L), class = "data.frame")
2
votes

You can also increase the width of the bars to better fit the figure. Try this:

 p = ggplot(df, aes(x=factor(names)), ) + 
  geom_bar(width=0.75,data=subset(df,num=="total"), aes(y=values), stat="identity",width=.5) +
  geom_bar( width=0.75, data=subset(df,num!="total"), aes(y=-values,fill=factor(num)), stat="identity",width=.5) 
print(p)

New plot

EDIT:

I think I misunderstood your question. Do you want the bars within one name to be side by side?

0
votes

I believe there have been changes to the ggplot2 package since these answers were posted. I am using R version 4.0.2 and ggplot2 version 3.3.3. If I follow what you are asking, I believe the stat='identity' and the position=position_dodge() arguments inside the geom_bar function should get after what you desired, at least it did for me.

  ggplot(df, aes(names, values, fill=num))+
     geom_bar(stat='identity', position = position_dodge())

enter image description here