4
votes

I seek advice on ggplot2::geom_bar().

My data contains the categories "A", "B", "Z". I want A, B as stacked bar, and Z as dodged bar next to the A, B bar.

# sample data
A = c(3, 4, 3, 5)
B = c(2, 2, 1, 4)
Z = c(1, 2, 1, 2)
R = c(-2, -1, -3, 0)
S = c(7,7,7,9)
mydata = data.frame(cbind(A,B,Z,R,S))
dates = c("2014-01-01","2014-02-01","2014-03-01","2014-04-01")
mydata$date = as.Date(dates)
mydata.m = melt(mydata,id="date")
names(mydata.m) = c("variable", "category","value")



s = ggplot(mydata.m, aes(x=variable, fill=category)) +        
# bars for cat A, B, Z
geom_bar(data=subset(mydata.m, category %in% c("A","B")), aes(y=value), stat="identity", position="stack") +
geom_bar(subset=.(category=="Z"), aes(y=-value), stat="identity", position="dodge") +

# lines for cat R, S
geom_line(subset=.(category=="R"), aes(y=value), linetype="solid", size=1) + 
geom_line(subset=.(category=="S"), aes(y=value), linetype="solid", size=1) ;

The above does not put Z bar next to AB bar, but overlaying the AB bar. Which problem is solved with aes(y=-value) or with facet_wrap to make separate plot for "Z". But I prefer it next to to AB bar, not below. Any suggestions? Thanks!

Side question: the fill legend has colors for R, S but in the plot the lines are just black, i don't see why?

1
How about adding some sample data so that people can better understand your problem?nrussell
Sample data added to post.user3817704
could you add the required packeges in your code ? Your code needs the reshape2 package. It needs another package that I dont know which defines the "." functionGiuliano
@Giuliano: I am not loading any specific package for the .() function, I don't even understand what it does and find no info on it. You can achieve the same with the subset function as in the code. Rgdsuser3817704

1 Answers

2
votes

Okay, so a couple of days ago there was a good question somewhat similar to the root issue you are facing, so I'm going to build off the method used in that question's accepted answer because it works and I'm not aware of any other way to have stacked and dodged bars in ggplot2. Essentially, the solution is to manually shift the data for the geom_bar of interest. This was a bit more straight forward in the cited question above because the horizontal axis was numeric, so you could quickly determine how much to shift by. In this situation your horizontal axis is of class Date, but it does not require much more effort to find an appropriate shift value for your category==Z subset of data. First build a plot object, like you did above (I'll call this s2); except you know your two geom_bars are overlapping, so you will have to play around with the width a bit - I used width = 5 after some trial and error:

s2 <- ggplot(
        mydata.m, 
        aes(x=variable, fill=category))+
      geom_bar(
        data=subset(mydata.m, category %in% c("A","B")), 
        aes(y=value), 
        stat="identity", 
        position="stack",
        width=5) +
     geom_bar(data = 
        subset(mydata.m, category=="Z"), 
        aes(y=-value), 
        stat="identity", 
        position="dodge",
        width=5)+ 
     geom_line(
        data = subset(mydata.m,category=="R"), 
        aes(y=value), 
        linetype="solid", 
        size=1) + 
     geom_line(
        data = subset(mydata.m,category=="S"), 
        aes(y=value), 
        linetype="solid", 
        size=1)
     ##
     s2

enter image description here

Then, we can look at the horizontal positions of the plot object by doing

> ggplot_build(s2)$panel$ranges[[1]]$x.major
    Jan 01     Jan 15     Feb 01     Feb 15     Mar 01     Mar 15     Apr 01 
0.06937799 0.20334928 0.36602871 0.50000000 0.63397129 0.76794258 0.93062201

I suppose you can eyeball this one by looking at the x-axis labels on the plot of s2, but incase you want to calculate specific positions / coordinates, this will give you the details. Either way, you just need to make some adjustments to the data you want to shift, in this case category == Z. I made a new object for this, s3, where I shifted the data by 7 (i.e. 7 days, since variable is a Date):

s3 <- ggplot(
  mydata.m, 
  aes(x=variable, fill=category))+
  geom_bar(
    data=subset(mydata.m, category %in% c("A","B")), 
    aes(y=value), 
    stat="identity", 
    position="stack",
    width=5) +
  geom_bar(
    subset(mydata.m, category=="Z"), 
    aes(y=value,x=variable+7), 
    stat="identity", 
    position="dodge",
    width=5)+ 
  geom_line(
    data = subset(mydata.m, category=="R"), 
    aes(y=value), 
    linetype="solid", 
    size=1) + 
  geom_line(
    data = subset(mydata.m, category=="S"), 
    aes(y=value), 
    linetype="solid", 
    size=1)
s3
##

enter image description here

EDIT: Also, I'm not quite sure why your lines for R and S were coming out black, although I suspect it had something to do with the fact that you are using a different geom for them. Anyways, you can sort of get around this by doing

s5 <- ggplot(
  subset(mydata.m,
         !(category %in% c("R","S"))), 
  aes(x=variable, fill=category))+
  geom_bar(
    data=subset(mydata.m, category %in% c("A","B")), 
    aes(y=value), 
    stat="identity", 
    position="stack",
    width=5) +
  geom_bar(data = 
    subset(mydata.m, category=="Z"), 
    aes(y=value,x=variable+7), 
    stat="identity", 
    position="dodge",
    width=5)+ 
  geom_line(
    data=subset(
      mydata.m,
      category %in% c("R","S")), 
    aes(y=value,color=category), 
    linetype="solid", 
    size=1) + 
  geom_line(
    data=subset(
      mydata.m,
      category %in% c("R","S")), 
    aes(y=value,color=category), 
    linetype="solid", 
    size=1)
s5

enter image description here

This still shows R and S in the original legend box (most likely because they are factor levels of your factor category), but you can at least get another legend distinguishing them.