6

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)
VincentShen
  • 63
  • 1
  • 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 May 05 '15 at 23:12
  • 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 May 07 '15 at 00:24

3 Answers3

4

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")
Community
  • 1
  • 1
LJW
  • 795
  • 2
  • 13
  • 27
  • Good to know this trick, but I do hope the bar for "nums" and "total" are shown as close as possible. If it is impossible to do dodge and stack together, I will definitely use this alternative. Thanks! – VincentShen May 07 '15 at 00:22
2

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?

MichaelVE
  • 1,304
  • 10
  • 15
  • Yes..I want something like the plot generated by LJW as shown below, but I don't want to use facet..According to him, it seems stack and dodge can't be done together...not sure if you know how to do this..thanks! – VincentShen May 07 '15 at 00:19
0

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

justin1.618
  • 691
  • 5
  • 15