如何在ggplot2箱线图中用不同形状区分数据点且避免重叠
问题描述
我有一个存储为prescriptivism_scores.csv的数据集,结构如下:
| Usage_Guide | Usage_Problem | Prescriptivism_Index |
|---|---|---|
| Book 1 | who/whom | 2 |
| Book 2 | who/whom | 2 |
| Book 3 | who/whom | 2.5 |
| Book 4 | who/whom | 4 |
| Book 5 | who/whom | 2 |
| Book 6 | who/whom | 1.5 |
| Book 7 | who/whom | 3 |
| Book 8 | who/whom | 2 |
| Book 9 | who/whom | 4 |
| Book 10 | who/whom | 4 |
| Book 11 | who/whom | 2 |
我使用以下代码生成了一个无数据点重叠的箱线图:
library(ggplot2) df <- read.csv(file = 'prescriptivism_scores.csv') ggplot(df, aes(y = Prescriptivism_Index, x = Usage_Problem)) + geom_boxplot(color = "#838383") + geom_dotplot(binaxis = "y", stackdir = "center", dotsize = 0.5, color = "#00853E", fill = "#C4D600", stackratio = 1.5) + scale_x_discrete(name = "Usage Problem", breaks = c("different_to_than_from", "I_for_me", "lay_lie", "less_fewer", "none", "singular_they", "split_infinitive", "who_whom"), labels = c("DIFF TO/THAN/FROM", "I FOR ME", "LAY/LIE", "LESS/FEWER", "NONE", "SG THEY", "SPLIT INF", "WHO/WHOM")) + ylab("Prescriptivism Index") + stat_summary(fun.y = mean, geom="point", shape=3, size=3, color="#EF4B81") + theme(panel.background = element_blank()) + geom_hline(yintercept = 1, linetype = "dashed", linewidth = 0.25, color = "darkgray") + geom_hline(yintercept = 2, linetype = "dashed", linewidth = 0.25, color = "darkgray") + geom_hline(yintercept = 3, linetype = "dashed", linewidth = 0.25, color = "darkgray") + geom_hline(yintercept = 4, linetype = "dashed", linewidth = 0.25, color = "darkgray")
我对该箱线图的整体效果满意,但希望每个数据点以不同形状对应数据集中Usage_Guide列的不同书籍,以便明确各数据点所属书籍。我尝试在ggplot的aes()中添加shape = Usage_Guide,但数据点未改变形状,且出现重叠,箱线图也显示异常;尝试颜色映射时,要么数据点重叠,要么箱线图消失。请问如何在保持原箱线图样式的前提下,实现用不同形状区分对应不同书籍的数据点?
解决方案
核心问题在于geom_dotplot本身不支持按分组(如Usage_Guide)设置不同形状,且直接在全局aes中添加shape=Usage_Guide会让箱线图也按书籍分组,导致样式异常。我们可以改用geom_jitter配合位置调整来替代geom_dotplot,既保留无重叠的布局,又能实现形状映射:
修改后的代码
library(ggplot2) df <- read.csv(file = 'prescriptivism_scores.csv') ggplot(df, aes(y = Prescriptivism_Index, x = Usage_Problem)) + # 保留原箱线图样式,避免分组影响 geom_boxplot(color = "#838383", width = 0.6) + # 用geom_jitter替代dotplot,添加形状映射,控制位置避免重叠 geom_jitter(aes(shape = Usage_Guide), width = 0.2, # 控制x方向分散程度,避免重叠 size = 2, # 点大小 color = "#00853E", fill = "#C4D600") + # 自定义形状映射,解决默认形状数量不足的问题 scale_shape_manual(values = c(1,2,3,4,5,6,7,8,9,10,11)) + scale_x_discrete(name = "Usage Problem", breaks = c("different_to_than_from", "I_for_me", "lay_lie", "less_fewer", "none", "singular_they", "split_infinitive", "who_whom"), labels = c("DIFF TO/THAN/FROM", "I FOR ME", "LAY/LIE", "LESS/FEWER", "NONE", "SG THEY", "SPLIT INF", "WHO/WHOM")) + ylab("Prescriptivism Index") + stat_summary(fun.y = mean, geom="point", shape=3, size=3, color="#EF4B81") + theme(panel.background = element_blank(), # 调整图例位置避免遮挡,可选 legend.position = "right") + geom_hline(yintercept = 1, linetype = "dashed", linewidth = 0.25, color = "darkgray") + geom_hline(yintercept = 2, linetype = "dashed", linewidth = 0.25, color = "darkgray") + geom_hline(yintercept = 3, linetype = "dashed", linewidth = 0.25, color = "darkgray") + geom_hline(yintercept = 4, linetype = "dashed", linewidth = 0.25, color = "darkgray")
关键说明
- 替换
geom_dotplot为geom_jitter:geom_dotplot基于分箱堆叠展示点,不支持分组形状映射;geom_jitter通过x轴方向的随机偏移避免点重叠,同时支持形状映射。 - 局部
aes添加形状映射:在geom_jitter的aes中单独设置shape=Usage_Guide,不会影响箱线图的分组逻辑,保证原箱线图样式不变。 - 自定义形状值:由于书籍数量(11本)超过ggplot默认提供的形状数量(8个),用
scale_shape_manual手动指定足够的形状值,避免重复。 - 调整
width参数:控制x方向的偏移范围,确保同y值的点分散开不重叠,同时不会偏离对应箱线图的位置太远。
如果仍想保留类似geom_dotplot的堆叠效果,可以使用geom_point配合position_dodge或position_stack,但geom_jitter是实现无重叠+形状区分的最简便方案。
内容的提问来源于stack exchange,提问作者Jordan Smith
相关产品推荐
相关产品推荐

