3

I am a newbie using ggplot2 and I'm trying to plot a scatter plot above a heatmap. Both plots have the same discrete x-axis.

This is the code I'm trying:

library(ggplot2)
library(grid)
library(reshape2)
#data for the scatterplot

df = data.frame(id1 = letters[1:10], C = abs(rnorm(10)))

#scatter plot 
p1 <- ggplot(df, aes(x= id1, y = C)) + 
  geom_point(pch = 19) + theme_bw() + 
  scale_x_discrete(expand = c(0, 0), breaks = letters[1:10]) +
  theme(legend.position = "none") + theme(axis.title.y = element_blank()) + theme(axis.title.x = element_blank())

#data for the heatmap
X = data.frame(matrix(rnorm(100), nrow = 10))
names(X) = month.name[1:10]
X = melt(cbind(id1 = letters[1:10], X))

#heatmap 
p2 <- ggplot(X,
             aes(x = id1, y = variable, fill = value))
p2 <- p2 + geom_tile()
p2 <- p2 + scale_fill_gradientn(colours = c("blue", "white" , "red"))
p2 <- p2 + theme(legend.position = "none") + theme(axis.title.y = element_blank()) + theme(axis.title.x = element_blank())
p2 <- p2 + scale_x_discrete(expand = c(0, 0), breaks = letters[1:10])
p2 <- p2 + scale_y_discrete(expand = c(0, 0))

layt <- grid.layout(nrow=2,ncol=1,heights=c(2/8,6/8),default.units=c('null','null'))

vplayout <- function(x,y) {viewport(layout.pos.row = x, layout.pos.col = y)}

grid.newpage()
pushViewport(viewport(layout=layt))
print(p1,vp=vplayout(1,1))
print(p2,vp = vplayout(2,1))

The problem is that the axis are not situated one above the other.

https://mail.google.com/mail/u/0/?ui=2&ik=81975edabc&view=att&th=13ece12a06a3cea2&attid=0.1&disp=emb&realattid=ii_13ece128398baede&zw&atsh=1

Is there any solution? It is possible to reshape the data and make something like facets?

afuzzyllama
  • 6,538
  • 5
  • 47
  • 64
  • I'd try the `grid.extra` package, but aligning axes like this is always difficult. You may want to consider different visualizations (i.e. include the legend in your heatmap so you can avoid the need for a scatter plot). – Justin May 22 '13 at 21:36

2 Answers2

2

Another option:

grid.draw(gtable:::rbind.gtable(ggplotGrob(p1),
                                ggplotGrob(p2), size='last'))

(ideally one would want size=max, but it has a bug preventing it to work).

baptiste
  • 75,767
  • 19
  • 198
  • 294
  • Definitely more elegant method to align the plots - I'll use it a lot! I think what makes this question different though is the alignment within the plots (because of the different `geom` in each), even if the left is aligned, the points will still not match the heatmap clearly. – alexwhan May 23 '13 at 00:11
  • the two alignment methods are virtually equivalent; only by working out the width manually you can use `unit.pmax`, which currently gtable:::rbind fails to do properly (you therefore need to guess which plot takes more space, hoping it's the same one across all viewports...) – baptiste May 23 '13 at 00:16
  • Thank you very much, baptiste... Just a last question, I forgot to mention that I need that the heatmap uses more than the lower half of the plot (maybe 2/5, 3/5). I don't want to steal more of your time, but where I can find information to know how to do it?? And thank you again... – user2411076 May 23 '13 at 07:53
  • 1
    in my opinion you should rather accept @alexwhan answer as it is currently better, if slightly less compact. Using grid.arrange at the end, you can use `heights = c(2,3)` and that should work. – baptiste May 23 '13 at 10:59
1

There are a couple of tricks here. The first is that the tick marks get treated differently, even though you have the same discrete axis. When you do expand = c(0,0), on the scatterplot the tick is now aligned with the y axis, while on the heatmap it is in the centre of the category. My method of getting around that is to manually assign the expand value for the scatterplot so that there is a gap of of 1/2 a categorical value. Because there are 10 categorical values, in this case it is 0.05 ((1/10)/2). The points will now align with the centre of each category.

The other side of the problem is because the y labels are different sizes they throw out the rest of the alignment. The solution comes from this question, using ggplot_gtable and grid.arrange from the gridExtra package.

library(gridExtra)
#data for the scatterplot

df = data.frame(id1 = letters[1:10], C = abs(rnorm(10)))

#scatter plot 
p1 <- ggplot(df, aes(x= id1, y = C)) + 
  geom_point(pch = 19) + theme_bw() + 
# Change the expand values
  scale_x_discrete(expand = c(0.05, 0.05), breaks = letters[1:10]) +
  #scale_y_discrete(breaks = NULL) +

  theme(legend.position = "none") + theme(axis.title.y = element_blank()) + theme(axis.title.x = element_blank())
p1
#data for the heatmap
X = data.frame(matrix(rnorm(100), nrow = 10))
names(X) = month.name[1:10]
X = melt(cbind(id1 = letters[1:10], X))

#heatmap 
p2 <- ggplot(X,
             aes(x = id1, y = variable, fill = value))
p2 <- p2 + geom_tile()
p2 <- p2 + scale_fill_gradientn(colours = c("blue", "white" , "red"))
p2 <- p2 + theme(legend.position = "none") + theme(axis.title.y = element_blank()) + theme(axis.title.x = element_blank())
p2 <- p2 + scale_x_discrete(expand = c(0, 0), breaks = letters[1:10])
p2 <- p2 + scale_y_discrete(expand = c(0, 0))

#Here's the gtable magic
gp1<- ggplot_gtable(ggplot_build(p1))
gp2<- ggplot_gtable(ggplot_build(p2))
#This identifies the maximum width
maxWidth = unit.pmax(gp1$widths[2:3], gp2$widths[2:3])
#Set each to the maximum width
gp1$widths[2:3] <- maxWidth
gp2$widths[2:3] <- maxWidth
#Put them together
grid.arrange(gp1, gp2)

EDIT - See @baptiste's answer for a more elegant method of alignment of the y axis

enter image description here

Community
  • 1
  • 1
alexwhan
  • 15,636
  • 5
  • 52
  • 66