combination of stacked area plot and stacked bar plot

584 views
Skip to first unread message

Anja Mirenska

unread,
May 15, 2012, 3:32:33 AM5/15/12
to ggplot2
Dear all

I would like to create a combination of a stacked bar plot and a stacked area plot. Basically, it should be a stacked area plot with a nominal scale of the x-axis, but the edges should be replaced by stacked bars. Or to put it the other way round: it should be a stacked bar plot whis some space between the bars, and the boundaries of the bars should be connected according to the grouping variable.

I have tried the following:


ggplot(my.data, aes(x=variable, y=value, group=Name, fill=Name)) +
geom_area
(positon="fill") +
geom_bar
(stat="identity", colour="black")
But obviously this code does not produce the desired result, as the area plot connects the midlines of the bar plots rather than the boundaries.

Is it possible to achieve the plot I described above?

Best wishes

Anja

Brandon Hurr

unread,
May 15, 2012, 10:19:43 AM5/15/12
to Anja Mirenska, ggplot2
I'm not sure I understand. Could you supply some sample data and draw a picture of what you want? Doesn't have to be perfect... 

B


Anja

--
You received this message because you are subscribed to the ggplot2 mailing list.
Please provide a reproducible example: https://github.com/hadley/devtools/wiki/Reproducibility
 
To post: email ggp...@googlegroups.com
To unsubscribe: email ggplot2+u...@googlegroups.com
More options: http://groups.google.com/group/ggplot2

Anja Mirenska

unread,
May 15, 2012, 11:06:22 AM5/15/12
to Brandon Hurr, ggplot2
Ok, here is a data example:

groups <- c("Group1", "Group2", "Group3", "Group1", "Group2", "Group3", "Group1", "Group2", "Group3")
variable <- c("Var16", "Var16", "Var16", "Var32", "Var32", "Var32", "Var35", "Var35", "Var35")
value <- c(21.1, 13, 5, 39.6, 3.7, 10.4, 21.7, 1, 30)
df <- data.frame(groups, variable, value)

I attached a bar plot that I modified with a graphic program to indicate approximately what I mean. For sure this picture is not perfect, as e.g. I would like the bars to be thinner etc., but I hope you get the idea.
 

2012/5/15 Brandon Hurr <brando...@gmail.com>
area_bar_plot.jpg

Brandon Hurr

unread,
May 15, 2012, 12:42:44 PM5/15/12
to Anja Mirenska, ggplot2
Thanks for the example and mockup. You're right that the width of the geom_area output is limited to the means of the data so they won't connect to the edges of the bars like you want. 

I wish I had a better idea if you could hack the area output to the width of the bars, but that would need to be answered by someone with greater knowledge of the guts like Hadley, Takahashi-san or Winston. 

Anja Mirenska

unread,
May 15, 2012, 1:18:02 PM5/15/12
to Brandon Hurr, ggplot2
Brandon, thanks for your reply anyway!


2012/5/15 Brandon Hurr <brando...@gmail.com>

Winston Chang

unread,
May 16, 2012, 12:56:27 PM5/16/12
to Anja Mirenska, Brandon Hurr, ggplot2
Here's what I came up with. It's a little tricky... Instead of using geom_bar, I used geom_area, but to do this I had to do the following:
- Convert discrete xvar values to numeric xvarn
- Make a new data frame by copying the original data, but with new x values at x+0.3 and x-0.3
- The x axis will by default show as numeric, so specify the breaks and labels so it looks like a discrete axis.
- The vertical lines with geom_linerange go from 0 to xvarn, but then they're stacked (like the area plot).


# Set up data
groups <- c("Group1", "Group2", "Group3", "Group1", "Group2", "Group3", "Group1", "Group2", "Group3")
xvar <- c("Var16", "Var16", "Var16", "Var32", "Var32", "Var32", "Var35", "Var35", "Var35")
value <- c(21.1, 13, 5, 39.6, 3.7, 10.4, 21.7, 1, 30)
df <- data.frame(groups, xvar, value)

# Get numeric version of x values
df$xvarn <- as.numeric(df$xvar)

# Make new df with xvarn values shifted to right and left by .3
df2 <- rbind(transform(df, xvarn = xvarn-.3),
             transform(df, xvarn = xvarn+.3))

