Tyler Rinker
Tyler Rinker

Reputation: 110054

Create heatmap with values from matrix in ggplot2

I've seen heatmaps with values made in various R graphics systems including lattice and base like this:

enter image description here

I tend to use ggplot2 a bit and would like to be able to make a heatmap with the corresponding cell values plotted. Here's the heat map and an attempt using geom_text:

library(reshape2, ggplot2)
dat <- matrix(rnorm(100, 3, 1), ncol=10)
names(dat) <- paste("X", 1:10)
dat2 <- melt(dat, id.var = "X1")
p1 <- ggplot(dat2, aes(as.factor(Var1), Var2, group=Var2)) +
    geom_tile(aes(fill = value)) +
    scale_fill_gradient(low = "white", high = "red") 
p1

#attempt
labs <- c(apply(round(dat[, -2], 1), 2, as.character))
p1 +  geom_text(aes(label=labs), size=1)

Normally I can figure out the x and y values to pass but I don't know in this case since this info isn't stored in the data set. How can I place the text on the heatmap?

Upvotes: 62

Views: 84427

Answers (3)

Yun
Yun

Reputation: 305

If you want to use ggplot2 to create a heatmap plot. I have created a package named ggalign

Specifically, it can split the heatmap into facet groups and ensure the proper alignment of the dendrogram even after faceting.

For this question, we can easily plot the heatmap and dendrogram.

library(ggalign)
#> Loading required package: ggplot2
  set.seed(10)
  dat <- matrix(rnorm(100, 3, 1), ncol = 10)
  names(dat) <- paste("X", 1:10)
  ggheatmap(dat) +
    scale_fill_viridis_c() +
    hmanno("t", size = unit(3, "cm"), free_spaces = "l") +
    align_dendro() +
    hmanno("l", size = unit(3, "cm")) +
    align_dendro(aes(color = branch), k = 3) +
    scale_x_reverse(expand = expansion())

enter image description here Created on 2024-09-04 with reprex v2.1.0 ~
~

A more complex heatmap example from the ComplexHeatmap can be created:

expr <- read_example("gene_expression.rds")
mat <- as.matrix(expr[, grep("cell", colnames(expr))])
base_mean <- rowMeans(mat)
mat_scaled <- t(apply(mat, 1, scale))
type <- gsub("s\\d+_", "", colnames(mat))

ggstack(data = mat_scaled) +
  # group stack rows into 5 groups
  align_kmeans(centers = 5L) +
  # add a block plot for each group in the stack
  ggpanel(size = unit(1, "cm")) +
  geom_tile(aes(x = 1, fill = factor(.panel))) +
  scale_fill_brewer(palette = "Dark2", name = "Kmeans group") +
  scale_x_continuous(breaks = NULL, name = NULL) +
  # add a heatmap plot in the stack
  ggheatmap() +
  hmanno(free_spaces = "l") +
  scale_y_continuous(breaks = NULL) +
  scale_fill_viridis_c() +
  # add dendrogram for this heatmap
  hmanno("t") +
  align_dendro() +
  # add a block for the heatmap column
  ggalign(data = type, size = unit(1, "cm")) +
  geom_tile(aes(y = 1, fill = factor(value))) +
  scale_y_continuous(breaks = NULL, name = NULL) +
  scale_fill_brewer(palette = "Set1", name = "type") +
  # reset the heatmap active context into the heatmap body
  hmanno() +
  # add another heatmap in the stack
  ggheatmap(base_mean) +
  # set the heatmap body width
  hmanno(width = unit(2, "cm")) +
  scale_y_continuous(breaks = NULL) +
  scale_x_continuous(name = "base mean", breaks = FALSE) +
  scale_fill_gradientn(colours = c("#2600D1FF", "white", "#EE3F3FFF")) +
  # set the active context of the heatmap to the top
  # and set the size of the top stack
  hmanno("t", size = unit(4, "cm")) +
  # add box plot in the heatmap top
  ggalign() +
  geom_boxplot(aes(y = value, fill = factor(.extra_panel))) +
  scale_x_continuous(expand = expansion(), breaks = NULL) +
  scale_fill_brewer(palette = "Dark2", guide = "none") +
  theme(axis.title.y = element_blank()) +
  # we move into the stack layout
  stack_active() +
  # add a point plot
  ggalign(data = expr$length, size = unit(2, "cm")) +
  geom_point(aes(x = value)) +
  labs(x = "length") +
  theme(
    panel.border = element_rect(fill = NA),
    axis.text.x = element_text(angle = -60, hjust = 0)
  ) +
  # add another heatmap
  ggheatmap(expr$type) +
  # set the heatmap body width, and remove the spaces in the y-axis
  hmanno(width = unit(2, "cm"), free_spaces = "y") +
  scale_fill_brewer(palette = "Set3", name = "gene type") +
  scale_x_continuous(breaks = NULL, name = "gene type") +
  # add barplot in the top annotation
  hmanno("t") +
  ggalign(limits = FALSE) +
  geom_bar(
    aes(.extra_panel, fill = factor(value)),
    position = position_fill()
  ) +
  scale_x_discrete() +
  scale_y_continuous(expand = expansion()) +
  scale_fill_brewer(palette = "Set3", name = "gene type", guide = "none") &
  theme(plot.margin = margin())

enter image description here

Upvotes: 1

Tyler Rinker
Tyler Rinker

Reputation: 110054

Key is to add a row identifier to the data and shape it "longer".

edit Dec 2022 to make code reproducible with R 4.2.2 / ggplot2 3.4.0 and reflect changes in tidyverse semantics

library(ggplot2)
library(tidyverse)
dat <- matrix(rnorm(100, 3, 1), ncol = 10)
## the matrix needs names
names(dat) <- paste("X", 1:10)

## convert to tibble, add row identifier, and shape "long"
dat2 <-
  dat %>%
  as_tibble() %>%
  rownames_to_column("Var1") %>%
  pivot_longer(-Var1, names_to = "Var2", values_to = "value") %>%
  mutate(
    Var1 = factor(Var1, levels = 1:10),
    Var2 = factor(gsub("V", "", Var2), levels = 1:10)
  )
#> Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if
#> `.name_repair` is omitted as of tibble 2.0.0.
#> ℹ Using compatibility `.name_repair`.

ggplot(dat2, aes(Var1, Var2)) +
  geom_tile(aes(fill = value)) +
  geom_text(aes(label = round(value, 1))) +
  scale_fill_gradient(low = "white", high = "red")

Created on 2022-12-31 with reprex v2.0.2

Upvotes: 118

risingStar
risingStar

Reputation: 340

There is another simpler way to make heatmaps with values. You can use pheatmap to do this.

dat <- matrix(rnorm(100, 3, 1), ncol=10)
names(dat) <- paste("X", 1:10)
install.packages('pheatmap') # if not installed already
library(pheatmap)
pheatmap(dat, display_numbers = T)

This will give you a plot like this

Heatmap with values

If you want to remove clustering and use your color scheme you can do

pheatmap(dat, display_numbers = T, color = colorRampPalette(c('white','red'))(100), cluster_rows = F, cluster_cols = F, fontsize_number = 15)

heatmap without clustering and red white colors

You can also change the fontsize, format, and color of the displayed numbers.

Upvotes: 15

Related Questions