ComplexHeatmap has a
densityHeatmap() to visualize a list of density
distributions, such as in the following example:
library(ComplexHeatmap) set.seed(123) mat = matrix(rnorm(500), ncol = 10) colnames(mat) = letters[1:10] densityHeatmap(mat)
In basic R graphics, since distributions can also be visualized by histograms, from ComplexHeatmap
version 2.7.9, I added a new function
frequencyHeatmap() which is like a histogram-version
of density heatmap. The usage is similar as
In the previous example, the frequency matrix is visualized as a heatmap. Note you can use different statistic
Well, the frequency heatmap claims to be a histogram-version of density heatmap, but it does not
look like histograms at all. Maybe a 3D heatmap with 3D bars is more proper.
This can be done by setting argument
use_3d = TRUE in
frequencyHeatmap(mat, use_3d = TRUE)
This looks nicer! In the next section I will explain how the 3D Heatmap is implemented.
Implementation of 3D heatmap
First, we need to draw 3D bars. This can be done by the new function
bar3D(). The usage is as follows:
bar3D(x = 0.5, y = 0.5, w = 0.2, h = 0.2, l = unit(1, "cm"), theta = 60)
The arguments are:
x: x coordinate of the center point in the bottom face. Value should be a
unitobject. If it is numeric, the default unit is
y: y coordinate of the center point in the bottom face.
w: Width of the bar (in the x direction). See the following figure.
h: Height of the bar (in the y direction). See the following figure.
l: Length of the bar (in the z direction). See the following figure.
theta: Angle for the projection. See the following figure. Note
thetacan only take value between 0 and 90.
fill argument sets the color. To enhance the visual effect of 3D visualization, the three faces actually have slighly different brightness.
bar3D(x = seq(0.2, 0.8, length = 4), y = 0.5, w = unit(5, "mm"), h = unit(5, "mm"), l = unit(1, "cm"), fill = c("red", "green", "blue", "purple"))
theta argument sets the angle of the projection. Since I am a right-hand
person, my left hand is more free so that it feels like it’s giving invisible
force to push the bars to the right, thus,
theta can only take value between
0 and 90. :)
bar3D(x = seq(0.2, 0.8, length = 4), y = 0.5, w = unit(5, "mm"), h = unit(5, "mm"), l = unit(1, "cm"), theta = c(20, 40, 60, 80))
To add bars to heatmap cells, we can simply implement
layer_fun where we add each bar to each cell. Here we have a new function
Heatmap3D() which simplifies this.
Heatmap3D() accepts almost all the arguments
Heatmap() and the only difference is each cell has a 3D bar of which the
height corresponds to its value.
Heatmap3D() only allows non-negative matrix as input. Also default values
are changes for some arguments, such as row names are put on the left side of heatmap,
and clusterings are still applied but the dendrograms are not drawn by default.
Following is a demonstration of the usage of
set.seed(7) mat = matrix(runif(100), 10) rownames(mat) = LETTERS[1:10] colnames(mat) = letters[1:10] Heatmap3D(mat, name = "mat", column_title = "This is a 3D heatmap")
In the previous example, if bars close to the top of the heatmap or to the right of the heatmap have too large length, they will overlap to the heatmap title or the legend, in this case, we need to manually adjust the space between, e.g., title and the heatmap body.
In ComplexHeatmap, there are several global options that control the
spaces between heatmap components. To solve the problem in the previous example,
we can manually set a proper value for
ht_opt$HEATMAP_LEGEND_PADDING = unit(5, "mm") ht_opt$TITLE_PADDING = unit(c(9, 2), "mm") # bottom and top padding Heatmap3D(mat, name = "mat", column_title = "This is a 3D heatmap")
Reset the global options by
ht_opt(RESET = TRUE):
ht_opt(RESET = TRUE)
Next I demonstrate another example which is applied to the well-known measles vaccine dataset. First I show the “normal 2D heatmap”. Code for generating the heatmap can be found here.
To change it to 3D visualization, simply replace
Heatmap3D() and most of the original arguments for
Heatmap() can still be
put there. For simplicity, in the 3D heatmap, I removed the top annotation and
the right annotation.
mat = readRDS(system.file("extdata", "measles.rds", package = "ComplexHeatmap")) year_text = as.numeric(colnames(mat)) year_text[year_text %% 10 != 0] = "" ha_column = HeatmapAnnotation( year = anno_text(year_text, rot = 0, location = unit(1, "npc"), just = "top") ) col_fun = circlize::colorRamp2(c(0, 800, 1000, 127000), c("white", "cornflowerblue", "yellow", "red")) ht_opt$TITLE_PADDING = unit(c(15, 2), "mm") Heatmap3D(mat, name = "cases", col = col_fun, cluster_columns = FALSE, show_row_dend = FALSE, show_column_names = FALSE, row_names_side = "left", row_names_gp = gpar(fontsize = 8), column_title = 'Measles cases in US states 1930-2001\nVaccine introduced 1961', bottom_annotation = ha_column, heatmap_legend_param = list(at = c(0, 5e4, 1e5, 1.5e5), labels = c("0", "50k", "100k", "150k")), # new arguments for Heatmap3D() bar_rel_width = 1, bar_rel_height = 1, bar_max_length = unit(2, "cm") )
By the way, it is also possible to turn the static 3D heatmap to an interactive Shiny application by package InteractiveComplexHeatmap. See the following figure:
Heatmap3D() can do a lot of things that are the same as
Heatmap(), such as adding
annotations, splitting the heatmap or concatenating more heatmaps by
%v%. But since 3D
visualization is in general not a good idea and it actually won’t give you
more information than what you can get from 2D visualization, thus, if you
want to use
Heatmap3D(), you better keep it as simple as possible. Also, please
apply it to small matrices, it will take long time to generate for large matrices.