PAYATHON 2026

如何使用 cvms 可视化 confusion matrix 中的 FPR 和 TPR?

技术老齐

建议用 ggplot2 画分面热图,直接比较这 16 个 FPR、TPR 数值。现有的 test 只有汇总指标,无法还原包含 TP、FP、TN、FN 计数的混淆矩阵。

如果对每种变异类型分别做二分类评估,就有 2 种策略 × 2 种测序技术 × 2 种变异类型 = 8 个评估组合,对应 8 个混淆矩阵。这些矩阵可以合并到一张图中,不必分别生成 8 张图片。

FPR、TPR 与混淆矩阵的区别

假设正类表示“存在目标类型的变异”,混淆矩阵如下:

真实类别 \ 预测类别阳性阴性
阳性TPFN
阴性FPTN

FPR 和 TPR 的计算公式为:

[ \mathrm{TPR}=\frac{TP}{TP+FN},\qquad \mathrm{FPR}=\frac{FP}{FP+TN} ]

TPR 是召回率,也称 sensitivity;FPR 是假阳性率。两者的分母不同,因此不需要相加等于 1。

仅凭这两个比例,无法确定各格的实际计数。还需要真实阳性总数 (P) 和真实阴性总数 (N),才能计算:

[ TP=\mathrm{TPR}\times P,\qquad FN=(1-\mathrm{TPR})\times P ]

[ FP=\mathrm{FPR}\times N,\qquad TN=(1-\mathrm{FPR})\times N ]

如果比例已经四舍五入,反推的计数可能有误差,应优先使用原始计数。

用四个分面展示现有数据

可以将 SNV、INDEL 分成两行,将 FPR、TPR 分成两列。每个分面内,横轴区分 linear、graph,纵轴区分 PacBio、Illumina。

这样,一张图就能展示全部结果:四个面板,每个面板包含四个色块。这是指标热图,横纵轴表示实验配置。

运行题目中创建 test 的代码后,执行:

library(ggplot2)

plot_data <- test

plot_data$Strategy <- factor(
  plot_data$Strategy,
  levels = c("linear", "graph")
)

plot_data$Approach <- factor(
  plot_data$Approach,
  levels = c("Illumina", "PacBio")
)

plot_data$Type <- factor(
  plot_data$Type,
  levels = c("snv", "indel"),
  labels = c("SNV", "INDEL")
)

plot_data$Predictor <- factor(
  plot_data$Predictor,
  levels = c("FPR", "TPR")
)

ggplot(
  plot_data,
  aes(x = Strategy, y = Approach, fill = Value)
) +
  geom_tile(colour = "white", linewidth = 0.8) +
  geom_text(
    aes(label = sprintf("%.1f%%", 100 * Value)),
    colour = "black",
    size = 4
  ) +
  facet_grid(
    Type ~ Predictor,
    labeller = labeller(
      Predictor = c(
        FPR = "FPR(越低越好)",
        TPR = "TPR(越高越好)"
      )
    )
  ) +
  scale_fill_gradient(
    low = "#f7fbff",
    high = "#6baed6",
    limits = c(0, 1),
    labels = scales::label_percent(accuracy = 1),
    name = "指标值"
  ) +
  coord_equal() +
  labs(
    x = "策略",
    y = "测序技术",
    caption = "颜色表示数值大小;FPR 越低越好,TPR 越高越好。"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    panel.grid = element_blank(),
    strip.text = element_text(face = "bold")
  )

所有分面共用 0–1 的色标,便于比较数值。颜色越深表示数值越大:TPR 越大通常越好,FPR 越大则越差。

例如,在提供的 PacBio SNV 数据中,graph 的 FPR 更低,TPR 也更高。热图中的数值标注可以直接呈现这两个差异:

策略FPRTPR
linear39.0%75.0%
graph33.5%86.5%

用四宫格展示归一化混淆矩阵

如果需要四宫格形式,且 FPR、TPR 符合上述定义,可以构造按真实类别归一化的矩阵:

真实类别 \ 预测类别阳性阴性
阳性TPR(1-\mathrm{TPR})
阴性FPR(1-\mathrm{FPR})

