Reputation: 6410
I want to visualize a 42x42 matrix as 28 separate heatmaps, each heatmap being 6x6 matrix with the values plotted on the top of colours. I only need lower half of he matrix, I don't want to plot anything that has been excluded. The subsequent 6x6 matrixes shouldn't overlap, as in the example below:
d = as.matrix(read.table("http://dl.dropbox.com/u/2505196/matrix_posthoc_tukey.dat"))
d[upper.tri(d)] <- NA
d1 <- d[1:6, 1:6]
d2 <- d[1:6, 7:12]
d3 <- d[1:6, 13:18]
d4 <- d[1:6, 18:24]
#...etc, up to d28 <- d[37:42,37:42]
Code I used to create a single heatmap looks like this:
#baseline to create a separated space for all 28 plots
par(mfrow=c(4,7), mar=c(2,2,4,1), oma=c(2,4,2,2))
#using `image` to create heatmap, with color breaks defined by specific values
#the code below create just single heatmap
image(x=1:6, y=1:6, axes = FALSE, ylab="", xlab="", d1,
breaks=c(min(d1,na.rm=TRUE), -5.45, -4.65, 4.65, 5.45, max(d1,na.rm=TRUE)),
col=c("red","orange","white","orange","red"))
axis(2, 1:6, cex.axis = 0.7, las=1, tick=F)
axis(3, 1:6, cex.axis = 0.7, tick=F)
#create vertical and forizontal lines
abline(h=seq(0.5,6.5,1), v=seq(0.5,6.5,1))
#plot values from the specific matrix subset
for (i in 1:6)
{
for (j in 1:6)
{
txt <- sprintf("%0.1f", d1[i,j])
text(i, j, txt, cex=0.7)
}
}
Three such heatmaps look like this:
That's where I'm stuck. I have to manually change d
value every time I add another image to my single-page, multiple heatmap collection. I don't know how to create a nice loop to plot those specific subsets of matrix at the same time using the code above.
Alternative solutions with ggplot2, lattice are also welcomed, although I believe the main question here is a good loop to make this series of heatmaps.
Upvotes: 2
Views: 4321
Reputation: 14872
This is quite a complex plot, but it can be readily produced by the standard graphics library in R. It is more or less only a matter of keeping track of what indices goes into which panel. The way you extract the d1
to d28
matrices can be automated so you don't have to write out each and every line.
# Get the submatrices
I <- unlist(lapply(0:6, function(a) a:6))
J <- rep(0:6, 7:1)
d2 <- mapply(function(i,j) d[1:6+6*i, 1:6+6*j], I, J, SIMPLIFY=FALSE)
# Setup the layout and add an outer margin for the title and axis labels
layout(matrix(c(1:28, 0, 0), 5, 6))
par(oma=c(3,3,3,1), mar=c(2,2,1,1))
# Plot all the matrices oriented the same way they appear in text
# i.e. the first (vertical) dimension is plotted along the Y-axis
for(k in 1:length(d2)){
x <- 1:6+6*J[k]
y <- 1:6+6*I[k]
# Heatmap & grid
image(x, y, t(d2[[k]][nrow(d2[[k]]):1,]), las=1, axes=FALSE,
breaks=c(-1e10, -5.45, -4.65, 4.65, 5.45, 1e10),
col=c("red","orange","white","orange","red"))
xg <- apply(!is.na(d2[[k]]), 2, sum)
yg <- rev(apply(!is.na(d2[[k]]), 1, sum))
segments(c(x[1]-1, x)+.5, min(y)-.5,
c(x[1]-1, x)+.5, min(y)+c(6, yg)-.5, xpd=TRUE)
segments(min(x)-.5, c(y[1]-1, y)+.5,
min(x)+c(6,xg)-.5, c(y[1]-1, y)+.5, xpd=TRUE)
# X & Y-axis values
mtext(x, 1, .1, at=x, cex=.5)
mtext(rev(y), 2, .2, at=y, las=1, cex=.5)
# Values of each cell
text(rep(x, each=6), rep(rev(y), 6),
sub("NA", "", sprintf("%.2f", d2[[k]])), cex=.3)
}
# Add title and axis labels
title("All 28 submatrices", outer=TRUE)
mtext("Columns", outer=TRUE, 1, 1)
mtext("Rows", outer=TRUE, 2, 1)
The numbers in each cell may be tiny, but if you plot it to a pdf and zoom in they can be read. The xpd
parameter of the segments
functions supressess R from clipping the lines to the plot area (otherwise the outer lines would appear slightly thinner).
Upvotes: 4
Reputation: 9850
To have 6x6 sub-arrays of your original matrix you may act as follows:
for (i in seq(1, 42, 6))
for (j in seq(i, 42, 6)) {
dsub = d[i:(i+5), j:(j+5)]
...
}
However I suggest using a better way to create heatmaps - rather than re-inventing it. Although my favorite package for making normal heatmaps - as you want with numbers inside the cells - is pheatmap
(= pretty heatmap), but it does not support multiple small heatmaps in the same page. This is just an example of pheatmap()
output, you may see the help of the function by running ?pheatmap
after you have installed and loaded the package.
To have multiple heatmaps in the same page you may use ggplot2
package. Here are good manuals of how to make ggplot2 heatmaps and also having multiple plots on the same page.
Upvotes: 1
Reputation: 5834
I think you just need a nested loop, and your d#'s will have to be an array (I'll call it subs for submatrices). Excuse my code as I do not really know R but something like this:
for (row in 1:7)
{
for (col in 1:7)
{
subs[((row-1)*6)+j] <- d[ ((row-1)*6) + 1) : (row*6), (((col-1)*6) + 1) : (col*6)]
}
}
This will give you all 49 submatrices. If you only want the first 4 columns of submatrices you can range col from 1:4 in the loop.
Upvotes: 0