PAYATHON 2026

ggrepel 如何让标签避开 ggplot2 多图层中的点和标签?

数据小许

结论

geom_text_repel() 只使用当前图层的数据计算避让。其他 geom_point() 或 geom_label() 图层不会自动参与碰撞计算。因此,把 site_scores、sp_scores 和质心分别放在其他图层中绘制,无法让物种标签自动避开这些点和标签。

可以将需要参与避让的点位和标签合并到同一份数据中。需要显示的物种保留标签文本,其他物种得分和站点得分的 label 设为空字符串 ""。ggrepel 会隐藏空标签,但这些数据点仍会参与非空标签的避让计算。geom_text_repel() 文档示例和示例说明展示了这种用法。

合并避让数据

下面假设 sp_scores2 是从 sp_scores 中挑出的、需要显示标签的物种,且两张表可以通过 species 对应。centroids_tree 按题目提供的 dput() 使用 group 列作为标签;如果实际列名是 tree_species,请相应替换。

library(dplyr)
library(ggrepel)

# 所有物种得分都参与避让;只为 sp_scores2 中的物种显示标签
species_repel <- sp_scores |>
  select(CCA1, CCA2, species) |>
  left_join(
    sp_scores2 |> select(species, label),
    by = "species"
  ) |>
  mutate(label = coalesce(label, "")) |>
  select(CCA1, CCA2, label)

# 站点点位参与避让,但不显示文字
sites_repel <- site_scores |>
  transmute(CCA1, CCA2, label = "")

# 若物种标签也要避开质心标签,把质心文字放进同一个 repel 图层
centroids_repel <- centroids_tree |>
  transmute(CCA1, CCA2, label = group)

repel_data <- bind_rows(
  species_repel,
  sites_repel,
  centroids_repel
)

将原来的独立 geom_text_repel() 和 geom_label() 合并为一个使用这份数据的 repel 图层:

ggplot() +
  geom_point(
    data = site_scores,
    aes(x = CCA1, y = CCA2, colour = tree_species),
    size = 6,
    alpha = 0.8
  ) +
  geom_point(
    data = sp_scores,
    aes(x = CCA1, y = CCA2),
    shape = 15,
    size = 6,
    alpha = 0.8
  ) +
  geom_text_repel(
    data = repel_data,
    aes(x = CCA1, y = CCA2, label = label),
    inherit.aes = FALSE,
    size = 5,
    fontface = "bold",
    box.padding = 0.25,
    point.padding = 0.4,
    force = 1,
    max.overlaps = Inf
  )

这样,站点和物种得分会参与点位避让,物种标签与质心文字也会在同一层内互相避让。示例使用 geom_text_repel() 绘制质心文字,因此不会保留原先 geom_label() 的标签框效果。若要保留标签框,可以把合并数据用于 geom_label_repel();它会为所有非空标签绘制框。

注意事项

  • 空标签应设为 ""。如果设为 NA,数据可能会被当作缺失值移除,无法让这些点参与避让。
  • max.overlaps = Inf 可以避免标签因重叠过多而被丢弃,但不能保证所有标签都完全避开点或其他标签。空间不足时,标签仍可能重叠。
  • point.padding 控制标签与对应点之间的间距,box.padding 控制标签之间的间距。可根据图形尺寸调整,并检查不同输出尺寸下的效果。
  • 题目中的 dput(centroids_tree) 显示列名是 group,示例绘图代码使用的则是 tree_species。请确认实际数据列名一致,否则 aes(label = tree_species) 会找不到对应列。

备注:内容仅供参考。