Enhanced Heat Maps with heatmap.2

Andy Liaw, original; R. Gentleman, M. Maechler, W. Huber, G. Warnes, revisions. Tal Galili

2025-11-29

Introduction

The heatmap.2 function provides an enhanced version of the standard R heatmap function with numerous additional features and customization options. This vignette demonstrates the key capabilities and usage patterns of heatmap.2. It is based on the manual for ?heatmap.2, written by Andy Liaw, original; R. Gentleman, M. Maechler, W. Huber, G. Warnes, revisions.

Details on Dendrograms, Scaling, and Plot Layout

Dendrogram Behavior (Rowv, Colv)

Data Scaling (scale)

Color Selection

Plot Layout (Default and Customization)

Note

Load Data

Code
# Load required libraries
library(gplots)  # for heatmap.2
library(RColorBrewer)  # for better color palettes
Code
data(mtcars)
x  <- as.matrix(mtcars)
rc <- rainbow(nrow(x), start=0, end=.3)
cc <- rainbow(ncol(x), start=0, end=.3)

Dendrogram Options

demonstrate the effect of row and column dendrogram options

Code
heatmap.2(x)                    ## default - dendrogram plotted and reordering done.

Code
heatmap.2(x, dendrogram="none") ##  no dendrogram plotted, but reordering done.

Code
heatmap.2(x, dendrogram="row")  ## row dendrogram plotted and row reordering done.

Code
heatmap.2(x, dendrogram="col")  ## col dendrogram plotted and col reordering done.

Code
heatmap.2(x, keysize=2)         ## default - dendrogram plotted and reordering done.

Code
heatmap.2(x, Rowv=FALSE, dendrogram="both") ## generates a warning!

Code
heatmap.2(x, Rowv=NULL, dendrogram="both")  ## generates a warning!
heatmap.2(x, Colv=FALSE, dendrogram="both") ## generates a warning!

Reorder dendrogram by branch means rather than sums

Code
heatmap.2(x, reorderfun=function(d, w) reorder(d, w, agglo.FUN = mean) )

Color dendrograms’ branches (using dendextend)

Code
full <- heatmap.2(x) # we use it to easily get the dendrograms
Code
library(dendextend)  # for color_branches
heatmap.2(x, 
        Rowv=color_branches(full$rowDendrogram, k = 3),
        Colv=color_branches(full$colDendrogram, k = 2))

Code
# Look at the vignette for more details:
# https://cran.r-project.org/web/packages/dendextend/vignettes/dendextend.html

plot a sub-cluster using the same color coding as for the full heatmap

Code
full <- heatmap.2(x)

Code
heatmap.2(x, Colv=full$colDendrogram[[2]], breaks=full$breaks)  # column subset

Code
heatmap.2(x, Rowv=full$rowDendrogram[[1]], breaks=full$breaks)  # row subset

Code
heatmap.2(x, Colv=full$colDendrogram[[2]],
            Rowv=full$rowDendrogram[[1]], breaks=full$breaks)  # both

Show effect of row and column label rotation

Code
heatmap.2(x, srtCol=NULL)

Code
heatmap.2(x, srtCol=0,   adjCol = c(0.5,1) )

Code
heatmap.2(x, srtCol=45,  adjCol = c(1,1)   )

Code
heatmap.2(x, srtCol=135, adjCol = c(1,0)   )

Code
heatmap.2(x, srtCol=180, adjCol = c(0.5,0) )

Code
heatmap.2(x, srtCol=225, adjCol = c(0,0)   ) ## not very useful

Code
heatmap.2(x, srtCol=270, adjCol = c(0,0.5) )

Code
heatmap.2(x, srtCol=315, adjCol = c(0,1)   )

Code
heatmap.2(x, srtCol=360, adjCol = c(0.5,1) )

Code
heatmap.2(x, srtRow=45, adjRow=c(0, 1) )

Code
heatmap.2(x, srtRow=45, adjRow=c(0, 1), srtCol=45, adjCol=c(1,1) )

Code
heatmap.2(x, srtRow=45, adjRow=c(0, 1), srtCol=270, adjCol=c(0,0.5) )

