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

使用geom_dotplot()实现点图分栏:单个x值点数超限时自动避让

实现ggplot2点图的分栏避让(单x值下限制每栏点数)

问题背景

现有如下R语言数据框:

DF <- structure(list(pub_year = c(2022, 2023, 2022, 2022, 2021, 2023, 2019, 
                                  2022, 2022, 2022, 2022, 2023, 2019, 2020, 
                                  2020, 2021, 2021, 2019, 2021, 2023), 
                     mega = c(1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 
                              0, 1, 1, 1, 1, 0, 1)), 
                 row.names = c(NA, -20L), 
                 class = c("tbl_df", "tbl", "data.frame"))

使用给定的ggplot2代码绘制点图时,部分x值(如2022)对应点数较多(当前为7个),希望设置单个x值下最多显示5个点为一栏,多余的点分栏避让(如分为5个点的栏和2个点的栏),达到目标图的效果。

当前绘图代码如下:

library(tidyverse)
library(ggExtra)

yHeight <- 10
binwidth <- 0.5
dotsize <- 0.5

DF %>%
  ggplot(aes(x = reorder(pub_year, -pub_year), fill = factor(mega))) +
  geom_dotplot(method = "histodot", dotsize = dotsize,
               binwidth = binwidth, stackgroups = T,
               binpositions = "all",
               stackdir = "up", stackratio = 1) +
  scale_fill_manual(values = c("#8FBEE5", "#DEE7EC"),
                    name = "TEST",
                    labels = c("≤ 40", "> 40")) +
  scale_y_continuous(limits=c(0, 1), expand = c(0, 0), breaks = seq(0, 1, 1/yHeight), labels = seq(0, yHeight)) +
  theme_minimal() +
  theme(axis.ticks = element_blank(),
        axis.title = element_blank(),
        #axis.text.y = element_blank(),
        axis.line = element_blank(),
        legend.position = "right",
        panel.grid.minor = element_blank()) +
  coord_fixed(ratio=binwidth*dotsize*yHeight) +
  ggExtra::removeGridX()

实现方法

geom_dotplot本身不支持直接设置单栏最大点数,我们可以通过预处理数据,给每个x分组内的点添加分栏偏移量,让多余的点在x轴方向偏移显示,模拟分栏效果。具体步骤如下:

  1. 数据预处理:按pub_year和mega分组,给每个点分配组内序号,再根据每栏最大点数(这里设为5)计算分栏数,最后生成x轴的偏移值。
  2. 调整绘图映射:将原本的x变量替换为带偏移的新变量,同时保持x轴标签不变。
  3. 调整坐标轴和主题:确保x轴显示原始年份,不显示偏移后的数值。

修改后的完整代码

library(tidyverse)
library(ggExtra)

yHeight <- 10
binwidth <- 0.5
dotsize <- 0.5
max_per_col <- 5  # 设置每栏最大点数

# 预处理数据:添加分栏偏移
DF_processed <- DF %>%
  mutate(pub_year = factor(pub_year, levels = sort(unique(pub_year), decreasing = TRUE))) %>%
  group_by(pub_year, mega) %>%
  mutate(
    idx = row_number(),
    col = (idx - 1) %/% max_per_col,  # 计算分栏编号
    x_offset = as.numeric(pub_year) + col * binwidth * 1.1  # 计算x轴偏移,1.1是栏间距系数,可调整
  ) %>%
  ungroup()

# 绘制分栏点图
DF_processed %>%
  ggplot(aes(x = x_offset, fill = factor(mega))) +
  geom_dotplot(method = "histodot", dotsize = dotsize,
               binwidth = binwidth, stackgroups = TRUE,
               binpositions = "all",
               stackdir = "up", stackratio = 1) +
  scale_fill_manual(values = c("#8FBEE5", "#DEE7EC"),
                    name = "TEST",
                    labels = c("≤ 40", "> 40")) +
  scale_x_continuous(
    breaks = as.numeric(unique(DF_processed$pub_year)),
    labels = unique(DF_processed$pub_year)
  ) +
  scale_y_continuous(limits=c(0, 1), expand = c(0, 0), breaks = seq(0, 1, 1/yHeight), labels = seq(0, yHeight)) +
  theme_minimal() +
  theme(axis.ticks = element_blank(),
        axis.title = element_blank(),
        axis.line = element_blank(),
        legend.position = "right",
        panel.grid.minor = element_blank()) +
  coord_fixed(ratio=binwidth*dotsize*yHeight) +
  ggExtra::removeGridX()

代码说明

  • 数据预处理部分:
    • 先把pub_year转为因子,确保排序正确;
    • idx是每组内的点序号,col是该点所属的分栏;
    • x_offset是原始x值加上分栏偏移,偏移量用binwidth * 1.1保证栏与栏之间有间隙,可根据需求调整系数。
  • 绘图部分:
    • x轴映射改为x_offset,但通过scale_x_continuous设置breaks和labels为原始年份,保证坐标轴显示正确;
    • 其余参数保持和原代码一致,确保点的样式、颜色、y轴刻度等不变。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 01:46:12