You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用ggplot和geom_text_repel按象限随机且比例化标注?

解决方案:mtcars四象限图的比例化随机标注

核心思路

先按mpg和drat的中位数划分四象限,再根据各象限数据点数量占比分配标注名额,随机抽取样本后用ggrepel的标注功能避免拥挤。

完整代码实现

library(ggplot2)
library(ggrepel)
library(dplyr)

# 1. 预处理数据:划分象限 + 计算象限占比
mtcars_quad <- mtcars %>%
  mutate(
    # 添加行名作为标注文本
    car = rownames(.),
    # 计算中位数作为象限分割线
    mpg_med = median(mpg),
    drat_med = median(drat),
    # 标记象限:Q1(右上), Q2(左上), Q3(左下), Q4(右下)
    quadrant = case_when(
      mpg > mpg_med & drat > drat_med ~ "Q1",
      mpg < mpg_med & drat > drat_med ~ "Q2",
      mpg < mpg_med & drat < drat_med ~ "Q3",
      mpg > mpg_med & drat < drat_med ~ "Q4",
      TRUE ~ "On Line" # 处理刚好在中位数线上的点
    )
  ) %>%
  filter(quadrant != "On Line") # 去掉线上点,也可根据需求保留

# 2. 按比例确定各象限标注数量
total_labels <- 10 # 设定总标注数,可自行调整
quad_counts <- mtcars_quad %>% count(quadrant) %>% mutate(prop = n / sum(n))
quad_labels <- quad_counts %>% mutate(num_label = round(prop * total_labels))

# 处理四舍五入后总数偏差的情况(比如总和不等于total_labels)
label_diff <- total_labels - sum(quad_labels$num_label)
if(label_diff != 0){
  # 给占比最大的象限调整数量
  quad_labels <- quad_labels %>%
    mutate(num_label = ifelse(prop == max(prop), num_label + label_diff, num_label))
}

# 3. 按象限随机抽取标注样本
mtcars_labeled <- mtcars_quad %>%
  left_join(quad_labels, by = "quadrant") %>%
  group_by(quadrant) %>%
  sample_n(size = num_label) %>%
  ungroup()

# 4. 绘制四象限图 + 比例化标注
ggplot(mtcars_quad, aes(x = mpg, y = drat)) +
  # 绘制象限分割线
  geom_vline(xintercept = median(mtcars$mpg), linetype = "dashed", color = "gray50") +
  geom_hline(yintercept = median(mtcars$drat), linetype = "dashed", color = "gray50") +
  # 绘制所有数据点
  geom_point(color = "steelblue", alpha = 0.7) +
  # 只标注抽样的点,用repel避免拥挤
  geom_text_repel(
    data = mtcars_labeled,
    aes(label = car),
    size = 3.5,
    box.padding = 0.5, # 增加文本与点的距离
    point.padding = 0.3,
    max.overlaps = Inf # 关闭重叠限制,因为已经抽样控制数量了
  ) +
  # 标注象限名称
  annotate("text", x = Inf, y = Inf, label = "Q1", hjust = 1.2, vjust = 1.2, size = 4) +
  annotate("text", x = -Inf, y = Inf, label = "Q2", hjust = -0.2, vjust = 1.2, size = 4) +
  annotate("text", x = -Inf, y = -Inf, label = "Q3", hjust = -0.2, vjust = -0.2, size = 4) +
  annotate("text", x = Inf, y = -Inf, label = "Q4", hjust = 1.2, vjust = -0.2, size = 4) +
  theme_minimal()

关键细节说明

  • 象限划分:用中位数作为分割线,保证四个象限的划分更贴合数据分布,也可以换成均值或自定义阈值。
  • 比例抽样:通过计算各象限数据点占比分配标注名额,解决了“点数越多标注越多”的需求;同时处理了四舍五入后的总数偏差,保证总标注数符合设定值。
  • 避免拥挤:先通过抽样控制标注数量,再用geom_text_repel的box.padding和point.padding参数调整文本与点、文本之间的距离,比单纯调max.overlaps更可控。
  • 灵活性:可以通过修改total_labels调整总标注数,也可以在抽样时加入weight参数(比如按点的离散程度加权),进一步优化标注分布。

内容的提问来源于stack exchange,提问作者Maul Seil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 08:35:56