Show effect of offsetRow/offsetCol (only works when srtRow/srtCol is

Code
## not also present)
heatmap.2(x, offsetRow=0, offsetCol=0)

Code
heatmap.2(x, offsetRow=1, offsetCol=1)

Code
heatmap.2(x, offsetRow=2, offsetCol=2)

Code
heatmap.2(x, offsetRow=-1, offsetCol=-1)

Code
heatmap.2(x, srtRow=0, srtCol=90, offsetRow=0, offsetCol=0)

Code
heatmap.2(x, srtRow=0, srtCol=90, offsetRow=1, offsetCol=1)

Code
heatmap.2(x, srtRow=0, srtCol=90, offsetRow=2, offsetCol=2)

Code
heatmap.2(x, srtRow=0, srtCol=90, offsetRow=-1, offsetCol=-1)

Show how to use ‘extrafun’ to replace the ‘key’ with a scatterplot

Code
lmat <- rbind( c(5,3,4), c(2,1,4) )
lhei <- c(1.5, 4)
lwid <- c(1.5, 4, 0.75)

myplot <- function() {
           oldpar <- par("mar")
           par(mar=c(5.1, 4.1, 0.5, 0.5))
           plot(mpg ~ hp, data=x)
         }

heatmap.2(x, lmat=lmat, lhei=lhei, lwid=lwid, key=FALSE, extrafun=myplot)

show how to customize the color key

Code
heatmap.2(x,
         key.title=NA, # no title
         key.xlab=NA,  # no xlab
         key.par=list(mgp=c(1.5, 0.5, 0),
                      mar=c(2.5, 2.5, 1, 0)),
         key.xtickfun=function() {
               breaks <- parent.frame()$breaks
               return(list(
                    at=parent.frame()$scale01(c(breaks[1],
                                                breaks[length(breaks)])),
                    labels=c(as.character(breaks[1]),
                             as.character(breaks[length(breaks)]))
                    ))
         })

Code
heatmap.2(x,
        breaks=256,
        key.title=NA,
        key.xlab=NA,
        key.par=list(mgp=c(1.5, 0.5, 0),
                     mar=c(1, 2.5, 1, 0)),
        key.xtickfun=function() {
             cex <- par("cex")*par("cex.axis")
             side <- 1
             line <- 0
             col <- par("col.axis")
             font <- par("font.axis")
             mtext("low", side=side, at=0, adj=0,
                   line=line, cex=cex, col=col, font=font)
             mtext("high", side=side, at=1, adj=1,
                   line=line, cex=cex, col=col, font=font)
             return(list(labels=FALSE, tick=FALSE))
        })

Show effect of z-score scaling within columns, blue-red color scale

Code
##
##
hv <- heatmap.2(x, col=bluered, scale="column", tracecol="#303030")

Code
###
## Look at the return values
###
names(hv)
##  [1] "rowInd"        "colInd"        "call"          "colMeans"     
##  [5] "colSDs"        "carpet"        "rowDendrogram" "colDendrogram"
##  [9] "breaks"        "col"           "vline"         "colorTable"   
## [13] "layout"
Code
## Show the mapping of z-score values to color bins
hv$colorTable
##           low       high   color
## 1  -3.2116766 -2.7834531 #0000FF
## 2  -2.7834531 -2.3552295 #2424FF
## 3  -2.3552295 -1.9270060 #4949FF
## 4  -1.9270060 -1.4987824 #6D6DFF
## 5  -1.4987824 -1.0705589 #9292FF
## 6  -1.0705589 -0.6423353 #B6B6FF
## 7  -0.6423353 -0.2141118 #DBDBFF
## 8  -0.2141118  0.2141118 #FFFFFF
## 9   0.2141118  0.6423353 #FFDBDB
## 10  0.6423353  1.0705589 #FFB6B6
## 11  1.0705589  1.4987824 #FF9292
## 12  1.4987824  1.9270060 #FF6D6D
## 13  1.9270060  2.3552295 #FF4949
## 14  2.3552295  2.7834531 #FF2424
## 15  2.7834531  3.2116766 #FF0000
Code
## Extract the range associated with white
hv$colorTable[hv$colorTable[,"color"]=="#FFFFFF",]
##          low      high   color
## 8 -0.2141118 0.2141118 #FFFFFF
Code
## Determine the original data values that map to white
whiteBin <- unlist(hv$colorTable[hv$colorTable[,"color"]=="#FFFFFF",1:2])
rbind(whiteBin[1] * hv$colSDs + hv$colMeans,
     whiteBin[2] * hv$colSDs + hv$colMeans )
##           cyl        am        vs     carb       wt     drat     gear     qsec
## [1,] 5.805113 0.2994102 0.3295842 2.466667 3.007751 3.482081 3.529527 17.46614
## [2,] 6.569887 0.5130898 0.5454158 3.158333 3.426749 3.711044 3.845473 18.23136
##           mpg       hp     disp
## [1,] 18.80018 132.0074 204.1851
## [2,] 21.38107 161.3676 257.2586
Code
##
## A more decorative heatmap, with z-score scaling along columns
##
hv <- heatmap.2(x, col=cm.colors(255), scale="column",
       RowSideColors=rc, ColSideColors=cc, margin=c(5, 10),
       xlab="specification variables", ylab= "Car Models",
       main="heatmap(<Mtcars data>, ..., scale=\"column\")",
       tracecol="green", density="density")

Code
## Note that the breakpoints are now symmetric about 0

## Color the labels to match RowSideColors and ColSideColors
hv <- heatmap.2(x, col=cm.colors(255), scale="column",
       RowSideColors=rc, ColSideColors=cc, margin=c(5, 10),
       xlab="specification variables", ylab= "Car Models",
       main="heatmap(<Mtcars data>, ..., scale=\"column\")",
       tracecol="green", density="density", colRow=rc, colCol=cc,
       srtCol=45, adjCol=c(0.5,1))

Correlation Heatmaps

Code
# TODO: want example using the `add.exp' argument!

data(attitude)
round(Ca <- cor(attitude), 2)
##            rating complaints privileges learning raises critical advance
## rating       1.00       0.83       0.43     0.62   0.59     0.16    0.16
## complaints   0.83       1.00       0.56     0.60   0.67     0.19    0.22
## privileges   0.43       0.56       1.00     0.49   0.45     0.15    0.34
## learning     0.62       0.60       0.49     1.00   0.64     0.12    0.53
## raises       0.59       0.67       0.45     0.64   1.00     0.38    0.57
## critical     0.16       0.19       0.15     0.12   0.38     1.00    0.28
## advance      0.16       0.22       0.34     0.53   0.57     0.28    1.00
Code
symnum(Ca) # simple graphic
##            rt cm p l rs cr a
## rating     1                
## complaints +  1             
## privileges .  .  1          
## learning   ,  .  . 1        
## raises     .  ,  . , 1      
## critical             .  1   
## advance          . . .     1
## attr(,"legend")
## [1] 0 ' ' 0.3 '.' 0.6 ',' 0.8 '+' 0.9 '*' 0.95 'B' 1
Code
# with reorder
heatmap.2(Ca,        symm=TRUE, margin=c(6, 6), trace="none" )

Code
# without reorder
heatmap.2(Ca, Rowv=FALSE, symm=TRUE, margin=c(6, 6), trace="none" )

Code
## Place the color key below the image plot
heatmap.2(x, lmat=rbind( c(0, 3), c(2,1), c(0,4) ), lhei=c(1.5, 4, 2 ) )

Code
## Place the color key to the top right of the image plot
heatmap.2(x, lmat=rbind( c(0, 3, 4), c(2,1,0 ) ), lwid=c(1.5, 4, 2 ) )

For variable clustering, rather use distance based on cor():

Code
data(USJudgeRatings)
symnum( cU <- cor(USJudgeRatings) )
##      CO I DM DI CF DE PR F O W PH R
## CONT 1                             
## INTG    1                          
## DMNR    B 1                        
## DILG    + +  1                     
## CFMG    + +  B  1                  
## DECI    + +  B  B  1               
## PREP    + +  B  B  B  1            
## FAMI    + +  B  *  *  B  1         
## ORAL    * *  B  B  *  B  B 1       
## WRIT    * +  B  *  *  B  B B 1     
## PHYS    , ,  +  +  +  +  + + + 1   
## RTEN    * *  *  *  *  B  * B B *  1
## attr(,"legend")
## [1] 0 ' ' 0.3 '.' 0.6 ',' 0.8 '+' 0.9 '*' 0.95 'B' 1
Code
hU <- heatmap.2(cU, Rowv=FALSE, symm=TRUE, col=topo.colors(16),
            distfun=function(c) as.dist(1 - c), trace="none")

Code
## The Correlation matrix with same reordering:
hM <- format(round(cU, 2))
hM
##      CONT    INTG    DMNR    DILG    CFMG    DECI    PREP    FAMI    ORAL   
## CONT " 1.00" "-0.13" "-0.15" " 0.01" " 0.14" " 0.09" " 0.01" "-0.03" "-0.01"
## INTG "-0.13" " 1.00" " 0.96" " 0.87" " 0.81" " 0.80" " 0.88" " 0.87" " 0.91"
## DMNR "-0.15" " 0.96" " 1.00" " 0.84" " 0.81" " 0.80" " 0.86" " 0.84" " 0.91"
## DILG " 0.01" " 0.87" " 0.84" " 1.00" " 0.96" " 0.96" " 0.98" " 0.96" " 0.95"
## CFMG " 0.14" " 0.81" " 0.81" " 0.96" " 1.00" " 0.98" " 0.96" " 0.94" " 0.95"
## DECI " 0.09" " 0.80" " 0.80" " 0.96" " 0.98" " 1.00" " 0.96" " 0.94" " 0.95"
## PREP " 0.01" " 0.88" " 0.86" " 0.98" " 0.96" " 0.96" " 1.00" " 0.99" " 0.98"
## FAMI "-0.03" " 0.87" " 0.84" " 0.96" " 0.94" " 0.94" " 0.99" " 1.00" " 0.98"
## ORAL "-0.01" " 0.91" " 0.91" " 0.95" " 0.95" " 0.95" " 0.98" " 0.98" " 1.00"
## WRIT "-0.04" " 0.91" " 0.89" " 0.96" " 0.94" " 0.95" " 0.99" " 0.99" " 0.99"
## PHYS " 0.05" " 0.74" " 0.79" " 0.81" " 0.88" " 0.87" " 0.85" " 0.84" " 0.89"
## RTEN "-0.03" " 0.94" " 0.94" " 0.93" " 0.93" " 0.92" " 0.95" " 0.94" " 0.98"
##      WRIT    PHYS    RTEN   
## CONT "-0.04" " 0.05" "-0.03"
## INTG " 0.91" " 0.74" " 0.94"
## DMNR " 0.89" " 0.79" " 0.94"
## DILG " 0.96" " 0.81" " 0.93"
## CFMG " 0.94" " 0.88" " 0.93"
## DECI " 0.95" " 0.87" " 0.92"
## PREP " 0.99" " 0.85" " 0.95"
## FAMI " 0.99" " 0.84" " 0.94"
## ORAL " 0.99" " 0.89" " 0.98"
## WRIT " 1.00" " 0.86" " 0.97"
## PHYS " 0.86" " 1.00" " 0.91"
## RTEN " 0.97" " 0.91" " 1.00"

now with the correlation matrix on the plot itself

Code
heatmap.2(cU, Rowv=FALSE, symm=TRUE, col=rev(heat.colors(16)),
           distfun=function(c) as.dist(1 - c), trace="none",
           cellnote=hM)

Interactive heatmap.2 using heatmaply

Code
data(mtcars)
x  <- as.matrix(mtcars)

library(heatmaply)
# just use heatmaply instead of heatmap.2
heatmaply(x)

The default are slightly different, but it supports most of the same arguments. If you want the dendrograms to match perfectly, use this:

Code
full <- heatmap.2(x) # we use it to easily get the dendrograms
Code
## Color branches of dendrograms by cluster membership using dendextend:
heatmaply(x, 
        Rowv=color_branches(full$rowDendrogram, k = 3),
        Colv=color_branches(full$colDendrogram, k = 2))
Code
# Look at the vignette for more details:
# https://cran.r-project.org/web/packages/heatmaply/vignettes/heatmaply.html

Session Information

Code
sessionInfo()
## R version 4.4.1 (2024-06-14)
## Platform: x86_64-apple-darwin20
## Running under: macOS Big Sur 11.7.10
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRblas.0.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0
## 
## locale:
## [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: Asia/Jerusalem
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] heatmaply_1.6.0    viridis_0.6.5      viridisLite_0.4.2  plotly_4.10.4     
## [5] ggplot2_3.5.1      dendextend_1.19.0  RColorBrewer_1.1-3 gplots_3.3.0      
## 
## loaded via a namespace (and not attached):
##  [1] sass_0.4.9         utf8_1.2.4         generics_0.1.3     tidyr_1.3.1       
##  [5] bitops_1.0-8       KernSmooth_2.23-24 gtools_3.9.5       stringi_1.8.4     
##  [9] digest_0.6.37      magrittr_2.0.3     caTools_1.18.2     evaluate_1.0.5    
## [13] grid_4.4.1         iterators_1.0.14   fastmap_1.2.0      plyr_1.8.9        
## [17] foreach_1.5.2      jsonlite_1.8.8     seriation_1.5.6    gridExtra_2.3     
## [21] httr_1.4.7         purrr_1.0.2        fansi_1.0.6        crosstalk_1.2.1   
## [25] scales_1.3.0       codetools_0.2-20   lazyeval_0.2.2     jquerylib_0.1.4   
## [29] registry_0.5-1     cli_3.6.5          rlang_1.1.6        munsell_0.5.1     
## [33] withr_3.0.2        cachem_1.1.0       yaml_2.3.10        tools_4.4.1       
## [37] reshape2_1.4.4     dplyr_1.1.4        colorspace_2.1-1   webshot_0.5.5     
## [41] assertthat_0.2.1   ca_0.71.1          TSP_1.2-4          vctrs_0.6.5       
## [45] R6_2.5.1           lifecycle_1.0.4    stringr_1.5.1      htmlwidgets_1.6.4 
## [49] pkgconfig_2.0.3    pillar_1.9.0       bslib_0.8.0        gtable_0.3.5      
## [53] Rcpp_1.0.13        glue_1.7.0         data.table_1.16.0  xfun_0.47         
## [57] tibble_3.2.1       tidyselect_1.2.1   highr_0.11         rstudioapi_0.16.0 
## [61] knitr_1.48         farver_2.1.2       htmltools_0.5.8.1  labeling_0.4.3    
## [65] rmarkdown_2.28     compiler_4.4.1

References