vignettes/s2.single_heatmap.Rmd
5ab2cb81
 <!--
 %\VignetteEngine{knitr}
12e85497
 %\VignetteIndexEntry{2. Making a single heatmap}
5ab2cb81
 -->
 
 Making A Single Heatmap
 ========================================
 
 **Author**: Zuguang Gu ( z.gu@dkfz.de )
 
 **Date**: `r Sys.Date()`
 
 -------------------------------------------------------------
 
 ```{r global_settings, echo = FALSE, message = FALSE}
 library(markdown)
 options(markdown.HTML.options = c(options('markdown.HTML.options')[[1]], "toc"))
 
 library(knitr)
 knitr::opts_chunk$set(
     error = FALSE,
     tidy  = FALSE,
     message = FALSE,
     fig.align = "center",
     fig.width = 5,
     fig.height = 5)
 options(markdown.HTML.stylesheet = "custom.css")
 
 options(width = 100)
 ```
 
 A single heatmap is mostly used for a quick view of the data. It is a special case of 
 a heatmap list which only contains one heatmap. Compare to available tools, **ComplexHeatmap** package provides a more flexible
 way to support visualization of a single heatmap. In following examples, we will demonstrate
 how to set parameters to visualize a single heatmap.
 
 First let's load packages and generate a random matrix:
 
 ```{r data}
 library(ComplexHeatmap)
 library(circlize)
 
 set.seed(123)
 
 mat = cbind(rbind(matrix(rnorm(16, -1), 4), matrix(rnorm(32, 1), 8)),
             rbind(matrix(rnorm(24, 1), 4), matrix(rnorm(48, -1), 8)))
 
 # permute the rows and columns
 mat = mat[sample(nrow(mat), nrow(mat)), sample(ncol(mat), ncol(mat))]
 
 rownames(mat) = paste0("R", 1:12)
 colnames(mat) = paste0("C", 1:10)
 ```
 
 Plot the heatmap with default settings. The default style of the heatmap is quite the same
 as those generated by other similar heatmap functions.
 
 ```{r default}
 Heatmap(mat)
 ```
 
 ## Colors
 
 In most cases, the heatmap visualizes a matrix with continuous values.
 In this case, user should provide a color mapping function. A color mapping function
 should accept a vector of values and return a vector of corresponding colors. The `colorRamp2()` from
 the **circlize** package is helpful for generating such functions. The two arguments for `colorRamp2()`
 is a vector of breaks values and corresponding colors. Currently `colorRamp2()` linearly interpolates
ba8a5070
 colors in every interval through LAB color space.
5ab2cb81
 
 In following example, values between -3 and 3 are 
 linearly interpolated to obtain corresponding colors, values larger than 3 are all mapped to red and values less than -3 are
 all mapped to green (so the color mapping function demonstrated here is robust to outliers).
 
 ```{r color_fun}
 mat2 = mat
 mat2[1, 1] = 100000
 Heatmap(mat2, col = colorRamp2(c(-3, 0, 3), c("green", "white", "red")), 
     cluster_rows = FALSE, cluster_columns = FALSE)
 ```
 
 If the matrix is continuous, you can also provide a vector of colors and colors will be interpolated according to the 'k'th quantile.
 But remember this method is not robust to outliers.
 
 ```{r color_vector}
 Heatmap(mat, col = rev(rainbow(10)))
 ```
 
 If the matrix contains discrete values (either numeric or character), colors should be specified as
 a named vector to make it possible for the mapping from discrete values to colors. If there is no name
 for the color, the order of colors corresponds to the order of `unique(mat)`.
 
 ```{r discrete_matrix}
 discrete_mat = matrix(sample(1:4, 100, replace = TRUE), 10, 10)
 colors = structure(circlize::rand_color(4), names = c("1", "2", "3", "4"))
 Heatmap(discrete_mat, col = colors)
 ```
 
 Or a character matrix:
 
 ```{r discrete_character_matrix}
 discrete_mat = matrix(sample(letters[1:4], 100, replace = TRUE), 10, 10)
 colors = structure(circlize::rand_color(4), names = letters[1:4])
 Heatmap(discrete_mat, col = colors)
 ```
 
 As you see, for the numeric matrix (no matter it is continuous mapping or discrete mapping), 
 by default clustering is applied on both dimensions while
 for character matrix, clustering is suppressed.
 
 ``NA`` is allowed in the heatmap. You can control the color of `NA` by `na_col` argument. 
