-
Notifications
You must be signed in to change notification settings - Fork 24
/
06-heatmap_decoration.Rmd
executable file
·221 lines (184 loc) · 9.48 KB
/
06-heatmap_decoration.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# Heatmap Decoration {#heatmap-decoration}
The plotting region for each heatmap component is still kept after the
heatmaps are made, so it is possible to go back to the original places to add
more graphics there. First let's generate a figure that almost contains all
types of heatmap components. `list_components()` lists the names of the
heatmap/annotation components (or the name of the viewport).
```{r access_components, fig.width = 10, fig.height = 7, echo = 1:15}
set.seed(123)
mat = matrix(rnorm(80, 2), 8, 10)
mat = rbind(mat, matrix(rnorm(40, -2), 4, 10))
rownames(mat) = paste0("R", 1:12)
colnames(mat) = paste0("C", 1:10)
ha_column1 = HeatmapAnnotation(points = anno_points(rnorm(10)),
annotation_name_side = "left")
ht1 = Heatmap(mat, name = "ht1", km = 2, column_title = "Heatmap 1",
top_annotation = ha_column1, row_names_side = "left")
ha_column2 = HeatmapAnnotation(type = c(rep("a", 5), rep("b", 5)),
col = list(type = c("a" = "red", "b" = "blue")))
ht2 = Heatmap(mat, name = "ht2", row_title = "Heatmap 2", column_title = "Heatmap 2",
bottom_annotation = ha_column2, column_km = 2)
ht_list = ht1 + ht2 +
rowAnnotation(bar = anno_barplot(rowMeans(mat), width = unit(2, "cm")))
draw(ht_list, row_title = "Heatmap list", column_title = "Heatmap list")
list_components()
library(GetoptLong)
for(heatmap_name in c("ht1", "ht2")) {
seekViewport(qq("heatmap_@{heatmap_name}"))
grid.rect(gp = gpar(fill = NA, col = "red"))
for(i in 1:2) {
seekViewport(qq("@{heatmap_name}_heatmap_body_@{i}_1"))
grid.rect(gp = gpar(fill = NA, col = "red"))
if(heatmap_name == "ht1") {
seekViewport(qq("@{heatmap_name}_row_names_@{i}"))
grid.rect(gp = gpar(fill = NA, col = "red"))
}
}
seekViewport(qq("@{heatmap_name}_column_title_1"))
grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("@{heatmap_name}_dend_column_1"))
grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("@{heatmap_name}_column_names_1"))
grid.rect(gp = gpar(fill = NA, col = "red"))
}
seekViewport(qq("ht2_heatmap_body_1_2")); grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("ht2_heatmap_body_2_2")); grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("ht2_dend_column_2")); grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("ht2_column_names_2")); grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("ht1_dend_row_2")); grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("ht1_dend_row_1")); grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("ht1_row_title_1")); grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("ht1_row_title_2")); grid.rect(gp = gpar(fill = NA, col = "red"))
for(annotation_name in c("points", "type")) {
seekViewport(qq("annotation_@{annotation_name}_1"))
grid.rect(gp = gpar(fill = NA, col = "red"))
}
seekViewport(qq("annotation_type_2")); grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("annotation_bar_1")); grid.rect(gp = gpar(fill = NA, col = "red"))
seekViewport(qq("annotation_bar_2")); grid.rect(gp = gpar(fill = NA, col = "red"))
```
Basically the red regions in above plot can be revisited by `decorate_*()`
functions.
## Decoration functions {#decoration-functions}
Since you can get the viewport name by `list_components()`, actually you can
directly go to the viewport by `seekViewport()`. To get rid of the complicated
viewport names, the `decorate_*()` functions provide a more friendly way to do
it.
There are following decoration functions in **ComplexHeatmap** package:
- `decorate_heatmap_body()`
- `decorate_annotation()`
- `decorate_dend()`
- `decorate_title()`
- `decorate_dimnames()`
- `decorate_row_names()`, identical to `decorate_dimnames(..., which = "row")`.
- `decorate_column_names()`, identical to `decorate_dimnames(..., which = "column")`.
- `decorate_row_dend()`, identical to `decorate_dend(..., which = "row")`.
- `decorate_column_dend()`, identical to `decorate_dend(..., which = "column")`.
- `decorate_row_title()`, identical to `decorate_title(..., which = "row")`.
- `decorate_column_title()`, identical to `decorate_title(..., which = "column")`.
Among them, `decorate_heatmap_body()` and `decorate_annotation()` are more often used.
For all these functions, they need a heatmap or annotation name, index for the
row/column slices if the heatmap is split and a code block which defines how
to add graphics. Check following example.
```{r components, fig.width = 10, fig.height = 7}
ht_list = draw(ht_list, row_title = "Heatmap list", column_title = "Heatmap list",
heatmap_legend_side = "right", annotation_legend_side = "left")
decorate_heatmap_body("ht1", {
grid.text("outlier", 1.5/10, 2.5/4, default.units = "npc")
grid.lines(c(0.5, 0.5), c(0, 1), gp = gpar(lty = 2, lwd = 2))
}, slice = 2)
decorate_column_dend("ht1", {
tree = column_dend(ht_list)$ht1[[1]]
ind = cutree(as.hclust(tree), k = 2)[order.dendrogram(tree)]
first_index = function(l) which(l)[1]
last_index = function(l) { x = which(l); x[length(x)] }
x1 = c(first_index(ind == 1), first_index(ind == 2)) - 1
x2 = c(last_index(ind == 1), last_index(ind == 2))
grid.rect(x = x1/length(ind), width = (x2 - x1)/length(ind), just = "left",
default.units = "npc", gp = gpar(fill = c("#FF000040", "#00FF0040"), col = NA))
})
decorate_row_names("ht1", {
grid.rect(gp = gpar(fill = "#FF000040"))
}, slice = 2)
decorate_row_title("ht1", {
grid.rect(gp = gpar(fill = "#00FF0040"))
}, slice = 1)
decorate_annotation("points", {
grid.lines(c(0, 1), unit(c(0, 0), "native"), gp = gpar(col = "red"))
})
```
For annotations which are created by `anno_points()`, `anno_barplot()` and
`anno_boxplot()`, "native" unit can be used in the decoration code.
## Examples {#decoration-examples}
### Barplot for single-column heatmap {#barplot-for-single-column-heatmap}
In Section \@ref(summary-annotation), we introduced adding barplots as
annotations for single-column heatmap. In that case the heatmap contains
discrete values where the barplots show the frequency of each level. In
following example, we show another scenario of using barplot as annotation but
for a continuous heatmap.
Imagining we are analyzing a set of genomic regions (e.g. differentially
methylated regions, DMRs) and we have a single-column heatmap which shows the
overlap to e.g. genes (measured by the fraction of a DMR covered by genes,
value between 0 and 1, e.g. a value of 0.5 means 50% of this DMR overlaps to
the genes). If we denote the width of DMRs as $w$ and the fraction as $p$, on
top of the fraction heatmap, we want to add barplots to show, on average, how
much of the DMRs are covered by genes. In this case, we need to calcualte the
mean fraction weighted by the width of DMRs ($\sum{(w \cdot p)}/\sum{w}$).
In following code, we randomly generated a fraction vector and split it into
two groups. We first use `anno_empty()` to allocate empty plotting region on
top of the heatmap and later we use `decorate_annotation()` to add the
barplots into it.
```{r}
library(circlize)
# DMRs
bed = generateRandomBed(nr = 1000)
# fractions
frac = c(runif(400, min = 0.4, max = 1), runif(nrow(bed) - 400, min = 0.2, max = 0.6))
col_fun = colorRamp2(c(0, 1), c("white", "orange"))
# two groups
split = c(rep("group1", 400), rep("group2", nrow(bed) - 400))
# draw the fraction heatmap with an empty annotation
ht = Heatmap(frac, name = "fraction", col = col_fun, width = unit(2, "cm"),
top_annotation = HeatmapAnnotation(barplot = anno_empty(height = unit(4, "cm"))))
ht = draw(ht, row_split = split)
# get the row indices in the two row-groups
ro = row_order(ht)
w = bed[, 3] - bed[, 2]
# the mean weighted fraction in the two groups
p = sapply(ro, function(index) {
sum(w[index]*frac[index])/sum(w[index])
})
# add two bars of `p`
decorate_annotation("barplot", {
pushViewport(viewport(xscale = c(0.5, 2.5), yscale = c(0, max(p)*1.1)))
grid.rect(x = 1:2, y = 0, width = 0.8, height = p, just = "bottom",
gp = gpar(fill = "orange"), default.units = "native")
grid.yaxis()
grid.text("mean fraction", x = unit(-1.5, "cm"),rot = 90, just = "bottom")
popViewport()
})
```
### Add titles for row annotations {#add-titles-for-row-annotations}
Row annotations can be concatenated to the heatmap list. Sometimes we need a
title for the row annotation. It is easy to implement it by decorations.
```{r, fig.width = 8}
ht_list = Heatmap(matrix(rnorm(100), 10), name = "mat", show_column_dend = FALSE) +
rowAnnotation(foo = anno_barplot(1:10, width = unit(4, "cm"))) +
rowAnnotation(bar = anno_boxplot(matrix(rnorm(100), 10)), width = unit(4, "cm"))
draw(ht_list, padding = unit(c(2, 2, 10, 2), "mm")) # add space for titles
decorate_annotation("foo", {
grid.text("title for barplot", y = unit(1, "npc") + unit(2, "mm"), just = "bottom")
})
decorate_annotation("bar", {
grid.text("title for boxplot", y = unit(1, "npc") + unit(2, "mm"), just = "bottom")
})
decorate_heatmap_body("mat", {
grid.text("title for the heatmap", y = unit(1, "npc") + unit(2, "mm"), just = "bottom")
})
```
This is basically the way we use in Section \@ref(stacked-summary-plot).
### Other possible use of decorations {#other-possible-use-of-decorations}
There are some other examples where decoration is helpful:
- The quantile lines are added in `densityHeatmap()`, Section \@ref(density-heatmap).
- Heatmap annotations are grouped by lines, Section \@ref(visualize-methylation-profile-with-complex-annotations).
- Texts are added to heatmaps, Section \@ref(the-measles-vaccine-heatmap).