Package InteractiveComplexHeatmap is available on Bioconductor, you can install it by:
if (!requireNamespace("BiocManager", quietly=TRUE))
install.packages("BiocManager")
BiocManager::install("InteractiveComplexHeatmap")
If you want the latest version, install it directly from GitHub:
A printer-friendly version of the documentation is available at bioRxiv.
The InteractiveComplexHeatmap package is very
straightforward to use. For any heatmap (or list of heatmaps as a
Heatmap
or HeatmapList
object) produced from
ComplexHeatmap package, you just use the function
htShiny()
to export it as a Shiny app.
You can copy and paste the following code:
library(ComplexHeatmap)
library(InteractiveComplexHeatmap)
m = matrix(rnorm(100*100), nrow = 100)
ht = Heatmap(m)
ht = draw(ht) # not necessary, but recommended
htShiny(ht)
A link will be automatically opend in your web browser (or a pop-up window in RStudio IDE).
To use htShiny()
, the heatmap object is recommended to
be updated with the function draw()
. If it has not been
updated, it will be applied inside htShiny()
automatically.
Updating by draw()
speeds up loading the Shiny application
because draw()
applies clusterings which is normally the
most time-consuming step in heatmap generation. After
draw()
is executed, clustering results are saved in the
returned heatmap object so that repeatedly drawing heatmap can directly
use the saved clustering results. If the heatmap includes randomness,
such as k-means clustering by setting row_km
or
column_km
argument or using random colors for annotations,
it is necessary to execute draw()
before sending the
heatmap object to htShiny()
to get rid of obtaining
different heatmaps when executing htShiny()
multiple
times.
Following screenshot demonstrates the Shiny app on rather complex
heatmaps. The data is from here
and the app can be generated by running
htShinyExample(4.2)
.
In this Shiny app, users can click on the orignal heatmap or select an area from it. The information of the cell or the area selected by users can be found in the text below the heatmaps. If an area is selected, a sub-heatmap is drawn on the right side of the app. Both heatmaps can be resized by dragging from the bottom right.
There are several tools under both heatmaps. For the original heatmap, there are following tools:
It allows to search the heatmap labels to obtain a subset of rows and columns. Once rows or columns are found in the heatmaps, the sub-heatmaps will be drawn in the right panel.
Border and backgroud of the brush can be configured here:
The main heatmap can be saved into a file with one of the three
formats (png
, pdf
and svg
).
The size of the original heatmap can be controlled by dragging the box, however, it can be precisely controlled by entering the width and height, as shown in the following figure:
For the sub-heatmap, there are following tools:
There are three sections of controls. 1. Basic controls such as whether to show row or column names, 2. Brushing on the original heatmap might not precisely capture rows or columns users expected. You can manually remove a certain number of rows or columns from the four dimensions of the selected sub-heatmap. 3. Selected sub-heatmap can be further converted into a second interactive heatmap application.
The values in the sub-heatmaps can be viewed and exported as a text table:
Similar as in the main heatmap.
Similar as in the main heatmap.
There are other vignettes focusing on more specific topics:
ComplexHeatmap is broadly used in many other scripts
and packages where they do not directly return the
Heatmap
/HeatmapList
object. This is of no
problem to make these heatmaps interactive because the last generated
heatmap object is automatically saved and calling htShiny()
without the heatmap object will automatically use the last one (by
internally using the function
ComplexHeatmap:::get_last_ht()
). I demonstrate this
functionality with the
cola pacakge.
cola package heavily uses
ComplexHeatmap to implement various customized heatmaps
to visualize consensus clustering results as well as downstream
analysis. As an example, the function get_signatures()
extracts signatures that are significantly different between the
predicted subgroups. get_signatures()
makes one heatmap and
returns a data frame of the signatures as well as various statistics for
the test. When get_signatures()
draws the heatmap, the
heatmap object is saved internally, then directly calling
htShiny()
without the heatmap object will convert the
static signature heatmap into an interactive one:
# the following code is runable
library(cola) # cola is from Bioconductor
data(golub_cola)
get_signatures(golub_cola["ATC:skmeans"], k = 2) # this makes the heatmap
htShiny()
Note the functionality of automatically saving the last heatmap is
only turned on when InteractiveComplexHeatmap packags
is loaded, which means, library(InteractiveComplexHeatmap)
should be called before making the heatmap, or you need to explicitly
set ComplexHeatmap::ht_opt(save_last = TRUE)
.
Examples are in htShinyExample(1.6)
and
htShinyExample(1.7)
.
Nevertheless, if possible, I still suggest to explictly put the
heatmap object in htShiny()
which makes it clear which
heatmap is becoming interactive.
In the right panel there is the sub-heatmap that is selected from the left panel. When the original heatmap is very huge, even a tiny selection rectangle still generates a dense sub-heatmap where single cells are very hard to identify and read. In this case, the sub-heatmap can be continually exported as another independent interactive heatmap widget which is in a new layer above current one, just by clicking the button “Interactivate sub-heatmap” under the sub-heatmap. This process can be recursively applied untill you are satisfied with the details you can see in the sub-heatmap. An example is as follows:
The output which contains information of the clicked cell or the
sub-heatmap by default is placed below the heatmaps. Argument
output_ui_float
can be set to TRUE
so that the
output is floating to the positions of mouse.
There are two examples: htShinyExample(9.1)
and
htShinyExample(9.2)
. The demonstration is as follows:
As will be explained in the vignette “Functions for Shiny app development”, the output can be self-defined to put customized information. The self-defined output can also be floated:
There are three main components in the interactive heatmap UI,
i.e., the orignal heatmap, the sub-heatmap and an output that
shows information of the clicked cell or the selected sub-heatmap. The
layout of the three components are controlled via layout
argument, see the
vignette “Functions for Shiny app development” for detailed
explanation of the layout
argument.
The argument response
can be set as one of
"click"
, "hover"
, "dblclick"
,
"brush"
and "brush-output"
to only respond to
one event on heatmap. E.g. if response
is set to
"click"
, there will be no response for the “brush event” in
the interactive heatmap, also the sub-heatmap component is removed from
the app.
Brushing on heatmap by default triggers two reponses: update in the
sub-heatmap and update in the output. If response
is set to
"brush-output"
, brushing will only trigger changes in the
output and there will be no sub-heatmap component in the app.
The following figure demonstrates only responding to
"click"
or "brush"
where the output floats at
mouse positions. Runnable examples are in
htShinyExample(1.9)
and
htShinyExample(9.3)
.
By default all the three UI components are includedin the app. In
htShiny()
, the argument compact
can be set to
TRUE
so that the sub-heatmap component is removed and the
output floats at mouse positions:
htShiny()
htShiny()
is just a simple wrapper on the other two
basic functions InteractiveComplexHeatmapOutput()
and
makeInteractiveComplexHeatmap()
. Users can go to the
vignette “Functions for Shiny app
development” for more customized controls.
The following code demostrates two horizontally concatenated heatmaps. Please visit https://jokergoo.shinyapps.io/interactive_complexheatmap/ for a live demo.
set.seed(123)
mat1 = matrix(rnorm(100), 10)
rownames(mat1) = colnames(mat1) = paste0("a", 1:10)
mat2 = matrix(sample(letters[1:10], 100, replace = TRUE), 10)
rownames(mat2) = colnames(mat2) = paste0("b", 1:10)
ht_list = Heatmap(mat1, name = "mat_a", row_km = 2, column_km = 2) +
Heatmap(mat2, name = "mat_b")
htShiny(ht_list)
The following code demostrates two vertically concatenated heatmaps. Check https://jokergoo.shinyapps.io/interactive_complexheatmap_vertical/ for a live demo.
The functionality of the interactivity is general. It can be applied to any heatmap as long as it is generated from the ComplexHeatmap package. Thus, it can turn many other plots into an interactive app.
ComplexHeatmap::densityHeatmap()
returns a
Heatmap
object, so it can be exported as a Shiny app. Check
https://jokergoo.shinyapps.io/interactive_densityheatmap/
for a live demo. The example can also be run locally by executing
htShinyExample(2.1)
. The usage of
htShinyExample()
will be introduced later and in the
webpage opened by htShinyExample()
there is also the source
code for generating this Shiny app.
ComplexHeatmap::oncoPrint()
returns a
Heatmap
object, thus, the oncoPrint can be interactive.
Example code is as follows. Check https://jokergoo.shinyapps.io/interactive_oncoprint/ for
a live demo. The example can be run locally by executing
htShinyExample(2.2)
.
ComplexHeatmap::UpSet()
returns a Heatmap
object, thus, the UpSet plot generated by
ComplexHeatmap can be interactive. It might be useful
when you visualize many sets at the same time. Example code is as
follows. Check https://jokergooo.shinyapps.io/interactive_upset/ for a
live demo. The example can be run locally by executing
htShinyExample(2.3)
.
EnrichedHeatmap
inherits ComplexHeatmap and it outputs
Heatmap
objects, thus, an “enriched heatmap” can be
exported as a Shiny app as well. Check https://jokergoo.shinyapps.io/interactive_enrichedheatmap/
for a live demo. The example can be run locally by executing
htShinyExample(3.1)
.
Since ComplexHeatmap can seamlessly
integrate pheatmap, this means your pheatmap can be
interactive! Check https://jokergooo.shinyapps.io/interactive_pheatmap/.
The example can be run locally by executing
htShinyExample(2.4)
.
ComplexHeatmap::pheatmap()
and
pheatmap::pheatmap()
have the same set of arguments and
generate almost the same heatmaps, but be careful when you directly use
pheatmap()
without the namespace prefix (the package name),
you need to make sure it is from ComplexHeatmap if you
want to use the interactive functionality.
To facilitate the users who are still using heatmap()
and heatmap.2()
functions, to make the output of these two
functions can be exported as interactive Shiny apps as well, from
ComplexHeatmap version 2.7.2, two similar translation
functions ComplexHeatmap:::heatmap()
and
ComplexHeatmap:::heatmap.2()
which use the same set of
arguments as the original functions and generate almost identical
heatmaps. Then an interactive heatmap()
will look like:
As you see, heatmap()
function is not exported from
ComplexHeatmap (so that it will not conflict with
stats package) and it should be explicitly specifid
with :::
.
An live example is at https://jokergooo.shinyapps.io/interactive_heatmap/. The
example can be run locally by executing
htShinyExample(2.5)
.
Similarly, an interactive heatmap.2()
looks like:
Similarlly, heatmap.2()
is not exported from
ComplexHeatmap (so that it will not conflict with
glots package) and it should be specified with
:::
.
An live example is at https://jokergooo.shinyapps.io/interactive_heatmap_2/.
The example can be run locally by executing
htShinyExample(2.6)
.
The
tidyHeatmap package basically wraps
ComplexHeatmap and provides “a tidy way” for generating
heatmaps. Since the final heatmap is actually generated by
ComplexHeatmap, it can be directly exported as an
interactive app. You can directly apply htShiny()
on the
object generated from tidyHeatmap. Check https://jokergooo.shinyapps.io/interactive_tidyheatmap/.
The example can be run locally by executing
htShinyExample(2.7)
.
library(tidyverse)
library(tidyHeatmap)
mtcars_tidy <-
mtcars %>%
as_tibble(rownames="Car name") %>%
mutate_at(vars(-`Car name`, -hp, -vs), scale) %>%
pivot_longer(cols = -c(`Car name`, hp, vs), names_to = "Property", values_to = "Value")
mtcars_heatmap <-
mtcars_tidy %>%
heatmap(`Car name`, Property, Value ) %>%
add_tile(hp)
htShiny(mtcars_heatmap)
As explained before, for all these functions and packages that
generate customized heatmaps, if the heatmaps are already generated in
the interactive graphics device, the heatmap objects can be ommited when
calling htShiny()
. E.g.:
There are many other examples shipped with the
InteractiveComplexHeatmap package. The list of examples
can be obtained by executing htShinyExample()
with no
argument.
## There are following examples. Individual example can be run by e.g. htShinyExample(1.1).
##
## ──────── 1. Simple examples ─────────────────────────────────────────────────────────
## 1.1 A single heatmap with minimal arguments.
## 1.2 A single heatmap from a character matrix.
## 1.3 A single heatmap with annotations on both rows and columns.
## 1.4 A single heatmap where rows and columns are split.
## 1.5 A list of two heatmaps.
## 1.6 A list of two vertically concatenated heatmaps
## 1.7 Use last generated heatmap, an example from cola package.
## 1.8 Use last generated heatmap, an app with three interactive heatmaps
## 1.9 Demonstrate hover, click and dblclick actions to select cells.
## 1.10 Only response to one of click/hover/dblclick/hover events. Please use
## htShinyExample('1.10') to get this example (quote the index, or else
## htShinyExample(1.10) will be treated as the same as htShinyExample(1.1)).
## 1.11 Interactive heatmap under compact mode.
##
## ──────── 2. On other plots and packages ─────────────────────────────────────────────
## 2.1 A density heatmap.
## 2.2 An oncoPrint.
## 2.3 A UpSet plot.
## 2.4 An interactive heatmap from pheatmap().
## 2.5 An interactive heatmap from heatmap().
## 2.6 An interactive heatmap from heatmap.2().
## 2.7 A heatmap produced from tidyHeatmap package.
## 2.8 Genome-scale heatmap.
## 2.9 A package-dependency heatmap. You can try to control "Fill figure region"
## and "Remove empty rows and columns" in the tools under the sub-heatmap.
##
## ──────── 3. Enriched heatmaps ───────────────────────────────────────────────────────
## 3.1 A single enriched heatmap.
## 3.2 A list of enriched heatmaps.
## 3.3 An enriched heatmap with discrete signals.
##
## ──────── 4. On public datasets ──────────────────────────────────────────────────────
## 4.1 An example from Lewis et al 2019.
## 4.2 Visualize cell heterogeneity from single cell RNASeq.
## 4.3 Correlations between methylation, expression and other genomic features.
##
## ──────── 5. Shiny app development ───────────────────────────────────────────────────
## 5.1 A single Shiny app with two interactive heatmap widgets.
## 5.2 Self-define the output. The selected sub-matrix is shown as a text table.
## 5.3 Self-define the output. Additional annotations for the selected genes are
## shown.
## 5.4 Visualize Gene Ontology similarities. A list of selected GO IDs as well as
## their descriptions are shown in the output.
## 5.5 Interactive correlation heatmap. Clicking on the cell generates a
## scatterplot of the two corresponding variables.
## 5.6 A heatmap on Jaccard coefficients for a list of genomic regions. Clicking
## on the cell generates a Hilbert curve of how the two sets of genomic
## regions overlap.
## 5.7 Implement interactivity from scratch. Instead of generating the whole
## interactive heatmap widget, it only returns the information of rows and
## columns that user have selected on heatmap and users can use this
## information to build their own interactive heatmap widgets.
## 5.8 Implement interactivity from scratch. A visualization of 2D density
## distribution. Brushing on heatmap triggers a new 2D density estimation
## only on the subset of data.
##
## ──────── 6. Dynamically generate heatmap widget in Shiny app ────────────────────────
## 6.1 The matrix with different dimensions is dynamically generated.
## 6.2 Reorder by a column that is specified by user.
## 6.3 Dynamically generate the widget with InteractiveComplexHeatmapModal(). The
## modal is triggered by an action button.
## 6.4 Dynamically select interactive heatmaps. The modal is triggered by radio
## buttons.
## 6.5 Dynamically generate the widget. A customized Javascript code is inserted
## after the UI to change the default behavior of the action button.
## 6.6 The widget is generated by InteractiveComplexHeatmapWidget() where the UI
## is directly put in the place defined by htmlOutput().
## 6.7 The widget is generated by InteractiveComplexHeatmapWidget() and a
## customized Javascript code is inserted after the UI.
##
## ──────── 7. Interactive R markdown document ─────────────────────────────────────────
## 7.1 Integrate in an interactive R Markdown document.
## 7.2 Integrate in an interactive R Markdown document where the heatmap widgets
## are dynamically generated.
##
## ──────── 8. Interactivate heatmaps indirectly generated by heatmap()/heatmap.2()/pheatmap()
## 8.1 Indirect use of pheatmap().
## 8.2 Indirect use of heatmap.2().
## 8.3 Two interactive heatmap widgets from indirect use of pheatmap().
##
## ──────── 9. Float output UI along with mouse positions ──────────────────────────────
## 9.1 A simple example that demonstrates output UI floating with the three
## actions: hover, click and dblclick.
## 9.2 Floating self-defined outputs.
## 9.3 Floating output only from one event on heatmap, i.e.
## hover/click/dblclick/brush-output.
##
## ──────── 10. Work with shinydashboard ────────────────────────────────────────────────
## 10.1 Separate the three UI components into three boxes.
## 10.2 The three UI components are draggable.
## 10.3 A Shiny dashboard with two tabs.
## 10.4 Only contain the original heatmap where output floats.
## 10.5 A complex dashboard that visualizes a DESeq2 results.
You can select one specific example by sending the corresponding
index to htShinyExample()
, e.g. to run the example “1.4 A
single heatmap where rows and columns are split.”, simply run:
Then the interactive heatmaps as well as the source code for generating the app will be available in the webpage.
Have fun!
## R version 4.4.1 (2024-06-14)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.1 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_US.UTF-8 LC_COLLATE=C
## [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
##
## time zone: Etc/UTC
## tzcode source: system (glibc)
##
## attached base packages:
## [1] grid stats graphics grDevices utils datasets methods
## [8] base
##
## other attached packages:
## [1] InteractiveComplexHeatmap_1.15.0 ComplexHeatmap_2.23.0
## [3] knitr_1.48 rmarkdown_2.28
##
## loaded via a namespace (and not attached):
## [1] sass_0.4.9 xml2_1.3.6 shape_1.4.6.1
## [4] stringi_1.8.4 digest_0.6.37 magrittr_2.0.3
## [7] evaluate_1.0.1 RColorBrewer_1.1-3 iterators_1.0.14
## [10] circlize_0.4.16 fastmap_1.2.0 foreach_1.5.2
## [13] doParallel_1.0.17 jsonlite_1.8.9 GlobalOptions_0.1.2
## [16] promises_1.3.0 viridisLite_0.4.2 scales_1.3.0
## [19] codetools_0.2-20 jquerylib_0.1.4 cli_3.6.3
## [22] shiny_1.9.1 rlang_1.1.4 crayon_1.5.3
## [25] munsell_0.5.1 cachem_1.1.0 yaml_2.3.10
## [28] tools_4.4.1 parallel_4.4.1 colorspace_2.1-1
## [31] httpuv_1.6.15 kableExtra_1.4.0 GetoptLong_1.0.5
## [34] BiocGenerics_0.53.0 buildtools_1.0.0 R6_2.5.1
## [37] mime_0.12 png_0.1-8 matrixStats_1.4.1
## [40] stats4_4.4.1 lifecycle_1.0.4 stringr_1.5.1
## [43] S4Vectors_0.43.2 IRanges_2.39.2 clue_0.3-65
## [46] clisymbols_1.2.0 fontawesome_0.5.2 cluster_2.1.6
## [49] bslib_0.8.0 later_1.3.2 glue_1.8.0
## [52] Rcpp_1.0.13 systemfonts_1.1.0 xfun_0.48
## [55] rstudioapi_0.17.1 sys_3.4.3 xtable_1.8-4
## [58] rjson_0.2.23 htmltools_0.5.8.1 svglite_2.1.3
## [61] maketools_1.3.1 compiler_4.4.1