900f036d
 The matrix which contains `NA` can also be clustered by `Heatmap()` (since `dist()` accepts `NA` values)
 and clustering a matrix with `NA` values by "pearson", "spearman" or "kendall" method gives warning messages.
5ab2cb81
 
 ```{r na_value}
 mat_with_na = mat
 mat_with_na[sample(c(TRUE, FALSE), nrow(mat)*ncol(mat), replace = TRUE, prob = c(1, 9))] = NA
900f036d
 Heatmap(mat_with_na, na_col = "orange", clustering_distance_rows = "pearson")
5ab2cb81
 ```
 
451e6d73
 Color space is important for interpolating colors. By default, colors are linearly interpolated in [LAB color space](https://en.wikipedia.org/wiki/Lab_color_space), but you can select the color space in `colorRamp2()` function. Compare following two plots
 (`+` operation on two heatmaps will be introduced in [**Making a list of heatmaps**](s3.a_list_of_heatmaps.html) vignette):
ba8a5070
 
 ```{r, fig.width = 10}
 f1 = colorRamp2(seq(min(mat), max(mat), length = 3), c("blue", "#EEEEEE", "red"))
 f2 = colorRamp2(seq(min(mat), max(mat), length = 3), c("blue", "#EEEEEE", "red"), space = "RGB")
 Heatmap(mat, col = f1, column_title = "LAB color space") +
 Heatmap(mat, col = f2, column_title = "RGB color space")
 ```
 
451e6d73
 On following figure, corresponding values change evenly on the folded axis, you can see how colors change under different
 color spaces (the plot is made by **HilbertCurve** package). Choosing a proper color space is a little bit 
 subjective and it depends on specific data and color theme.
ba8a5070
 Sometimes you need to try several color spaces to determine one which can best reveal potential structure of your data.
 
 
451e6d73
 ```{r, fig.width = 14, fig.height = 14/5, echo = FALSE, message = FALSE}
 suppressPackageStartupMessages(library(HilbertCurve))
1f5bf88e
 suppressPackageStartupMessages(library(IRanges))
451e6d73
 space = c("RGB", "LAB", "XYZ", "sRGB", "LUV")
 pushViewport(viewport(layout = grid.layout(nr = 1, nc = length(space))))
ba8a5070
 for(i in seq_along(space)) {
451e6d73
     pushViewport(viewport(layout.pos.row = 1, layout.pos.col = i))
     hc = HilbertCurve(1, 100, level = 4, newpage = FALSE, title = space[i])
     ir = IRanges(start = 1:99, end = 2:100)
     f = colorRamp2(c(-1, 0, 1), c("green", "black", "red"), space = space[i])
     col = f(seq(-1, 1, length = 100))
     hc_points(hc, ir, np = 3, gp = gpar(col = col, fill = col))
     upViewport()
ba8a5070
 }
451e6d73
 upViewport()
 grid.newpage()
 pushViewport(viewport(layout = grid.layout(nr = 1, nc = length(space))))
 for(i in seq_along(space)) {
     pushViewport(viewport(layout.pos.row = 1, layout.pos.col = i))
     hc = HilbertCurve(1, 100, level = 4, newpage = FALSE, title = space[i])
     ir = IRanges(start = 1:99, end = 2:100)
     f = colorRamp2(c(-1, 0, 1), c("blue", "white", "red"), space = space[i])
     col = f(seq(-1, 1, length = 100))
     hc_points(hc, ir, np = 3, gp = gpar(col = col, fill = col))
     upViewport()
 }
 upViewport()
 
ba8a5070
 ```
 
5ab2cb81
 ## Titles
 
 The name of the heatmap by default is used as the title of the heatmap legend.
 The name also plays as a unique id if you plot more than one heatmaps together.
 Later we can use this name to go to the corresponding heatmap to add more graphics 
451e6d73
 (see [**Heatmap Decoration**](s6.heatmap_decoration.html) vignette).
5ab2cb81
 
 ```{r with_matrix_name}
 Heatmap(mat, name = "foo")
 ```
 
 The title of the heatmap legend can be modified by `heatmap_legend_param` 
451e6d73
 (see [**Heatmap and Annotation Legends**](s5.legend.html) vignette
5ab2cb81
 for more control on the legend).
 
 ```{r heatmap_legend_title}
 Heatmap(mat, heatmap_legend_param = list(title = "legend"))
 ```
 
 You can set heatmap titles to be put either by the rows or by the columns. Note at a same time
 you can only put e.g. column title either on the top or at the bottom of the heatmap.
 The graphic parameters can be set by `row_title_gp` and `column_title_gp` respectively.
 Please remember you should use `gpar()` to specify graphic parameters.
 
 ```{r row_column_title}
 Heatmap(mat, name = "foo", column_title = "I am a column title", 
     row_title = "I am a row title")
 Heatmap(mat, name = "foo", column_title = "I am a column title at the bottom", 
     column_title_side = "bottom")
 Heatmap(mat, name = "foo", column_title = "I am a big column title", 
     column_title_gp = gpar(fontsize = 20, fontface = "bold"))
 ```
 
f6325167
 Rotations for titles can be set by `row_title_rot` and `column_title_rot`, but only horizontal and vertical 
5ab2cb81
 rotations are allowed.
 
 ```{r title_rotation}
 Heatmap(mat, name = "foo", row_title = "row title", row_title_rot = 0)
 ```
 
 ## Clustering
 
 Clustering may be the key feature of the heatmap visualization. In **ComplexHeatmap** package,
 clustering is supported with high flexibility. You can specify the 
 clustering either by a pre-defined method (e.g. "eulidean" or "pearson"), or by a distance function, or by a object that already
 contains clustering, or directly by a clustering function. It is also possible to render 
 your dendrograms with different colors and styles for different branches for better revealing
 structures of your data.
 
f6325167
 First there are general settings for the clustering, e.g. whether do or show dendrograms, side
5ab2cb81
 of the dendrograms and size of the dendrograms.
 
 ```{r cluster_basic}
 Heatmap(mat, name = "foo", cluster_rows = FALSE)
ac4a2fca
 Heatmap(mat, name = "foo", show_column_dend = FALSE)
 Heatmap(mat, name = "foo", row_dend_side = "right")
 Heatmap(mat, name = "foo", column_dend_height = unit(2, "cm"))
5ab2cb81
 ```
 
 There are three ways to specify distance metric for clustering:
 
 - specify distance as a pre-defined option. The valid values are the supported methods
   in `dist()` function and within `pearson`, `spearman` and `kendall`. `NA` values are ignored
   for pre-defined clustering but with giving warnings (see example in **Colors** section).
 - a self-defined function which calculates distance from a matrix. The function should
   only contain one argument. Please note for clustering on columns, the matrix will be transposed
   automatically.
 - a self-defined function which calculates distance from two vectors. The function should
   only contain two arguments.
 
 ```{r cluster_distance}
 Heatmap(mat, name = "foo", clustering_distance_rows = "pearson")
 Heatmap(mat, name = "foo", clustering_distance_rows = function(m) dist(m))
 Heatmap(mat, name = "foo", clustering_distance_rows = function(x, y) 1 - cor(x, y))
 ```
 
 Based on this feature, we can apply clustering which is robust to outliers based on the pair-wise distance.
 
 ```{r cluster_distance_advanced}
 mat_with_outliers = mat
 for(i in  1:10) mat_with_outliers[i, i] = 1000
 robust_dist = function(x, y) {
     qx = quantile(x, c(0.1, 0.9))
     qy = quantile(y, c(0.1, 0.9))
     l = x > qx[1] & x < qx[2] & y > qy[1] & y < qy[2]
     x = x[l]
     y = y[l]
     sqrt(sum((x - y)^2))
 }
 Heatmap(mat_with_outliers, name = "foo", 
     col = colorRamp2(c(-3, 0, 3), c("green", "white", "red")),
     clustering_distance_rows = robust_dist,
     clustering_distance_columns = robust_dist)
 ```
 
 If possible distance method provided, you can also cluster a character matrix.
 `cell_fun` argument will be explained in later section.
 
 ```{r cluster_character_matrix}
 mat_letters = matrix(sample(letters[1:4], 100, replace = TRUE), 10)
f6325167
 # distance in th ASCII table
5ab2cb81
 dist_letters = function(x, y) {
     x = strtoi(charToRaw(paste(x, collapse = "")), base = 16)
     y = strtoi(charToRaw(paste(y, collapse = "")), base = 16)
     sqrt(sum((x - y)^2))
 }
 Heatmap(mat_letters, name = "foo", col = structure(2:5, names = letters[1:4]),
     clustering_distance_rows = dist_letters, clustering_distance_columns = dist_letters,
     cell_fun = function(j, i, x, y, w, h, col) {
         grid.text(mat_letters[i, j], x, y)
     })
 ```
 
 Method to make hierarchical clustering can be specified by `clustering_method_rows` and
 `clustering_method_columns`. Possible methods are those supported in `hclust()` function.
 
 ```{r cluster_method}
 Heatmap(mat, name = "foo", clustering_method_rows = "single")
 ```
 
 By default, clustering is performed by `hclust()`. But you can also utilize clustering results
 which are generated by other methods by specifying `cluster_rows` or `cluster_columns` to a 
 `hclust` or `dendrogram` object. In following examples, we use `diana()` and `agnes()` methods
 which are from the **cluster** package to perform clusterings.
 
 ```{r cluster_object}
 library(cluster)
 Heatmap(mat, name = "foo", cluster_rows = as.dendrogram(diana(mat)),
    cluster_columns = as.dendrogram(agnes(t(mat))))
 ```
 
12e85497
 In the native `heatmap()` function, dendrograms on row and on column are reordered to let features with larger different
f6325167
 separated more from each other, By default the reordering for the dendrograms are turned on by `Heatmap()` as well. 
12e85497
 
 Besides the default reordering method, you can first generate a dendrogram and apply other reordering
 method and then send the reordered dendrogram to `cluster_rows` argument. 
5ab2cb81
 
 Compare following three plots:
 
 ```{r cluster_dendsort, fig.width = 14}
 pushViewport(viewport(layout = grid.layout(nr = 1, nc = 3)))
 pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 1))
ac4a2fca
 draw(Heatmap(mat, name = "foo", row_dend_reorder = FALSE, column_title = "no reordering"), newpage = FALSE)
5ab2cb81
 upViewport()
 
 pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 2))
ac4a2fca
 draw(Heatmap(mat, name = "foo", row_dend_reorder = TRUE, column_title = "applied reordering"), newpage = FALSE)
5ab2cb81
 upViewport()
 
 library(dendsort)
 dend = dendsort(hclust(dist(mat)))
 pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 3))
ac4a2fca
 draw(Heatmap(mat, name = "foo", cluster_rows = dend, row_dend_reorder = FALSE, 
5ab2cb81
     column_title = "reordering by dendsort"), newpage = FALSE)
 upViewport(2)
 ```
 
 You can render your `dendrogram` object by the **dendextend** package and make a more customized
 visualization of the dendrogram. 
 
 ```{r cluster_dendextend}
 library(dendextend)
 dend = hclust(dist(mat))
 dend = color_branches(dend, k = 2)
 Heatmap(mat, name = "foo", cluster_rows = dend)
 ```
 
 More generally, `cluster_rows` and `cluster_columns` can be functions which calculate the clusterings.
 The input argument for the self-defined function should be a matrix and returned value should be a `hclust` or `dendrogram`
 object. Please note, when `cluster_rows` is executed internally, the argument `m` is the input `mat` itself
 while `m` is the transpose of `mat` when executing `cluster_columns`.
 
 ```{r cluster_function}
 Heatmap(mat, name = "foo", cluster_rows = function(m) as.dendrogram(diana(m)),
     cluster_columns = function(m) as.dendrogram(agnes(m)))
 ```
 
f6325167
 `fastcluster::hclust` implements a faster version of `hclust`. We can re-define `cluster_rows` and `cluster_columns`
 to use the faster version of `hclust`. But note `fastcluster::hclust` only speed up the calculation of the cluster while not the
 calculation of distance matrix.
 
 ```{r}
 # code not run when building the vignette
 Heatmap(mat, name = "foo", cluster_rows = function(m) fastcluster::hclust(dist(m)),
     cluster_columns = function(m) fastcluster::hclust(dist(m))) # for column cluster, m will be automatically transposed
 ```
 
 To make it more convinient to use the faster version of `hclust` (assuming you have many heatmaps to be concatenated), it can
 be set as a global option:
 
 ```{r}
 # code not run when building the vignette
 ht_global_opt(fast_hclust = TRUE)
 # now hclust from fastcluster package is used in all heatmaps
 Heatmap(mat, name = "foo")
 ```
 
5ab2cb81
 Clustering can help to adjust order in rows and in columns. But you can still set the order manually by `row_order`
 and `column_order`. Note you need to turn off clustering
ac4a2fca
 if you want to set order manually. `row_order` and `column_order` can also be set according to matrix row names and column names if they exist.
5ab2cb81
 
 ```{r manual_order}
 Heatmap(mat, name = "foo", cluster_rows = FALSE, cluster_columns = FALSE, 
     row_order = 12:1, column_order = 10:1)
 ```
 
ac4a2fca
 Note `row_dend_reorder` and `row_order` are different. `row_dend_reorder` is applied on the dendrogram. Because for any node in the
5ab2cb81
 dendrogram, rotating two leaves gives an identical dendrogram. Thus, reordering the dendrogram by automatically rotating sub-dendrogram
 at every node will help to separate elements with more difference to be farther from each other. While `row_order` is
ac4a2fca
 applied on the matrix and dendrograms are suppressed.
5ab2cb81
 
 ## Dimension names
 
 Side, visibility and graphic parameters for dimension names can be set as follows.
 
 ```{r dimension_name}
ac4a2fca
 Heatmap(mat, name = "foo", row_names_side = "left", row_dend_side = "right", 
     column_names_side = "top", column_dend_side = "bottom")
5ab2cb81
 Heatmap(mat, name = "foo", show_row_names = FALSE)
 Heatmap(mat, name = "foo", row_names_gp = gpar(fontsize = 20))
 Heatmap(mat, name = "foo", row_names_gp = gpar(col = c(rep("red", 4), rep("blue", 8))))
 ```
 
 Currently, rotations for column names and row names are not supported (or maybe in the future versions). Because
 after the text rotation, the dimension names will go inside other heatmap components
451e6d73
 which will mess up the heatmap layout. However, as will be introduced in [**Heatmap Annotation**](s4.heatmap_annotation.html)
5ab2cb81
 vignette, text rotation is allowed in the heatmap annotations. Thus, users can provide a row annotation
 or column annotation which only contains rotated text to simulate rotated row/column names (You will see the 
451e6d73
 example in the [**Heatmap Annotation**](s4.heatmap_annotation.html) vignette).
5ab2cb81
 
 ## Split heatmap by rows
 
 A heatmap can be split by rows. This will enhance the visualization of group separation in the heatmap.
 The `km` argument with a value larger than 1 means applying a k-means clustering on rows and clustering
ac4a2fca
 is applied on every k-means cluster.
5ab2cb81
 
 ```{r k_means}
 Heatmap(mat, name = "foo", km = 2)
 ```
 
 More generally, `split` can be set to a vector or a data frame in which different combination of levels
 split the rows of the heatmap. Actually, k-means clustering just generates a vector of row classes and appends
 `split` with one additional column. The combined row titles for each row slice can be controlled by `combined_name_fun` argument.
 The order of each slice can be controlled by `levels` of each variable in `split`.
 
 ```{r split}
 Heatmap(mat, name = "foo", split = rep(c("A", "B"), 6))
 Heatmap(mat, name = "foo", split = data.frame(rep(c("A", "B"), 6), rep(c("C", "D"), each = 6)))
 Heatmap(mat, name = "foo", split = data.frame(rep(c("A", "B"), 6), rep(c("C", "D"), each = 6)), 
     combined_name_fun = function(x) paste(x, collapse = "\n"))
 Heatmap(mat, name = "foo", km = 2, split = factor(rep(c("A", "B"), 6), levels = c("B", "A")), 
     combined_name_fun = function(x) paste(x, collapse = "\n"))
 Heatmap(mat, name = "foo", km = 2, split = rep(c("A", "B"), 6), combined_name_fun = NULL)
 ```
 
 If you are not happy with the default k-means partitioning method, it is easy to use other partitioning methods
 by just assigning the partitioning vector to `split`.
 
 ```{r pam}
 pa = pam(mat, k = 3)
 Heatmap(mat, name = "foo", split = paste0("pam", pa$clustering))
 ```
 
 If ``row_order`` is set, in each slice, rows are still ordered.
 
 ```{r split_row_order}
 Heatmap(mat, name = "foo", row_order = 12:1, cluster_rows = FALSE, km = 2)
 ```
 
 Height of gaps between row slices can be controlled by `gap` (a single unit or a vector of units).
 
 ```{r split_gap}
 Heatmap(mat, name = "foo", split = paste0("pam", pa$clustering), gap = unit(5, "mm"))
 ```
 
 Character matrix can only be split by `split` argument.
 
 ```{r split_discrete_matrix}
ac4a2fca
 Heatmap(discrete_mat, name = "foo", col = 1:4,
     split = rep(letters[1:2], each = 5))
5ab2cb81
 ```
 
 When split is applied on rows, graphic parameters for row title and row names can be specified as same
 length as number of row slices.
 
 ```{r split_graphical_parameter}
 Heatmap(mat, name = "foo", km = 2, row_title_gp = gpar(col = c("red", "blue"), font = 1:2),
     row_names_gp = gpar(col = c("green", "orange"), fontsize = c(10, 14)))
 ```
 
 Users may already have a dendrogram for rows
 and they want to split rows by splitting the dendrogram into k sub trees. In this case,
 `split` can be specified as a single number:
 
 ```{r split_dendrogram}
f6325167
 dend = hclust(dist(mat))
 dend = color_branches(dend, k = 2)
5ab2cb81
 Heatmap(mat, name = "foo", cluster_rows = dend, split = 2)
 ```
 
 Or they just split rows by specifying `split` as an integer. Note it is different from by `km`.
 If `km` is set, k-means clustering is applied first and clustering is applied to every k-mean cluster;
 while if `split` is an integer, clustering is applied to the whole matrix and later split by `cutree()`.
 
 ```{r}
 Heatmap(mat, name = "foo", split = 2)
 ```
 
 ## Self define the heatmap body
 
 `rect_gp` argument provides basic graphic settings for the heatmap body (note `fill` parameter is disabled).
 
 ```{r rect_gp}
 Heatmap(mat, name = "foo", rect_gp = gpar(col = "green", lty = 2, lwd = 2))
 ```
 
 The heatmap body can be self-defined. By default the heatmap body is composed by an array of rectangles (it is called cells here)
 with different filled colors. If `type` in `rect_gp` is set to `none`, the array for cells is initialized but
 no graphics are put in. Then, users can define their own graphic function by `cell_fun`. `cell_fun` is applied
 on every cell in the heatmap and provides following information on the 'current' cell:
 
 - `j`: column index in the matrix. Column index corresponds to the x-direction in the viewport, 
   that's why `j` is put as the first argument.
 - `i`: row index in the matrix.
 - `x`: x coordinate of middle point of the cell which is measured in the viewport of the heatmap body.
 - `y`: y coordinate of middle point of the cell which is measured in the viewport of the heatmap body.
 - `width`: width of the cell.
 - `height`: height of the cell.
 - `fill`: color of the cell.
 
ba175e8f
 The most common use is to add numeric values to the heatmap:
 
 ```{r}
 Heatmap(mat, name = "foo", cell_fun = function(j, i, x, y, width, height, fill) {
     grid.text(sprintf("%.1f", mat[i, j]), x, y, gp = gpar(fontsize = 10))
 })
 ```
 
5ab2cb81
 In following example, we make a heatmap which shows correlation matrix similar as the **corrplot** package:
 
 ```{r cell_fun, fig.width = 6.5, fig.height = 6}
 cor_mat = cor(mat)
 od = hclust(dist(cor_mat))$order
 cor_mat = cor_mat[od, od]
 nm = rownames(cor_mat)
 col_fun = circlize::colorRamp2(c(-1, 0, 1), c("green", "white", "red"))
 # `col = col_fun` here is used to generate the legend
 Heatmap(cor_mat, name = "correlation", col = col_fun, rect_gp = gpar(type = "none"), 
     cell_fun = function(j, i, x, y, width, height, fill) {
         grid.rect(x = x, y = y, width = width, height = height, gp = gpar(col = "grey", fill = NA))
         if(i == j) {
             grid.text(nm[i], x = x, y = y)
         } else if(i > j) {
             grid.circle(x = x, y = y, r = abs(cor_mat[i, j])/2 * min(unit.c(width, height)), 
                 gp = gpar(fill = col_fun(cor_mat[i, j]), col = NA))
         } else {
             grid.text(sprintf("%.1f", cor_mat[i, j]), x, y, gp = gpar(fontsize = 8))
         }
     }, cluster_rows = FALSE, cluster_columns = FALSE,
     show_row_names = FALSE, show_column_names = FALSE)
 ```
 
 Note `cell_fun` is applied to every cell through a `for` loop, so it will be a little bit slow
 for large matrix.
 
c1ecc265
 One last example is to visualize a [GO game](https://en.wikipedia.org/wiki/Go_(game)). The input data takes
 records of moves in the game.
 
 ```{r}
 str = "B[cp];W[pq];B[dc];W[qd];B[eq];W[od];B[de];W[jc];B[qk];W[qn]
 ;B[qh];W[ck];B[ci];W[cn];B[hc];W[je];B[jq];W[df];B[ee];W[cf]
 ;B[ei];W[bc];B[ce];W[be];B[bd];W[cd];B[bf];W[ad];B[bg];W[cc]
 ;B[eb];W[db];B[ec];W[lq];B[nq];W[jp];B[iq];W[kq];B[pp];W[op]
 ;B[po];W[oq];B[rp];W[ql];B[oo];W[no];B[pl];W[pm];B[np];W[qq]
 ;B[om];W[ol];B[pk];W[qp];B[on];W[rm];B[mo];W[nr];B[rl];W[rk]
 ;B[qm];W[dp];B[dq];W[ql];B[or];W[mp];B[nn];W[mq];B[qm];W[bp]
 ;B[co];W[ql];B[no];W[pr];B[qm];W[dd];B[pn];W[ed];B[bo];W[eg]
 ;B[ef];W[dg];B[ge];W[gh];B[gf];W[gg];B[ek];W[ig];B[fd];W[en]
 ;B[bn];W[ip];B[dm];W[ff];B[cb];W[fe];B[hp];W[ho];B[hq];W[el]
 ;B[dl];W[fk];B[ej];W[fp];B[go];W[hn];B[fo];W[em];B[dn];W[eo]
 ;B[gp];W[ib];B[gc];W[pg];B[qg];W[ng];B[qc];W[re];B[pf];W[of]
 ;B[rc];W[ob];B[ph];W[qo];B[rn];W[mi];B[og];W[oe];B[qe];W[rd]
 ;B[rf];W[pd];B[gm];W[gl];B[fm];W[fl];B[lj];W[mj];B[lk];W[ro]
 ;B[hl];W[hk];B[ik];W[dk];B[bi];W[di];B[dj];W[dh];B[hj];W[gj]
 ;B[li];W[lh];B[kh];W[lg];B[jn];W[do];B[cl];W[ij];B[gk];W[bl]
 ;B[cm];W[hk];B[jk];W[lo];B[hi];W[hm];B[gk];W[bm];B[cn];W[hk]
 ;B[il];W[cq];B[bq];W[ii];B[sm];W[jo];B[kn];W[fq];B[ep];W[cj]
 ;B[bk];W[er];B[cr];W[gr];B[gk];W[fj];B[ko];W[kp];B[hr];W[jr]
 ;B[nh];W[mh];B[mk];W[bb];B[da];W[jh];B[ic];W[id];B[hb];W[jb]
 ;B[oj];W[fn];B[fs];W[fr];B[gs];W[es];B[hs];W[gn];B[kr];W[is]
 ;B[dr];W[fi];B[bj];W[hd];B[gd];W[ln];B[lm];W[oi];B[oh];W[ni]
 ;B[pi];W[ki];B[kj];W[ji];B[so];W[rq];B[if];W[jf];B[hh];W[hf]
 ;B[he];W[ie];B[hg];W[ba];B[ca];W[sp];B[im];W[sn];B[rm];W[pe]
 ;B[qf];W[if];B[hk];W[nj];B[nk];W[lr];B[mn];W[af];B[ag];W[ch]
 ;B[bh];W[lp];B[ia];W[ja];B[ha];W[sf];B[sg];W[se];B[eh];W[fh]
 ;B[in];W[ih];B[ae];W[so];B[af]"
 ```
 
 Then we convert it into a matrix:
 
 ```{r}
 str = gsub("\\n", "", str)
 step = strsplit(str, ";")[[1]]
 type = gsub("(B|W).*", "\\1", step)
 row = gsub("(B|W)\\[(.).\\]", "\\2", step)
 column = gsub("(B|W)\\[.(.)\\]", "\\2", step)
 
 mat = matrix(nrow = 19, ncol = 19)
 rownames(mat) = letters[1:19]
 colnames(mat) = letters[1:19]
 for(i in seq_along(row)) {
     mat[row[i], column[i]] = type[i]
 }
 mat
 ```
 
 Black and white stones are put based on the values in the matrix:
 
 ```{r, fig.width = 8, fig.height = 8}
 Heatmap(mat, name = "go", rect_gp = gpar(type = "none"),
     cell_fun = function(j, i, x, y, w, h, col) {
         grid.rect(x, y, w, h, gp = gpar(fill = "#dcb35c", col = NA))
         if(i == 1) {
             grid.segments(x, y-h*0.5, x, y)
         } else if(i == nrow(mat)) {
             grid.segments(x, y, x, y+h*0.5)
         } else {
             grid.segments(x, y-h*0.5, x, y+h*0.5)
         }
         if(j == 1) {
             grid.segments(x, y, x+w*0.5, y)        
         } else if(j == ncol(mat)) {
             grid.segments(x-w*0.5, y, x, y)
         } else {
             grid.segments(x-w*0.5, y, x+w*0.5, y)
         }
 
         if(i %in% c(4, 10, 16) & j %in% c(4, 10, 16)) {
             grid.points(x, y, pch = 16, size = unit(2, "mm"))
         }
                 
         r = min(unit.c(w, h))*0.45
         if(is.na(mat[i, j])) {
         } else if(mat[i, j] == "W") {
             grid.circle(x, y, r, gp = gpar(fill = "white", col = "white"))
         } else if(mat[i, j] == "B") {
             grid.circle(x, y, r, gp = gpar(fill = "black", col = "black"))
         }
     },
     col = c("B" = "black", "W" = "white"),
     show_row_names = FALSE, show_column_names = FALSE,
     column_title = "One famous GO game",
     heatmap_legend_param = list(title = "Player", at = c("B", "W"), 
         labels = c("player1", "player2"), grid_border = "black")
 )
 ```
 
2a78c184
 ## Set heatmap body as raster image
 
bf46e913
 Saving plots in PDF format is kind of best parctice to preserve the quality. However,
2a78c184
 when there are too many rows (say, > 10000), the output PDF file size would be huge and it takes
 time and memory to read the plot. On the other hand, details of the huge matrix will not be seen
 in limited size of PDF file. Rendering heatmaps as raster images will effectively reduce
d43848ac
 the file size. In `Heatmap()` function, there are four options which control how to generate
2a78c184
 the raster image: `use_raster`, `raster_device`, `raster_quality`, `raster_device_param`. 
 
 You can choose graphic device (`png`, `jpeg` and `tiff`) by `raster_device`, control the quality of the raster image by `raster_quality`,
f6325167
 and pass further parameters for a specific device by `raster_device_param`. [Check this web page for better demonstrations.](http://zuguang.de/blog/html/d3aa6e2b289514ecddded64a467d1961.html)
2a78c184
 
5ab2cb81
 ## Session info
 
 ```{r}
 sessionInfo()
 ```