PAYATHON 2026

如何定制 UpSet 图的数字标签、科学计数法、集合配色和粗体标签?

数据小许

可以改用 ComplexUpset:通过交集标签参数调整数字显示,用 ggplot2 刻度函数设置科学计数法,通过集合查询设置配色,再单独修改矩阵面板中的集合名称字体。这些绘图组件可以分别配置,适合需要同时调整多个面板的情况。组件配置说明

先把集合列表转换为成员关系表

ComplexUpset 接收数据框:每行表示一个元素,每个集合对应一列,列值表示该元素是否属于该集合。可以继续使用 UpSetR::fromList() 转换现有的 combined,再把成员列转成逻辑值。

先运行问题中的 combined <- list(...),然后执行:

library(ggplot2)
library(ComplexUpset)

membership <- UpSetR::fromList(combined)
membership[] <- lapply(membership, as.logical)

set_order <- rev(c(
  "c0_prt", "c0_24h", "c4_12w", "c5_12w", "c7_12w"
))

set_colors <- c(
  c0_prt = "#4477AA",
  c0_24h = "#EE6677",
  c4_12w = "#228833",
  c5_12w = "#CCBB44",
  c7_12w = "#AA3377"
)

数据格式和集合排序参数可参见 ComplexUpset 数据接口。sort_sets = FALSE 会保留传入的集合顺序;如果显示方向与预期相反,调整 set_order 是否使用 rev() 即可。

定义科学计数法刻度函数

原代码定义了 fancy_scientific(),但没有将它应用到刻度上,所以图形不会因此改变。

下面的函数将有限非零值转换为 plotmath 表达式,让零直接显示为 0:

fancy_scientific <- function(x) {
  labels <- vapply(x, function(value) {
    if (is.na(value)) return("NA")
    if (!is.finite(value) || value == 0) {
      return(as.character(value))
    }

    parts <- strsplit(
      sprintf("%.2e", value), "e", fixed = TRUE
    )[[1]]

    sprintf(
      "%s %%*%% 10^{%d}",
      parts[1], as.integer(parts[2])
    )
  }, character(1))

  parse(text = labels)
}

%.2e 表示尾数保留两位小数。它只影响刻度文字,不改变计数或柱高,也不会把线性轴改成对数轴。

设置数字标签、集合颜色和粗体名称

下面的示例保留柱顶计数,缩小字号、旋转标签,并在顶部预留空间。每个集合使用一个 upset_query(set = ...),将颜色限制在集合大小柱和交集矩阵两个组件上;矩阵中的有效圆点会按对应集合着色。查询参数说明、矩阵着色实现

set_queries <- lapply(names(set_colors), function(set_name) {
  ComplexUpset::upset_query(
    set = set_name,
    color = unname(set_colors[set_name]),
    fill = unname(set_colors[set_name]),
    only_components = c(
      "overall_sizes", "intersections_matrix"
    )
  )
})

intersection_panel <- ComplexUpset::intersection_size(
  counts = TRUE,
  bar_number_threshold = 1,
  text = list(
    size = 2.6,
    angle = 90,
    hjust = 0,
    vjust = 0.5
  )
) +
  scale_y_continuous(
    labels = fancy_scientific,
    expand = expansion(mult = c(0, 0.25))
  )

p <- ComplexUpset::upset(
  membership,
  intersect = set_order,
  sort_sets = FALSE,
  sort_intersections = "descending",
  sort_intersections_by = "cardinality",
  mode = "distinct",
  name = "Dataset",
  width_ratio = 0.22,
  base_annotations = list(
    "Intersection size" = intersection_panel
  ),
  set_sizes = (
    ComplexUpset::upset_set_size() +
      scale_y_continuous(labels = fancy_scientific)
  ),
  queries = set_queries,
  themes = ComplexUpset::upset_modify_themes(list(
    "intersections_matrix" = theme(
      axis.text.y = element_text(face = "bold")
    )
  ))
)

print(p)

ggsave(
  filename = "upset_custom.png",
  plot = p,
  width = 12,
  height = 6,
  units = "in",
  dpi = 300
)

intersection_size() 的 text 参数会传给 geom_text();bar_number_threshold = 1 用于让标签保留在柱顶。集合名称位于矩阵的纵轴,因此可以修改 intersections_matrix 的 axis.text.y。交集标签参数、组件主题示例

集合大小面板在内部使用翻转坐标,所以示例对它使用 scale_y_continuous(),显示时对应横向数值轴。该面板可通过 upset_set_size() 单独配置。集合大小面板接口

数字仍然拥挤时如何调整

数字是否重叠取决于交集数量、标签长度和最终绘图宽度。示例中的字号、旋转角度和导出尺寸可以作为调整起点,但不能保证适用于所有数据。

如果希望保留全部数字,可以优先增大导出宽度。标签较短时,也可以把 angle 改成 0,同时调整对齐方式。如果顶部标签被裁切,可继续增加 expand 的上侧留白。

如果允许省略部分标签,可以在 text 列表中加入 check_overlap = TRUE。它会隐藏检测到重叠的标签,不会自动将标签移到其他位置。geom_text 重叠处理说明

代码中的科学计数法作用于轴刻度,柱顶标签仍然保留整数计数。给出的样例数值较小,整数刻度通常更容易阅读;实际计数较大时,再启用科学计数法更合适。

mode = "distinct" 表示按精确成员组合计数。例如,某根柱对应两个集合时,只统计属于这两个集合且不属于其他所选集合的元素,适合区分独有和不同共享组合的需求。示例配色可以替换为需要的颜色;同时加载 UpSetR 和 ComplexUpset 时,保留代码中的命名空间前缀,避免调用到同名的 upset()。

备注:内容仅供参考。