5 Legends
The heatmaps and simple annotations automatically generate legends which are
put one the right side of the heatmap. By default there is no legend for
complex annotations, but they can be constructed and added manually (Section
5.5). All legends are internally constructed by
Legend()
constructor. In later sections, we first introduce the settings for
continuous legends and discrete legends, then we will discuss how to configure
the legends associated with the heatmaps and annotations, and how to add new
legends to the plot.
All the legends (no matter a single legend or a pack of legends) all belong to
the Legends
class. The class only has one slot grob
which is the real
grid::grob
object or the grid::gTree
object that records how to draw the
graphics. The wrapping of the Legends
class and the methods designed for the
class make legends as single objects and can be drawn like points with
specifying the positions on the viewport.
The legends for heatmaps and annotations can be controlled by
heatmap_legend_param
argument in Heatmap()
, or annotation_legend_param
argument in HeatmapAnnotation()
. Most of the parameters in Legend()
function can be directly set in the two arguments with the same parameter
name. The details of setting heatmap legends and annotation legends
parameters are introduced in Section 5.4.
5.1 Continuous legends
Since most of heatmaps contain continuous values, we first introduce the settings for the continuous legend.
Continuous legend needs a color mapping function which should be generated by
circlize::colorRamp2()
. In the heatmap legends and annotation legends that
are automatically generated, the color mapping functions are passed by the
col
argument from Heatmap()
or HeatmapAnnotation()
function, while if
you construct a self-defined legend, you need to provide the color mapping
function.
The break values provided in the color mapping function (e.g. c(0, 0.5, 1)
in following example) will not exactly be the same as the break values in the
legends). The finally break values presented in the legend are internally
adjusted to make the numbers of labels close to 5 or 6.
First we show the default style of a vertical continuous legend:
library(circlize)
col_fun = colorRamp2(c(0, 0.5, 1), c("blue", "white", "red"))
lgd = Legend(col_fun = col_fun, title = "foo")
lgd
is a Legends
class object. The size of the legend can be obtained by
ComplexHeatmap:::width()
and ComplexHeatmap:::height()
function.
ComplexHeatmap:::width(lgd)
## [1] 9.90361111111111mm
ComplexHeatmap:::height(lgd)
## [1] 30.2744052165491mm
The legend is actually a packed graphic object composed of rectangles, lines
and texts. It can be added to the plot by draw()
function. In
ComplexHeatmap pacakge, you don’t need to use draw()
directly on legend
objects, but it might be useful if you use the legend objects in other places.
pushViewport(viewport(width = 0.9, height = 0.9))
grid.rect() # border
draw(lgd, x = unit(1, "cm"), y = unit(1, "cm"), just = c("left", "bottom"))
draw(lgd, x = unit(0.5, "npc"), y = unit(0.5, "npc"))
draw(lgd, x = unit(1, "npc"), y = unit(1, "npc"), just = c("right", "top"))
popViewport()
If you only want to configure the legends generated by heatmaps or
annotations, you don’t need to construct the Legends
object by your own.
The parameters introduced later can be directly used to customize the
legends by heatmap_legend_param
argument in Heatmap()
and
annotation_legend_param
argument in HeatmapAnnotation()
(introduced in
Section
5.4). It is still nice to see how these parameters change the
styles of the legend in following examples. Following is a simple example
showing how to configure legends in the heatmap and heatmap annotation.
Heatmap(matrix(rnorm(100), 10),
heatmap_legend_param = list(
title = "rnorm", at = c(-2, 0, 2),
labels = c("neg_two", "zero", "pos_two")
),
top_annotation = HeatmapAnnotation(
foo = 1:10,
annotation_legend_param = list(foo = list(title = "foo_top_anno"))
))
In following examples, we only show how to construct the legend object, while
not show the code which draws the legends. Only remember you can use draw()
function on the Legends
object to draw the single legend on the plot.
For continuous legend, you can manually adjust the break values in the legend
by setting at
. Note the height is automatically adjusted.
lgd = Legend(col_fun = col_fun, title = "foo", at = c(0, 0.25, 0.5, 0.75, 1))
The labels corresponding to the break values are set by labels
.
lgd = Legend(col_fun = col_fun, title = "foo", at = c(0, 0.5, 1),
labels = c("low", "median", "high"))
The height of the vertical continous legend is set by legend_height
.
legend_height
can only be set for the veritcal continous legend and the
value is the height of the legend body (excluding the legend title).
lgd = Legend(col_fun = col_fun, title = "foo", legend_height = unit(6, "cm"))
If it is a vertical legend, grid_width
controls the widths of the legend
body. grid_width
is originally designed for the discrete legends where the
each level in the legend is a grid, but here we use the same name for the
parameter that controls the width of the legend.
lgd = Legend(col_fun = col_fun, title = "foo", grid_width = unit(1, "cm"))
The graphic parameters for the labels are controlled by labels_gp
.
lgd = Legend(col_fun = col_fun, title = "foo", labels_gp = gpar(col = "red", font = 3))
The border of the legend as well as the ticks for the break values are
controlled by border
. The value of border
can be logical or a string of
color.
lgd = Legend(col_fun = col_fun, title = "foo", border = "red")
title_position
controls the position of titles. For vertical legends, the
value should be one of topleft
, topcenter
, lefttop-rot
and
leftcenter-rot
. Following two plots show the effect of lefttop-rot
title
and leftcenter-rot
title.
lgd = Legend(col_fun = col_fun, title = "foooooooo", title_position = "lefttop-rot",
legend_height = unit(4, "cm"))
lgd = Legend(col_fun = col_fun, title = "foooooooo", title_position = "leftcenter-rot",
legend_height = unit(4, "cm"))
Legend titles and labels can be set as mathematical formulas.
lgd = Legend(col_fun = col_fun, title = expression(hat(beta) == (X^t * X)^{-1} * X^t * y),
at = c(0, 0.25, 0.5, 0.75, 1), labels = expression(alpha, beta, gamma, delta, epsilon))
More complicated texts can be added by using the gridtext package (Section 10.3.5).
lgd = Legend(col_fun = col_fun,
title = gt_render("<span style='color:orange'>**Legend title**</span>"),
title_gp = gpar(box_fill = "grey"),
at = c(-3, 0, 3),
labels = gt_render(c("<span style='color:blue'>*negative*</span> three", "zero",
"<span style='color:red'>*positive*</span> three"))
)
Settings for horizontal continuous legends are almost the same as vertical
legends, except that now legend_width
controls the width of the legend, and
the title position can only be one of topcenter
, topleft
, lefttop
and
leftcenter
.
The default style for horizontal legend:
lgd = Legend(col_fun = col_fun, title = "foo", direction = "horizontal")
Manually set at
:
lgd = Legend(col_fun = col_fun, title = "foo", at = c(0, 0.25, 0.5, 0.75, 1),
direction = "horizontal")
Manually set labels
:
lgd = Legend(col_fun = col_fun, title = "foo", at = c(0, 0.5, 1),
labels = c("low", "median", "high"), direction = "horizontal")
Set legend_width
:
lgd = Legend(col_fun = col_fun, title = "foo", legend_width = unit(6, "cm"),
direction = "horizontal")
Set graphic parameters for labels:
lgd = Legend(col_fun = col_fun, title = "foo", labels_gp = gpar(col = "red", font = 3),
direction = "horizontal")
Set rotations of labels.
lgd = Legend(col_fun = col_fun, title = "foo", labels_rot = 45,
direction = "horizontal")
Title can be set as topleft
, topcenter
or lefttop
and leftcenter
.
lgd = Legend(col_fun = col_fun, title = "foooooooo", direction = "horizontal",
title_position = "topcenter")
lgd = Legend(col_fun = col_fun, title = "foooooooo", direction = "horizontal",
title_position = "lefttop")
In examples we showed above, the intervals between every two break values are
equal. Actually at
can also be set as break values with uneuqal intervals.
In this scenario, the ticks on the legend are still at the original places
while the corresponding texts are shifted to get rid of overlapping. Then,
there are lines connecting the ticks and the labels.
lgd = Legend(col_fun = col_fun, title = "foo", at = c(0, 0.1, 0.15, 0.5, 0.9, 0.95, 1))
If the labels do not need to be adjusted, they are still at the original places.
lgd = Legend(col_fun = col_fun, title = "foo", at = c(0, 0.3, 1),
legend_height = unit(4, "cm"))
It is similar for the horizontal legends:
lgd = Legend(col_fun = col_fun, title = "foo", at = c(0, 0.1, 0.15, 0.5, 0.9, 0.95, 1),
direction = "horizontal")
Set rotations of labels to 90 degree.
lgd = Legend(col_fun = col_fun, title = "foo", at = c(0, 0.1, 0.15, 0.5, 0.9, 0.95, 1),
direction = "horizontal", title_position = "lefttop", labels_rot = 90)
When the position of title is set to lefttop
, the area below the title will
also be taken into account when calculating the adjusted positions of labels.
lgd = Legend(col_fun = col_fun, title = "foo", at = c(0, 0.1, 0.5, 0.75, 1),
labels = c("mininal", "q10", "median", "q75", "maximal"),
direction = "horizontal", title_position = "lefttop")
If at
is set in the decreasing order, the legend is reversed, i.e. the smallest value
is on the top of the legend.
lgd = Legend(col_fun = col_fun, title = "foo", at = c(1, 0.8, 0.6, 0.4, 0.2, 0))
Most continuous legends have legend breaks with equal distance, which I mean, e.g. the distance between the first and the second breaks are the same as the distance between the second and the third breaks. However, there are still special cases where users want to set legend breaks with unequal distances.
In the following example, the color mapping function col_fun_prop
visualizes
proportion values with breaks in c(0, 0.05, 0.1, 0.5, 1)
. The legend breaks
with unequal distance might reflect the different importance of the values in c(0, 1)
.
For example, maybe we want to see more details in the interval c(0, 0.1)
.
Following is the default style of the legend where the breaks are selected from 0 to 1 with equal distance.
col_fun_prop = colorRamp2(c(0, 0.05, 0.1, 0.5, 1),
c("green", "white", "red", "black", "blue"))
lgd = Legend(col_fun = col_fun_prop, title = "Prop")
You cann’t see the details in the interval c(0, 0.1)
, right? This also reminds us that
the breaks set in colorRamp2()
only defines the color mapping while not determine
the breaks in the legend.
If we manually select the break values, the color bar keeps the same. The labels are shifted and lines connect them to the original positions. In this case, the distance in the color bar is still proportional to the real difference in the break values, i.e., the distance between 0.5 and 1 is five times longer than 0 and 0.1.
col_fun_prop = colorRamp2(c(0, 0.05, 0.1, 0.5, 1),
c("green", "white", "red", "black", "blue"))
lgd = Legend(col_fun = col_fun_prop, title = "Prop",
at = c(0, 0.05, 0.1, 0.5, 1))
From version 2.7.1, Legend()
function has a new argument break_dist
that
controls the distance between two neighbouring break values in the legend.
It might be confusing, but from here, when I mention “break distance,” it
always means the visual distance in the legend.
The value of break_dist
should have length either one which means all break
values have equal distance in the legend, or length(at) - 1
.
lgd = Legend(col_fun = col_fun_prop, title = "Prop", break_dist = 1)
And in the following example, the top two break intervals are three times longer than the bottom two intervals.
lgd = Legend(col_fun = col_fun_prop, title = "Prop", break_dist = c(1, 1, 3, 3))
If we increase the legend height by legend_height
argument, there will be enough space
for the labels and their positions are not adjusted any more.
lgd = Legend(col_fun = col_fun_prop, title = "Prop", break_dist = c(1, 1, 3, 3),
legend_height = unit(4, "cm"))
Imaging following user case, we want to use one color scheme for the values in
c(0, 0.1)
and a second color schema for the values in c(0.1, 1)
, maybe for
the reason that we want to emphasize the two intervals are very different. The
color mapping can be defined as:
col_fun2 = colorRamp2(c(0, 0.1, 0.1+1e-6, 1), c("white", "red", "yellow", "blue"))
So here I just added a tiny shift (1e-6
) to 0.1 and set it as the lower bound for the
second color scheme. The legend looks like:
lgd = Legend(col_fun = col_fun2, title = "Prop", at = c(0, 0.05, 0.1, 0.5, 1),
break_dist = c(1, 1, 3, 3), legend_height = unit(4, "cm"))
Now you can see the colors are not changed smoothly from 0 to 1 and there are two disticnt color schemes.
5.2 Discrete legends
Discrete legends are used for discrete color mappings. The continuous color mapping can also be degenerated as discrete color mapping by only providing the colors and the break values.
You can either specify at
or labels
, but most probably you specify
labels
. The colors should be specified by legend_gp
.
lgd = Legend(at = 1:6, title = "foo", legend_gp = gpar(fill = 1:6))
lgd = Legend(labels = month.name[1:6], title = "foo", legend_gp = gpar(fill = 1:6))
The discrete legend for continuous color mapping:
at = seq(0, 1, by = 0.2)
lgd = Legend(at = at, title = "foo", legend_gp = gpar(fill = col_fun(at)))
The position of title:
lgd = Legend(labels = month.name[1:6], title = "foo", legend_gp = gpar(fill = 1:6),
title_position = "lefttop")
lgd = Legend(labels = month.name[1:6], title = "foo", legend_gp = gpar(fill = 1:6),
title_position = "leftcenter-rot")
The size of grids are controlled by grid_width
and grid_height
.
lgd = Legend(at = 1:6, legend_gp = gpar(fill = 1:6), title = "foo",
grid_height = unit(1, "cm"), grid_width = unit(5, "mm"))
The graphic parameters of labels are controlled by labels_gp
.
lgd = Legend(labels = month.name[1:6], legend_gp = gpar(fill = 1:6), title = "foo",
labels_gp = gpar(col = "red", fontsize = 14))
The graphic parameters of the title are controlled by title_gp
.
lgd = Legend(labels = month.name[1:6], legend_gp = gpar(fill = 1:6), title = "foo",
title_gp = gpar(col = "red", fontsize = 14))
Title and labels are be complicated texts by integrating gridtext package (Section 10.3.5):
lgd = Legend(
title = gt_render("<span style='color:orange'>**Legend title**</span>"),
title_gp = gpar(box_fill = "grey"),
at = c(-3, 0, 3),
labels = gt_render(c("**negative** three", "*zero*", "**positive** three")),
legend_gp = gpar(fill = 1:3)
)
Borders of grids are controlled by border
.
lgd = Legend(labels = month.name[1:6], legend_gp = gpar(fill = 1:6), title = "foo",
border = "red")
One important thing for the discrete legend is you can arrange the grids into
multiple rows or/and columns. If ncol
is set to a number, the grids are
arranged into ncol
columns.
lgd = Legend(labels = month.name[1:10], legend_gp = gpar(fill = 1:10),
title = "foo", ncol = 3)
Still the title position is calculated based on the multiplt-column legend.
lgd = Legend(labels = month.name[1:10], legend_gp = gpar(fill = 1:10), title = "foo",
ncol = 3, title_position = "topcenter")
You can choose to list the legend levels by rows by setting by_row = TRUE
.
lgd = Legend(labels = month.name[1:10], legend_gp = gpar(fill = 1:10), title = "foo",
ncol = 3, by_row = TRUE)
The gaps between two columns are controlled by gap
or column_gap
. These two arguments
are treated the same.
lgd = Legend(labels = month.name[1:10], legend_gp = gpar(fill = 1:10), title = "foo",
ncol = 3, gap = unit(1, "cm"))
The gaps between rows are controlled by row_gap
.
lgd = Legend(labels = month.name[1:10], legend_gp = gpar(fill = 1:10), title = "foo",
ncol = 3, row_gap = unit(5, "mm"))
Instead of ncol
, you can also specify the layout by nrow
. Note you cannot
use ncol
and nrow
at a same time.
lgd = Legend(labels = month.name[1:10], legend_gp = gpar(fill = 1:10),
title = "foo", nrow = 3)
One extreme case is when all levels are put in one row and the title are rotated by 90 degree. The height of the legend will be the height of the rotated title.
lgd = Legend(labels = month.name[1:6], legend_gp = gpar(fill = 1:6), title = "foooooo",
nrow = 1, title_position = "lefttop-rot")
Following style a lot of people might like:
lgd = Legend(labels = month.name[1:6], legend_gp = gpar(fill = 1:6), title = "foooooo",
nrow = 1, title_position = "leftcenter")
Legend()
also supports to use simple graphics (e.g. points, lines, boxplots) as
legends. type
argument can be specified as points
or p
that you can use number
for pch
or single-letter for pch
.
lgd = Legend(labels = month.name[1:6], title = "foo", type = "points",
pch = 1:6, legend_gp = gpar(col = 1:6), background = "#FF8080")
lgd = Legend(labels = month.name[1:6], title = "foo", type = "points",
pch = letters[1:6], legend_gp = gpar(col = 1:6), background = "white")
Or set type = "lines"
/type = "l"
to use lines as legend:
lgd = Legend(labels = month.name[1:6], title = "foo", type = "lines",
legend_gp = gpar(col = 1:6, lty = 1:6), grid_width = unit(1, "cm"))
Or set type = "boxplot"
/type = "box"
to use boxes as legends:
lgd = Legend(labels = month.name[1:6], title = "foo", type = "boxplot",
legend_gp = gpar(fill = 1:6))
When pch
is an integer number, the numbers in 26:28
correspond to following symbols:
lgd = Legend(labels = paste0("pch = ", 26:28), type = "points", pch = 26:28)
In all examples showed above, the labels are single lines. Multiple-line labels are also supported. As shown in the following example, legend grids for multiple-line labels are automatically enlongated.
lgd = Legend(labels = c("aaaaa\naaaaa", "bbbbb\nbbbbb", "c", "d"),
legend_gp = gpar(fill = 1:4))
If the legend is arranged in multiple rows or columns, the sizes of legend grids are adjusted to the label with the most number of lines.
lgd = Legend(labels = c("aaaaa\naaaaa", "c", "d", "bbbbb\nbbbbb"),
legend_gp = gpar(fill = 1:4), nrow = 2)
The last useful argument graphics
can be used to self-define the legend
graphics. The value for graphics
should be a list of functions with four
arguments: x
and y
: the center of the legend grid, w
and h
: the width
and height of the legend grid. Length of graphics
should be the same as at
or labels.
If graphics
is a named list where the names correspond to
labels
, then the order of the list of graphics
is automatically adjusted.
lgd = Legend(labels = letters[1:4],
graphics = list(
function(x, y, w, h) grid.rect(x, y, w*0.33, h, gp = gpar(fill = "red")),
function(x, y, w, h) grid.rect(x, y, w, h*0.33, gp = gpar(fill = "blue")),
function(x, y, w, h) grid.text("A", x, y, gp = gpar(col = "darkgreen")),
function(x, y, w, h) grid.points(x, y, gp = gpar(col = "orange"), pch = 16)
))
5.3 A list of legends
A list of legends can be constructed or packed as a Legends
object where the
individual legends are arranged within a certain layout. The legend list can
be sent to packLegend()
separatedly or as a list. The legend can be arranged
either vertically or horizontally. ComplexHeatmap uses packLegend()
internally to arrange multiple legends. Normally you don’t need to manually
control the arrangement of multiple legends, but the following section would
be useful if you want to manually construct a list of legends and apply to
other plots.
lgd1 = Legend(at = 1:6, legend_gp = gpar(fill = 1:6), title = "legend1")
lgd2 = Legend(col_fun = col_fun, title = "legend2", at = c(0, 0.25, 0.5, 0.75, 1))
lgd3 = Legend(labels = month.name[1:3], legend_gp = gpar(fill = 7:9), title = "legend3")
pd = packLegend(lgd1, lgd2, lgd3)
# which is same as
pd = packLegend(list = list(lgd1, lgd2, lgd3))
Simillar as single legend, you can draw the packed legends by draw()
function. Also you can get the size of pd
by ComplexHeatmap:::width()
and
ComplexHeatmap:::height()
.
ComplexHeatmap:::width(pd)
## [1] 19.1675555555556mm
ComplexHeatmap:::height(pd)
## [1] 78.6988333333334mm
Horizontally arranging the legends simply by setting direction = "horizontal"
.
pd = packLegend(lgd1, lgd2, lgd3, direction = "horizontal")
One feature of packLegend()
is, e.g. if the packing is vertically and the
sum of the packed legends exceeds the height specified by max_height
, it
will be rearragned as mutliple column layout. In following example, the
maximum height is 10cm
.
When all the legends are put into multiple columns, column_gap
controls the
space between two columns.
pd = packLegend(lgd1, lgd3, lgd2, lgd3, lgd2, lgd1, max_height = unit(10, "cm"),
column_gap = unit(1, "cm"))
Similar for horizontal packing:
lgd1 = Legend(at = 1:6, legend_gp = gpar(fill = 1:6), title = "legend1",
nr = 1)
lgd2 = Legend(col_fun = col_fun, title = "legend2", at = c(0, 0.25, 0.5, 0.75, 1),
direction = "horizontal")
pd = packLegend(lgd1, lgd2, lgd3, lgd1, lgd2, lgd3, max_width = unit(10, "cm"),
direction = "horizontal", column_gap = unit(5, "mm"), row_gap = unit(1, "cm"))
The packed legends pd
is also a Legends
object, which means you can use
draw()
to draw it by specifying the positions.
pd = packLegend(lgd1, lgd2, lgd3, direction = "horizontal")
pushViewport(viewport(width = 0.8, height = 0.8))
grid.rect()
draw(pd, x = unit(1, "cm"), y = unit(1, "cm"), just = c("left", "bottom"))
draw(pd, x = unit(1, "npc"), y = unit(1, "npc"), just = c("right", "top"))
popViewport()
To be mentioned again, packLegend()
is used internally to manage the list
of heatmap and annotation legends.
5.4 Heatmap and annotation legends
Settings for heatmap legend are controlled by heatmap_legend_param
argument
in Heatmap()
. The value for heatmap_legend_param
is a list of parameters
which are supported in Legend()
.
m = matrix(rnorm(100), 10)
Heatmap(m, name = "mat", heatmap_legend_param = list(
at = c(-2, 0, 2),
labels = c("low", "zero", "high"),
title = "Some values",
legend_height = unit(4, "cm"),
title_position = "lefttop-rot"
))
annotation_legend_param
controls legends for annotations. Since a
HeatmapAnnotation
may contain multiple annotations, the value of
annotation_legend_param
is a list of configurations of each annotation.
ha = HeatmapAnnotation(foo = runif(10), bar = sample(c("f", "m"), 10, replace = TRUE),
annotation_legend_param = list(
foo = list(
title = "Fooooooh",
at = c(0, 0.5, 1),
labels = c("zero", "median", "one")
),
bar = list(
title = "Baaaaaaar",
at = c("f", "m"),
labels = c("Female", "Male")
)
))
Heatmap(m, name = "mat", top_annotation = ha)
If the heatmaps are concatenated horizontally, all heatmap and row annotation legends are grouped and all column annotation legends ae grouped. The reason we assume the horizontal direction passes the main message of the plot, while the vertical direction provides secondary information.
ha1 = HeatmapAnnotation(foo1 = runif(10), bar1 = sample(c("f", "m"), 10, replace = TRUE))
ha2 = HeatmapAnnotation(foo2 = runif(10), bar2 = sample(c("f", "m"), 10, replace = TRUE))
Heatmap(m, name = "mat1", top_annotation = ha1) +
rowAnnotation(sth = runif(10)) +
Heatmap(m, name = "mat2", top_annotation = ha2)
Similarlly, if the heatmaps are concatenated vertically, all heatmaps/column annotations are grouped and legends for all row annotations are grouped.
ha1 = HeatmapAnnotation(foo1 = runif(10), bar1 = sample(c("f", "m"), 10, replace = TRUE),
annotation_name_side = "left")
ha2 = HeatmapAnnotation(foo2 = runif(10), bar2 = sample(c("f", "m"), 10, replace = TRUE))
Heatmap(m, name = "mat1", top_annotation = ha1) %v%
Heatmap(m, name = "mat2", top_annotation = ha2,
right_annotation = rowAnnotation(sth = 1:10))
show_legend
in HeatmapAnnotation()
and show_heatmap_legend
in
Heatmap()
controls whether show the legends. Note show_legend
can be a
single logical value, a logical vector, or a named vector which controls
subset of annotations.
ha = HeatmapAnnotation(foo = runif(10),
bar = sample(c("f", "m"), 10, replace = TRUE),
show_legend = c(TRUE, FALSE), # it can also be show_legend = c(bar = FALSE)
annotation_name_side = "left")
Heatmap(m, name = "mat1", top_annotation = ha) +
Heatmap(m, name = "mat2", show_heatmap_legend = FALSE)
merge_legend
in draw()
function controlls whether to merge all the legends
into a single group. Normally, when there are many annotations and heatmaps,
the number of legends is always large. In this case, the legends are
automatically arranged into multiple columns (or multiple rows if they are put
at the bottom of the heatmaps) to get rid of being out of the figure page. If
a heatmap has heatmap annotations, the order of putting legends are: legends
for the left annotations, legends for the top annotations, legend of the
heatmap, legends for the bottom annotations and legends for the right
annotations.
ha1 = HeatmapAnnotation(foo1 = runif(10),
bar1 = sample(c("f", "m"), 10, replace = TRUE))
ha2 = rowAnnotation(foo2 = runif(10),
bar2 = sample(letters[1:3], 10, replace = TRUE))
ha3 = rowAnnotation(foo3 = runif(10),
bar3 = sample(month.name[1:3], 10, replace = TRUE))
ht_list = Heatmap(m, name = "mat1", top_annotation = ha1) +
Heatmap(m, name = "mat2", left_annotation = ha2) +
ha3
draw(ht_list, merge_legend = TRUE)
If you want the heatmap legends to be the “pure heatmap legends,” you can set
legend_grouping = "original"
to enforce all annotation legends to be put together,
no matter whether they are row annotation legends or column annotation legends.
draw(ht_list, legend_grouping = "original")
A continuous color mapping can have a
discrete legend by setting color_bar = "discrete"
, both work for heatmap
legends and annotation legends.
Heatmap(m, name = "mat", heatmap_legend_param = list(color_bar = "discrete"),
top_annotation = HeatmapAnnotation(foo = 1:10,
annotation_legend_param = list(
foo = list(color_bar = "discrete"))))
If the value
is a character vector, no matter it is an annotation or the one-row/one-column
matrix for the heatmap, the default order of legend labels is sort(unique(value))
and if value
is a factor, the order of legend labels is levels(value)
. Always remember the order can be
fine-tuned by setting at
and labels
parameters in heatmap_legend_param
/annotation_legend_param
in Heatmap()
/HeamtapAnnotation()
functions respectively.
chr = sample(letters[1:3], 10, replace = TRUE)
chr
## [1] "a" "c" "b" "c" "b" "a" "a" "a" "b" "c"
5.5 Add customized legends
The self-defined legends (constructed by Legend()
) can be added to the
heatmap legend list by heatmap_legend_list
argument in draw()
and the
legends for annotations can be added to the annotation legend list by
annotation_legend_list
argument.
There is a nice example of adding self-defined legends in Section 11.2, but here we show a simple example.
As mentioned before, only the heatmap and simple annotations can generate legends on the plot. ComplexHeatmap provides many annotation functions, but none of them supports generating legends. In following code, we add a point annotation, a line annotation and a summary annotation to the heatmaps.
ha1 = HeatmapAnnotation(pt = anno_points(1:10, gp = gpar(col = rep(2:3, each = 5)),
height = unit(2, "cm")), show_annotation_name = FALSE)
ha2 = HeatmapAnnotation(ln = anno_lines(cbind(1:10, 10:1), gp = gpar(col = 4:5, lty = 1:2),
height = unit(2, "cm")), show_annotation_name = FALSE)
m = matrix(rnorm(100), 10)
ht_list = Heatmap(m, name = "mat1", top_annotation = ha1) +
Heatmap(m, name = "mat2", top_annotation = ha2) +
Heatmap(m[, 1], name = "mat3",
top_annotation = HeatmapAnnotation(
summary = anno_summary(gp = gpar(fill = 2:3))
), width = unit(1, "cm"))
draw(ht_list, ht_gap = unit(7, "mm"), row_km = 2)
Next we construct legends for the points, the lines and the boxplots.
lgd_list = list(
Legend(labels = c("red", "green"), title = "pt", type = "points", pch = 16,
legend_gp = gpar(col = 2:3)),
Legend(labels = c("darkblue", "lightblue"), title = "ln", type = "lines",
legend_gp = gpar(col = 4:5, lty = 1:2)),
Legend(labels = c("group1", "group2"), title = "km", type = "boxplot",
legend_gp = gpar(fill = 2:3))
)
draw(ht_list, ht_gap = unit(7, "mm"), row_km = 2, annotation_legend_list = lgd_list)
5.6 The side of legends
By default, the heatmap legends and annotation legends are put on the right of
the plot. The side relative to the heatmaps of the two types of legends can be
controlled by heatmap_legend_side
and annotation_legend_side
arguments in
draw()
function. The values that can be set for the two arguments are
left
, right
, bottom
and top
.
m = matrix(rnorm(100), 10)
ha1 = HeatmapAnnotation(foo1 = runif(10), bar1 = sample(c("f", "m"), 10, replace = TRUE))
ha2 = HeatmapAnnotation(foo2 = runif(10), bar2 = sample(c("f", "m"), 10, replace = TRUE))
ht_list = Heatmap(m, name = "mat1", top_annotation = ha1) +
rowAnnotation(sth = runif(10)) +
Heatmap(m, name = "mat2", top_annotation = ha2)
draw(ht_list, heatmap_legend_side = "left", annotation_legend_side = "bottom")
When the legends are put at the bottom or on the top, the legends are arranged
horizontally. We might also want to set every single legend as horizontal
legend, this needs to be set via the heatmap_legend_param
and
annotation_legend_param
arguments in Heatmap()
and HeatmapAnnotation()
functions:
ha1 = HeatmapAnnotation(foo1 = runif(10), bar1 = sample(c("f", "m"), 10, replace = TRUE),
annotation_legend_param = list(
foo1 = list(direction = "horizontal"),
bar1 = list(nrow = 1)))
ha2 = HeatmapAnnotation(foo2 = runif(10), bar2 = sample(c("f", "m"), 10, replace = TRUE),
annotation_legend_param = list(
foo2 = list(direction = "horizontal"),
bar2 = list(nrow = 1)))
ht_list = Heatmap(m, name = "mat1", top_annotation = ha1,
heatmap_legend_param = list(direction = "horizontal")) +
rowAnnotation(sth = runif(10),
annotation_legend_param = list(sth = list(direction = "horizontal"))) +
Heatmap(m, name = "mat2", top_annotation = ha2,
heatmap_legend_param = list(direction = "horizontal"))
draw(ht_list, merge_legend = TRUE, heatmap_legend_side = "bottom",
annotation_legend_side = "bottom")