---
title: "Heatmap Layout"
output: 
  bookdown::html_document2:
    base_format: rmarkdown::html_vignette
    number_sections: no
    fig_caption: yes
    fig_width: 7
    fig_height: 5
params:
  mode: !r if (endsWith(as.character(desc::desc_get_version()), '9000')) 'devel' else 'release'
  release: "https://yunuuuu.github.io/ggalign"
  devel: "https://yunuuuu.github.io/ggalign/dev"
vignette: >
  %\VignetteIndexEntry{Heatmap Layout}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
pkgdown:
  as_is: true
---

```{r, include = FALSE}
knitr::opts_chunk$set(
    collapse = TRUE,
    comment = "#>"
)
```

```{r echo=FALSE}
mode <- params$mode
url <- params[[mode]]
```

The `heatmap_layout()`/`ggheatmap()` function provides a powerful way to create
customizable heatmaps in R using `ggplot2`. This vignette will guide you through
its usage.

# input data
The data input can be a numeric or character vector, a data frame, and any other
data which can be converted into a matrix by `fortify_matrix()`.

```{r setup_data}
set.seed(123)
small_mat <- matrix(rnorm(81), nrow = 9)
rownames(small_mat) <- paste0("row", seq_len(nrow(small_mat)))
colnames(small_mat) <- paste0("column", seq_len(ncol(small_mat)))
```

```{r matrix}
library(ggalign)
ggheatmap(small_mat)
```

# heatmap body
For `ggplot2` usage, the matrix input will be converted into a long formated
data frame when drawing. The default mapping will use `aes(.data$.x, .data$.y)`,
but can be controlled using `mapping` argument. The data in the underlying
ggplot object contains following columns:

  - `.xpanel` and `.ypanel`: the column and row panel

  - `.x` and `.y`: the `x` and `y` coordinates

  - `.row_names` and `.column_names`: A factor of the row and column names of
    the original matrix (only applicable when names exist).

  - `.row_index` and `.column_index`: the row and column index of the original
    matrix.

  - `value`: the actual matrix value.

You can treat the `ggheatmap()`/`heatmap_layout()` object as a standard
`ggplot2` object with default mapping and data. This means you can add `ggplot2`
layers or elements just like in any `ggplot` object.
```{r}
ggheatmap(small_mat) + geom_point() + scale_fill_viridis_c()
```

By default, `ggheatmap()`/`heatmap_layout()` adds a heatmap layer. If the matrix
has more than 20,000 cells (`nrow * ncol > 20000`), it uses `geom_raster()` for
performance efficiency; for smaller matrices, `geom_tile()` is used. You can
explicitly choose the layer by providing a single string (`"raster"` or
`"tile"`) in the `filling` argument.
```{r}
ggheatmap(small_mat, filling = "raster")
```

```{r}
ggheatmap(small_mat, filling = "tile")
```

If you set `filling = NULL`, a blank heatmap will be drawn, allowing for
customized filling geoms. In this way, you must set `fill` mapping manually.
```{r}
ggheatmap(small_mat, filling = NULL) +
    geom_tile(aes(fill = value), color = "black", width = 0.9, height = 0.9)
```

A heatmap pie charts:
```{r}
set.seed(123)
ggheatmap(matrix(runif(360L), nrow = 20L), filling = NULL) +
    geom_pie(aes(angle = value * 360, fill = value))
```

If you want more complex customization of pie charts, try using
`ggforce::geom_arc_bar()` instead.