ggplot(df2, aes(x=xvarn, y=value, fill=groups)) +
  geom_area(colour="black") +
  geom_linerange(aes(ymax=xvarn), ymin=0, position="stack") +
  scale_x_continuous(breaks=1:max(df2$xvarn), labels=levels(df2$xvar))


-Winston
connectbars.png

Winston Chang

unread,
May 16, 2012, 1:00:33 PM5/16/12
to Anja Mirenska, Brandon Hurr, ggplot2
# Set up data
groups <- c("Group1", "Group2", "Group3", "Group1", "Group2", "Group3", "Group1", "Group2", "Group3")
xvar <- c("Var16", "Var16", "Var16", "Var32", "Var32", "Var32", "Var35", "Var35", "Var35")
value <- c(21.1, 13, 5, 39.6, 3.7, 10.4, 21.7, 1, 30)
df <- data.frame(groups, xvar, value)

# Get numeric version of x values
df$xvarn <- as.numeric(df$xvar)

# Make new df with xvarn values shifted to right and left by .3
df2 <- rbind(transform(df, xvarn = xvarn-.3),
             transform(df, xvarn = xvarn+.3))

ggplot(df2, aes(x=xvarn, y=value, fill=groups)) +
  geom_area(colour="black") +
  geom_linerange(aes(ymax=xvarn), ymin=0, position="stack") +
  scale_x_continuous(breaks=1:max(df2$xvarn), labels=levels(df2$xvar))


Oops, that code was slightly wrong. It should be this (with ymax=value, ymax=xvarn):

ggplot(df2, aes(x=xvarn, y=value, fill=groups)) +
  geom_area(colour="black") +
  geom_linerange(aes(ymax=value), ymin=0, position="stack") +
  scale_x_continuous(breaks=1:max(df2$xvarn), labels=levels(df2$xvar))

 

Anja Mirenska

unread,
May 16, 2012, 3:54:23 PM5/16/12
to Winston Chang, Brandon Hurr, ggplot2
Wow, that's awesome, thanks a lot!


2012/5/16 Winston Chang <winsto...@gmail.com>

Anja Mirenska

unread,
May 21, 2012, 9:58:44 AM5/21/12
to ggplot2, Winston Chang, Brandon Hurr
After discussing the resulting plot with my colleagues, I was asked to make the "connecting part" in a lighter colour than the bars. So I tried to overlay a bar plot onto the area plot created the way Winston suggests.

ggplot(df2, aes(x=xvarn, y=value, fill=groups, width=0.3)) +
  geom_area(colour="grey") +
  scale_fill_hue(c=45, l=80) +
  geom_bar(position="stack", stat="identity") +
  scale_fill_hue(l=40) +
  scale_x_continuous(breaks=1:max(df2$xvarn), labels=levels(df2$xvarn))

This didn't work, as this approach would have required different colour scales, which according to Brandon's recent answer to Roey is not possible in ggplot2. Instead, Brandon suggested to overlay plots with grid, so that's what I tried next:

library("gridExtra")

df3 <- df2[1:(nrow(df2)/2),]  # remove duplicates for the bar plot

vp1 <- viewport(width=1, height=1, x=0.5, y=0.515)  # create a viewport of the same size and position as the plot area

p1 <- ggplot(df2, aes(x=xvarn, y=value, fill=groups)) +
  geom_area(colour="grey") +
  scale_fill_hue(c=45, l=80)

p2 <- ggplot(df2, aes(x=xvarn, y=value, fill=groups, width=0.3)) +
  geom_bar(position="stack", stat="identity") +
  scale_fill_hue(l=40) +
  opts(
    panel.background=theme_rect(fill="transparent", colour=NA),
    plot.background=theme_rect(fill="transparent", colour=NA)) +
  scale_x_continuous(breaks=1:max(df2$xvarn), labels=levels(df2$xvarn))

p1
print(p2, vp=vp1)

Fortunately, the principle worked. Unfortunately, the result isn't perfect. The bars are slightly higher than the area height, and the y-values as well as the legend are slightly shifted. I tried ylim(0, 55) to fix the y-scale, but it didn't help. Does anyone have an idea what I could do to get a precise overlay?



2012/5/16 Anja Mirenska <a.mir...@googlemail.com>

Winston Chang

