0

I would like to construct a stacked bar plot (or arrow plot), preferably using ggplot2, with the example data d below, for each id.

The peculiarity is that I would like for the stack to follow the columns order v0 first, z1 second, and z2 third (data is in wide format). Notice however that z2 has some negative values. So it should somehow be represented by an arrow or something, from the top of the stack going down (or up, depending on the value of z3).

Is there a way to do this?

d <- data.frame(
  id=c('AAA','BBB','CCC','DDD','EEE','FFF'),
  v0 =c( 50  , 60  , 40  , 50  , 70  ,40),
  z1 =c( 20  , 15  , 5   , 40  , 5  , 40),  
  z2 =c(-10  , 5   , 10  ,-20  , 15 ,-15)  
)

#normaly people transform the data to long format:
d %>% gather(ef_type, value,v0:d2) %>%
  ggplot(aes(...) + geom_bar(...)

OBS: Notice that this is different from the answer to this related question, in which the order of the columns does not matter, and the negative values are represented in the negative part of the y-axis.

EDIT1: based on @fanli answer bellow I tried the following code:

d %>% select(id,v0,z1) %>% gather(ef_type, value,v0:z1) -> df
d %>% mutate(z2_start=v0+z1,z2_end=v0+z1+z2) %>% select(id,z2_start,z2_end) -> df2

ggplot(df, aes(x=id,y=value,fill=ef_type))+ geom_bar(stat = "identity") +
  + geom_segment(data=df2, aes(x=id, xend=id, y=z2_start, yend=z2_end), arrow = arrow(length = unit(0.02, "npc")))

#which results in the error:
Error: ggplot2 doesn't know how to deal with data of class uneval
Community
  • 1
  • 1
LucasMation
  • 2,408
  • 2
  • 22
  • 45

1 Answers1

4

Are you looking for something like this?

df <- melt(d)
df$absvalue <- abs(df$value)
df <- ddply(df, .(id), transform, pos = 
  ifelse(value<0, cumsum(absvalue)-(0.4 * absvalue), 
  cumsum(absvalue)-(0.6 * absvalue)))
df <- ddply(df, .(id), transform, pos2 = 
  ifelse(value>0, cumsum(absvalue)-(0.4 * absvalue), 
  cumsum(absvalue)-(0.6 * absvalue)))
ggplot(df, aes(x=id,y=absvalue,group=variable,fill=variable)) 
  + geom_bar(stat="identity", position="stack") 
  + geom_segment(aes(x=id, xend=id, y=pos, yend=pos2), arrow = arrow(length = unit(0.02, "npc")))

enter image description here

You can play with the position/number/size of the arrows, but geom_segment seems like one possibility for what you'd like.

Edit: Based on clarification of desired output:

df <- melt(d)

ggplot(subset(df, variable!="z2"), aes(x=id,y=value,group=variable,fill=variable)) 
+ geom_bar(stat="identity", position="stack") + geom_segment(data=d, 
  aes(x=id, xend=id, y=v0+z1, yend=v0+z1+z2), arrow = arrow(length = unit(0.02, "npc")), inherit.aes=F)

enter image description here

Edit 2: Use scale_fill_manual and scale_color_manual to customize your legend keys:

d <- data.frame(
  id=c('AAA','BBB','CCC','DDD','EEE','FFF'),
  v0 =c( 50  , 60  , 40  , 50  , 70  ,40),
  z1 =c( 20  , 15  , 5   , 40  , 5  , 40),  
  z2 =c(-10  , 5   , 10  ,-20  , 15 ,-15)  
)
d$cc <- "effect B"
df <- melt(d)
ggplot(subset(df, variable!="z2"), aes(x=id,y=value,group=variable,fill=variable)) + geom_bar(stat="identity", position="stack") + geom_segment(data=d, aes(x=id, xend=id, y=v0+z1, yend=v0+z1+z2, color=cc), arrow = arrow(length = unit(0.02, "npc")), inherit.aes=F) + scale_color_manual(values="black") + scale_fill_manual(values=c("skyblue", "red"), labels=c("base level", "effect A"))
fanli
  • 1,069
  • 7
  • 13
  • cool. It is more or less what I want. Cathegory z2 should not be displayed as a bar, only as an arrow, starting at "v0+z1" and ending at "v0+z1-z2" – LucasMation Apr 07 '16 at 15:51
  • This is great! The only problem remaining with this chart is that it would be nice to atribute some value to the legend. Lets say the labels are v0: "base level" ; z1: "effect A"; z2 (arrow) "effect B". Can this ne included in the legend somehow? – LucasMation Apr 07 '16 at 16:14
  • are you using reshape2::melt ? I tried replicating your code in my pc but I'm getting an error: Error in +geom_bar(stat = "identity", position = "stack") : invalid argument to unary operator – LucasMation Apr 07 '16 at 16:26
  • tks. I managed to make it work. Another possibility is to have both, z1 and z2 represented by arrows, as in this example: http://stackoverflow.com/questions/29468437/look-of-arrows-in-ggplot2-geom-segment (although as you see in the question thick arrows give rendering problems) – LucasMation Apr 07 '16 at 17:02