04使用 geom_tile 绘制二维色块图

把长格式数据映射到二维网格,用填充色、边框、顺序和标签表达第三个变量。

2026-03-02
Rggplot2geom_tileHeatmapData Visualization
本章目录 · 10

geom_tile() 用矩形色块表示二维网格中的观测。xy 决定色块位置,fill 用颜色表达第三个变量,因此它适合画表达量、频数、评分和其他二维离散组合的数据。

它画出来可能像热图,但职责只是绘制色块:不会替你把矩阵转成长表,也不会自动聚类、标准化或添加复杂注释。

数据应当是什么形状

geom_tile() 最自然的输入是长格式数据,每行对应一个网格单元:

library(dplyr)
library(ggplot2)
library(tibble)

set.seed(42)

tiles <- expand_grid(
  sample = LETTERS[1:5],
  gene = paste0("G", 1:5)
) |>
  mutate(expression = runif(n(), 0, 10))

tiles

数据包含三类变量:

变量 图形角色
gene 横轴位置
sample 纵轴位置
expression 色块填充色

如果数据当前是一个行名为基因、列名为样本的矩阵,需要先整理成长格式,再交给 ggplot2。

绘制基础色块图

ggplot(tiles, aes(x = gene, y = sample, fill = expression)) +
  geom_tile()

这里的 fill = expression 放在 aes() 内,表示颜色随数据变化。若将 fill 写在 aes() 外,则所有色块使用同一种固定颜色。

设置边框与连续色标

为色块增加细边框,可以让网格边界更清楚:

ggplot(tiles, aes(gene, sample, fill = expression)) +
  geom_tile(color = "white", linewidth = 0.4) +
  scale_fill_gradient(low = "white", high = "red") +
  theme_minimal()

边框属于图层的固定外观,所以写在 aes() 外。连续数值可使用连续 fill scale;类别变量则应使用离散色标。

色标需要表达变量含义,而不只是装饰。例如以零为中心的正负变化,通常需要能区分方向的发散色标:

ggplot(tiles, aes(gene, sample, fill = expression - 5)) +
  geom_tile() +
  scale_fill_gradient2(
    low = "#3B4CC0",
    mid = "white",
    high = "#B40426",
    midpoint = 0,
    name = "Change"
  )

控制行列顺序

离散坐标轴的顺序由变量的 levels 决定。需要特定顺序时,应在数据中明确设置因子,而不是依赖字符的默认排序:

tiles <- tiles |>
  mutate(
    gene = factor(gene, levels = paste0("G", 1:5)),
    sample = factor(sample, levels = rev(LETTERS[1:5]))
  )

ggplot(tiles, aes(gene, sample, fill = expression)) +
  geom_tile()

这会同时控制坐标轴标签与色块位置。只修改数据行顺序并不能可靠改变离散坐标轴顺序。

在色块上叠加数值

网格较小时,可以使用 geom_text() 显示具体数值:

ggplot(tiles, aes(gene, sample, fill = expression)) +
  geom_tile(color = "white") +
  geom_text(aes(label = round(expression, 1)), color = "black") +
  scale_fill_gradient(low = "white", high = "red")

文字本身也是一个独立图层。label 随数据变化,所以放在 aes() 内;固定的文字颜色放在外面。

如果网格很密,逐格标注会遮挡色块并制造视觉噪声。此时应保留色标,或只标注需要强调的子集。

保持色块比例

当横纵轴都是等间距类别时,使用相同的坐标比例可以避免方格被拉成长方形:

ggplot(tiles, aes(gene, sample, fill = expression)) +
  geom_tile() +
  coord_equal()

如果横轴和纵轴本来具有不同物理单位或间距,则不应为了得到正方形而强行使用相同比例。

设置色块宽度与高度

xy 表示每个 tile 的中心位置,widthheight 控制尺寸:

ggplot(tiles, aes(gene, sample, fill = expression)) +
  geom_tile(width = 0.9, height = 0.9)

略小于默认尺寸会在色块之间留下缝隙。对于规则的离散网格,通常无需手动设置;在连续坐标或单元格间距具有含义时,才需要明确控制。

缺失组合与缺失值

geom_tile() 只绘制数据中实际存在的行。某个 gene × sample 组合没有对应记录时,图上会留下空位;记录存在但 fillNA 时,则由色标的 na.value 控制颜色。

ggplot(tiles, aes(gene, sample, fill = expression)) +
  geom_tile() +
  scale_fill_gradient(
    low = "white",
    high = "red",
    na.value = "grey80"
  )

“组合不存在”和“组合存在但值缺失”是不同的数据状态。如果所有组合理论上都应存在,可以在绘图前补全组合;否则空白可能被误读为零值。

突出部分色块

需要强调满足条件的网格时,可以叠加第二个 tile 图层:

ggplot(tiles, aes(gene, sample, fill = expression)) +
  geom_tile() +
  geom_tile(
    data = filter(tiles, expression > 8),
    fill = NA,
    color = "black",
    linewidth = 0.8
  )

第二层不重新填充颜色,只给高值色块增加边框。保留完整底图再叠加强调层,比删除其他观测更能维持上下文。

什么时候选择其他工具

geom_tile() 适合已经是长表、网格规模适中、需要继续叠加 ggplot2 图层的场景。

如果目标是带行列聚类、注释条和多个矩阵联动的表达热图,专门的热图工具更合适;如果规则网格非常大且无需分别控制色块尺寸,栅格化的绘制方式通常更高效。

关键不是图看起来是否像热图,而是数据是否已经形成一个由 xyfill 定义的二维网格,以及后续是否需要使用 ggplot2 的图层语法继续组合。