You can use external packages like
[ggrastr](https://github.com/VPetukhov/ggrastr) or
[ggfx](https://ggfx.data-imaginist.com/reference/with_raster.html) to rasterize
the heatmap body. Additionally,
[ggfx](https://ggfx.data-imaginist.com/reference/with_raster.html) offers many
image filters that can be applied to ggplot2 layers.
```{r eval=rlang::is_installed("ragg")}
ggheatmap(small_mat, filling = FALSE) +
    ggrastr::rasterise(geom_tile(aes(fill = value)), dev = "ragg")
```

You can rasterize all plots in the layout directly with `ggrastr::rasterise()`.
This method works for both `ggheatmap()`/`quad_layout()` and `ggstack()`
objects.
```{r eval=rlang::is_installed("ragg")}
ggrastr::rasterise(ggheatmap(small_mat), dev = "ragg")
```

# heatmap annotations
A heatmap typically has two observation axes (rows and columns), which can be
reordered or supplemented with additional information. In `ggheatmap()`,
annotations are handled by a `stack_layout()` object, specifically
`stack_align()`, which aligns the observations. This object can hold multiple
plots and can be positioned at the top, left, bottom, or right of the heatmap. 

Note that `ggalign` use the concept of `"number of observations"` in the
[vctrs](https://vctrs.r-lib.org/reference/vec_size.html) package or `NROW()`
function. When aligning the observations, you must ensure the number of
observations is equal. For column annotations, the layout matrix will be
transposed before use. This is necessary because column annotations use heatmap
columns as observations, but we need rows.

## `quad_anno()`
By default, `heatmap_layout()`/`ggheatmap()` do not initialize an active
context, which means that all additions are directed within the heatmap body.
You can use `quad_anno()` to set the active context, directing all subsequent
additions to the specified annotation position. The `quad_anno()` function has
the following aliases:

  - `anno_top`: A special case of `quad_anno` with `position = "top"`.
  - `anno_left`: A special case of `quad_anno` with `position = "left"`.
  - `anno_bottom`: A special case of `quad_anno` with `position = "bottom"`.
  - `anno_right`: A special case of `quad_anno` with `position = "right"`.

The annotations allows for custom layout adjustments and the addition of various
plot types. In the following example, `align_kmeans()` is used to group the
columns into three panels. It doesn't matter if this is added to the top or
bottom since it won't add a plot area: 

```{r}
ggheatmap(small_mat) +
    # we set the active context to the top annotation
    anno_top() +
    # we split the observations into 3 groups by kmeans
    align_kmeans(3L)
```

We can add any `align_*()` function to the annotation. For more details on
`align_*()` functions, refer to
[layout-customize](`r sprintf("%s/articles/layout-customize.html", url)`) and
[layout-plot](`r sprintf("%s/articles/layout-plot.html", url)`).

```{r}
ggheatmap(small_mat) +
    # in the heatmap body, we set the axis text theme
    theme(axis.text.x = element_text(angle = -60, hjust = 0)) +
    # we set the active context to the right annotation
    anno_right() +
    # in the right annotation, we add a dendrogram
    align_dendro(k = 3L) +
    # in the dendrogram, we add a point layer
    geom_point(aes(color = factor(branch)))
```

In this example:

 - `anno_right()` change the active context to the right of the heatmap.
 - `align_dendro(k = 3L)` adds a dendrogram to this right-side annotation
 context and sets itself as the active plot in the annotation stack. 
 - `geom_point(aes(color = factor(branch)))` is then added to this active plot
 within the annotation stack, here, it means the `align_dendro()` plot.

## `quad_active()`
To remove the active context and redirect additions back to the heatmap body,
you can use `quad_active()`. 
```{r}
ggheatmap(small_mat) +
    # we set the active context to the top annotation
    anno_top() +
    # we split the observations into 3 groups by kmeans
    align_kmeans(3L) +
    # remove any active annotation
    quad_active() +
    # set fill color scale for the heatmap body
    scale_fill_viridis_c()
```

## `quad_switch()`/`hmanno()`
We also provide `quad_switch()`/`hmanno()` (heatmap annotation) which integrates
`quad_active()` and `quad_anno()` into one function for ease of use. Feel free
to use any of these functions to streamline your annotation process.
```{r}
ggheatmap(small_mat) +
    # we set the active context to the top annotation
    quad_switch("t") +
    # we split the observations into 3 groups by kmeans
    align_kmeans(3L) +
    # remove any active annotation
    quad_switch() +
    # set fill color scale for the heatmap body
    scale_fill_viridis_c()
```

```{r}
ggheatmap(small_mat) +
    # we set the active context to the top annotation
    hmanno("t") +
    # we split the observations into 3 groups by kmeans
    align_kmeans(3L) +
    # remove any active annotation
    hmanno() +
    # set fill color scale for the heatmap body
    scale_fill_viridis_c()
```

You may wonder about the prefix `quad_`. The `ggheatmap()` function is a
specialized version of `quad_layout()`, designed to simplify the creation of
heatmap plots by integrating essential elements for a standard heatmap layout.
Both `quad_anno()` and `quad_active()` are functions designed for
`quad_layout()`. 

# Plot Size
## Heatmap Body Size
You can specify the relative sizes of the heatmap body using the `width` and
`height` arguments in the `ggheatmap()` function.
```{r}
ggheatmap(small_mat, height = 2) +
    scale_fill_viridis_c() +
    anno_top() +
    align_dendro()
```

Alternatively, the `quad_active()` function allows you to control the heatmap
body sizes. 
```{r}
ggheatmap(small_mat) +
    quad_active(height = 2) +
    scale_fill_viridis_c() +
    anno_top() +
    align_dendro()
```

## Annotation Stack Size
The `quad_anno()` function allows you to control the total annotation stack
size. The `size` argument controls the relative width (for left and right
annotations) or height (for top and bottom annotations) of the whole annotation
stack. 

```{r}
ggheatmap(small_mat) +
    scale_fill_viridis_c() +
    anno_top(size = 1) +
    align_dendro()
```

You can also specify it as an absolute size using `unit()`:
```{r}
ggheatmap(small_mat) +
    scale_fill_viridis_c() +
    anno_top(size = unit(30, "mm")) +
    align_dendro()
```

## Single Plot Size
All `align_*()`/`ggfree()` functions that add plots in the annotation stack have
a `size` argument to control the relative `width` (for left and right
annotations) or `height` (for top and bottom annotations) of the single plot in
the annotation stack.
```{r}
ggheatmap(small_mat) +
    scale_fill_viridis_c() +
    anno_left(size = 0.2) +
    ggalign(data = rowSums, aes(x = value), size = unit(10, "mm")) +
    geom_bar(
        aes(y = .y, fill = factor(.y)),
        stat = "identity", orientation = "y"
    ) +
    scale_fill_brewer(palette = "Set1", guide = "none")
```

# Session information
```{r}
sessionInfo()
```
