如何为散点图的X、Y轴添加双层标签(ggplot2实现)
实现ggplot2坐标轴双层标签解决方案
需求背景
现有如下DataFrame df:
products price_index quantity_index price_rank quantity_rank 1 a 95.00000 95.00000 high price high volume 2 b 80.69012 94.53585 high price high volume 3 c 74.41048 90.95081 high price high volume 4 d 73.24853 89.82692 medium price medium volume 5 e 70.50442 79.71225 medium price medium volume 6 f 67.11609 75.43498 low price low volume 7 g 64.14685 58.26419 low price low volume 8 h 56.76375 56.16531 low price low volume 9 i 55.76472 56.02838 low price low volume 10 j 55.70475 50.24873 low price low volume
数据结构定义:
df <- structure(list(products = c("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), price_index = c(95, 80.69011538, 74.41047705, 73.24853055, 70.5044217, 67.1160916, 64.14685495, 56.76375355, 55.76472446, 55.70475052), quantity_index = c(95, 94.53585227, 90.95080999, 89.82692004, 79.71224701, 75.43498354, 58.26419203, 56.16530529, 56.02838119, 50.24873055), price_rank = c("high price", "high price", "high price", "medium price", "medium price", "low price", "low price", "low price", "low price", "low price"), quantity_rank = c("high volume", "high volume", "high volume", "medium volume", "medium volume", "low volume", "low volume", "low volume", "low volume", "low volume" )), class = "data.frame", row.names = c(NA, -10L))
已有基础ggplot2绘图代码:
ggplot(df, aes(x = price_index, y = quantity_index, label = products)) + coord_fixed() + geom_point(colour = 'blue', size = 3, alpha=0.9) + scale_x_continuous(expand = c(0, 0), limits = c(50, 100), breaks = seq(50, 100, 5) ) + scale_y_continuous(expand = c(0, 0), limits = c(50, 100), breaks = seq(50, 100, 5) ) + geom_vline(xintercept = c(68, 74), color='black', linetype='dotted', size=1) + geom_hline(yintercept = c(77, 90), color='black', linetype='dotted', size=1)
需要给X轴(price_index)和Y轴(quantity_index)添加双层标签:底层保留原有的数值刻度,上层添加对应区域的分类标签(高值/中值/低值),对应分割线为X轴的68、74,Y轴的77、90。
解决方案
提供两种实现方式,可根据需求选择:
方式一:通过annotate添加外部区域标签
这种方式直接在坐标轴外侧添加分类标签,简单直观,适合需要灵活调整标签位置的场景。
完整代码
library(ggplot2) # 定义X/Y轴区域标签的位置和内容 x_region_labels <- data.frame( x = c(87, 71, 61), # 高/中/低价位区域的中心位置 label = c("高价位", "中价位", "低价位") ) y_region_labels <- data.frame( y = c(95, 83.5, 63), # 高/中/低销量区域的中心位置 label = c("高销量", "中销量", "低销量") ) ggplot(df, aes(x = price_index, y = quantity_index, label = products)) + coord_fixed() + geom_point(colour = 'blue', size = 3, alpha=0.9) + # 绘制区域分割线 geom_vline(xintercept = c(68, 74), color='black', linetype='dotted', size=1) + geom_hline(yintercept = c(77, 90), color='black', linetype='dotted', size=1) + # 自定义基础坐标轴 scale_x_continuous( expand = c(0, 0), limits = c(50, 100), breaks = seq(50, 100, 5), name = "价格指数" ) + scale_y_continuous( expand = c(0, 0), limits = c(50, 100), breaks = seq(50, 100, 5), name = "销量指数" ) + # 添加X轴上层区域标签 annotate("text", x = x_region_labels$x, y = 47, label = x_region_labels$label, size = 4, fontface = "bold") + # 添加Y轴上层区域标签(旋转90度) annotate("text", y = y_region_labels$y, x = 47, label = y_region_labels$label, size = 4, fontface = "bold", angle = 90) + # 调整边距避免标签被截断 theme( plot.margin = margin(10, 10, 25, 25, "pt"), axis.title.x = element_text(margin = margin(t = 15)), axis.title.y = element_text(margin = margin(r = 15)) )
关键说明
- 区域标签位置:根据分割线计算各区域的中心坐标,确保标签居中显示
- 边距调整:扩大绘图区域的下、左边距,防止外侧标签被截断;同时调整轴标题边距,避免和上层标签重叠
- 样式区分:用
fontface = "bold"让分类标签更醒目,和底层数值刻度形成层级
方式二:通过辅助轴(sec_axis)实现双层标签
这种方式利用ggplot2的辅助轴功能,将分类标签绑定到坐标轴系统,位置更规整。
完整代码
library(ggplot2) ggplot(df, aes(x = price_index, y = quantity_index, label = products)) + coord_fixed() + geom_point(colour = 'blue', size = 3, alpha=0.9) + # 绘制区域分割线 geom_vline(xintercept = c(68, 74), color='black', linetype='dotted', size=1) + geom_hline(yintercept = c(77, 90), color='black', linetype='dotted', size=1) + # 自定义X轴:底层数值刻度,上层分类标签 scale_x_continuous( expand = c(0, 0), limits = c(50, 100), breaks = seq(50, 100, 5), name = "价格指数", sec.axis = sec_axis( trans = ~., # 保持和原轴一致的转换关系 breaks = c(61,71,87), # 对应各区域中心位置 labels = c("低价位","中价位","高价位"), name = "" # 隐藏辅助轴标题 ) ) + # 自定义Y轴:底层数值刻度,上层分类标签 scale_y_continuous( expand = c(0, 0), limits = c(50, 100), breaks = seq(50, 100, 5), name = "销量指数", sec.axis = sec_axis( trans = ~., breaks = c(63,83.5,95), labels = c("低销量","中销量","高销量"), name = "" ) ) + # 调整辅助轴标签的边距 theme( axis.text.x.top = element_text(margin = margin(b = 10)), axis.text.y.right = element_text(margin = margin(l = 10)) )
关键说明
- 辅助轴转换:设置
trans = ~.让辅助轴和原轴完全对齐,保证标签位置准确 - 标签映射:将分类标签绑定到对应区域的中心位置,实现和分割线的对应
- 样式调整:通过
theme调整辅助轴标签的边距,避免和原轴刻度重叠
内容的提问来源于stack exchange,提问作者ah bon
相关产品推荐
相关产品推荐