unread,
May 21, 2012, 10:10:10 AM5/21/12
to Anja Mirenska, ggplot2, Brandon Hurr
On Mon, May 21, 2012 at 8:58 AM, Anja Mirenska <a.mir...@googlemail.com> wrote:
After discussing the resulting plot with my colleagues, I was asked to make the "connecting part" in a lighter colour than the bars. So I tried to overlay a bar plot onto the area plot created the way Winston suggests.

ggplot(df2, aes(x=xvarn, y=value, fill=groups, width=0.3)) +
  geom_area(colour="grey") +
  scale_fill_hue(c=45, l=80) +
  geom_bar(position="stack", stat="identity") +
  scale_fill_hue(l=40) +
  scale_x_continuous(breaks=1:max(df2$xvarn), labels=levels(df2$xvarn))


You could also use 'alpha' for the geom_area:

library(ggplot2)
ggplot(df2, aes(x=xvarn, y=value, fill=groups)) +
  geom_area(colour="grey", alpha=.6) +
  geom_bar(data=df, position="stack", stat="identity", aes(width=0.6)) +

Anja Mirenska

unread,
May 21, 2012, 10:19:02 AM5/21/12
to Winston Chang, ggplot2, Brandon Hurr
Oh, ok, so I made everything much more complicated than it actually is. Thanks for your help!


2012/5/21 Winston Chang <winsto...@gmail.com>

Johannes Björk

unread,
Oct 10, 2012, 8:15:35 AM10/10/12
to Winston Chang, ggp...@googlegroups.com
Hi,

Sorry for maybe posting at the wrong place...

I wanted to create a very similar thing and I have tried the script
you wrote, however my plot gets really weird. I though it was because
some of y values for x was missing, but when entering the mean value
in those places the plot looks the same...

Anyhow, this is how my data looks like; https://www.dropbox.com/s/b7kghckpph37jlb/ao_percent_class.txt

What I run is;

> ao=read.table('ao_percent_class.txt', sep="", header=F)
> names(ao)=c("Class","3","4","5","6","7","8","9","10","11","12","1","2")
> ao.m=melt(ao)
Using Class as id variables
> names(ao.m)=c("class", "month", "r.abu")
> ao.m$month=as.numeric(ao.m$month)
> ggplot(ao.m, aes(x=month, y=r.abu, fill=class, na.rm=FALSE)) +
+ geom_area(colour="black") +
+ geom_linerange(aes(ymax=r.abu), ymin=0, position="stack") +
+ scale_x_continuous(breaks=1:max(ao.m$month), labels=levels(ao.m
$month))

Thanks for any help!

Cheers

Johannes
> <a.miren...@googlemail.com>wrote:
>
>
>
>
>
>
>
> > Brandon, thanks for your reply anyway!
>
> > 2012/5/15 Brandon Hurr <brandon.h...@gmail.com>
>
> >> Thanks for the example and mockup. You're right that the width of the
> >> geom_area output is limited to the means of the data so they won't connect
> >> to the edges of the bars like you want.
>
> >> I wish I had a better idea if you could hack the area output to the width
> >> of the bars, but that would need to be answered by someone with greater
> >> knowledge of the guts like Hadley, Takahashi-san or Winston.
>
> >> On Tue, May 15, 2012 at 4:06 PM, Anja Mirenska <a.miren...@googlemail.com
> >> > wrote:
>
> >>> Ok, here is a data example:
>
> >>> groups <- c("Group1", "Group2", "Group3", "Group1", "Group2", "Group3",
> >>> "Group1", "Group2", "Group3")
> >>> variable <- c("Var16", "Var16", "Var16", "Var32", "Var32", "Var32",
> >>> "Var35", "Var35", "Var35")
> >>> value <- c(21.1, 13, 5, 39.6, 3.7, 10.4, 21.7, 1, 30)
> >>> df <- data.frame(groups, variable, value)
>
> >>> I attached a bar plot that I modified with a graphic program to indicate
> >>> approximately what I mean. For sure this picture is not perfect, as e.g. I
> >>> would like the bars to be thinner etc., but I hope you get the idea.
>
> >>> 2012/5/15 Brandon Hurr <brandon.h...@gmail.com>
>  connectbars.png
> 14 KVerDescargar
Reply all
Reply to author
Forward
0 new messages