Geek On Acid
Geek On Acid

Reputation: 6410

Create multiple separate heatmaps from a single matrix

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:

enter image description here

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

Answers (3)

Backlin
Backlin

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).

enter image description here

Upvotes: 4

Ali
Ali

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.

enter image description here

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

Superboggly
Superboggly

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

Related Questions