每行之和为 1。这种图能展示检出、漏检、误报和正确排除的比例,但无法展示样本数量或类别占比。

以下代码为每个评估组合生成一个四宫格,将 8 个四宫格放在同一张图中:

library(tidyr)
library(ggplot2)

rates <- pivot_wider(
  test,
  id_cols = c(Approach, Strategy, Type),
  names_from = Predictor,
  values_from = Value
)

normalized_cm <- rbind(
  transform(
    rates,
    Truth = "Positive",
    Prediction = "Positive",
    Rate = TPR
  ),
  transform(
    rates,
    Truth = "Positive",
    Prediction = "Negative",
    Rate = 1 - TPR
  ),
  transform(
    rates,
    Truth = "Negative",
    Prediction = "Positive",
    Rate = FPR
  ),
  transform(
    rates,
    Truth = "Negative",
    Prediction = "Negative",
    Rate = 1 - FPR
  )
)

normalized_cm$Truth <- factor(
  normalized_cm$Truth,
  levels = c("Negative", "Positive")
)

normalized_cm$Prediction <- factor(
  normalized_cm$Prediction,
  levels = c("Positive", "Negative")
)

normalized_cm$Type <- factor(
  normalized_cm$Type,
  levels = c("snv", "indel"),
  labels = c("SNV", "INDEL")
)

ggplot(
  normalized_cm,
  aes(x = Prediction, y = Truth, fill = Rate)
) +
  geom_tile(colour = "white", linewidth = 0.8) +
  geom_text(
    aes(label = sprintf("%.1f%%", 100 * Rate)),
    size = 3.5
  ) +
  facet_grid(Approach + Strategy ~ Type) +
  scale_fill_gradient(
    low = "#f7fbff",
    high = "#6baed6",
    limits = c(0, 1),
    labels = scales::label_percent(accuracy = 1),
    name = "行内比例"
  ) +
  coord_equal() +
  labs(
    x = "预测类别",
    y = "真实类别",
    caption = "按真实类别归一化,每行合计 100%;色块不表示计数。"
  ) +
  theme_minimal(base_size = 12) +
  theme(panel.grid = element_blank())

这里对 SNV 和 INDEL 分别评估。如果将二者定义为互斥类别,要构造多分类混淆矩阵,还需要它们之间的误分类计数。当前数据没有这些信息。

什么时候使用 cvms

拿到每组实际的 TP、FN、FP、TN 后,可以使用 cvms::plot_confusion_matrix() 绘图。每组输入整理为三列:真实类别、预测类别和对应计数。

输入结构如下,运行前需将 TP、FN、FP、TN 赋为该组的实际计数:

cm_counts <- data.frame(
  Target = factor(
    c("Positive", "Positive", "Negative", "Negative"),
    levels = c("Positive", "Negative")
  ),
  Prediction = factor(
    c("Positive", "Negative", "Positive", "Negative"),
    levels = c("Positive", "Negative")
  ),
  N = c(TP, FN, FP, TN)
)

cvms::plot_confusion_matrix(
  cm_counts,
  target_col = "Target",
  prediction_col = "Prediction",
  counts_col = "N"
)

计数列应使用实际计数,不能直接填入 FPR、TPR,也不能随意假设样本总数来推算。现有指标表用 ggplot2 就能展示,分面也能清楚表达分组关系,无须使用嵌套色块。

绘图前确认指标定义

评估变异检测结果时,要特别确认 FPR 的分母。如果实际计算的是:

[ \frac{FP}{TP+FP} ]

这个指标是 FDR(false discovery rate),即 (1-\mathrm{precision}),不能标为 FPR。还需明确负类的定义,并确认不同方法是否使用一致的评估区域和匹配规则,否则图中的数值未必能直接比较。

如果想比较 FPR 与 TPR 的取舍,也可以按 SNV、INDEL 分面,画横轴为 FPR、纵轴为 TPR 的散点图,用颜色和形状区分策略与测序技术。当前每个组合只有一个点,只能展示工作点;要画 ROC 曲线,还需要多个阈值下的 FPR、TPR。

备注:内容仅供参考。