R语言ggplot2 coord_polar双层环形图仅移动外环标签至外侧方法
问题说明
目前仅检索到单层饼图场景下的同类问题解决方案,基于ggplot2的coord_polar极坐标转换绘制双层环形图时,存在外环标签适配差、显示拥挤的问题,效果如下:
需求:
- 保留内环标签原有显示位置不变
- 仅将外环(第二层环)的标签移动到图表外侧,优先保证显示空间不足、无法正常展示的外环标签移至外侧
当前绘图代码
ggplot(usage.may, aes(x = Level, y = Percent, fill = Subcategory, label = Label)) + geom_bar(stat = "identity", color='white', show.legend = FALSE) + geom_text(aes(label = paste0(Label, "\n", Value, " (", Per_label, ")")), size = 2.5, colour = "white", check_overlap = TRUE, position = position_stack(vjust = 0.5)) + coord_polar('y') + scale_fill_manual(values = c("C01" = "#404688FF", "C011" = "#3B528BFF","C012" = "#3B528BFF","C013" = "#3B528BFF","C014" = "#3B528BFF", "C02" = "#287C8EFF", "C021" = "#287C8EFF", "C03" = "#27AD81FF", "C031" = "#35B779FF","C032" = "#35B779FF","C033" = "#35B779FF", "C04" = "#8FD744FF", "C041" = "#8FD744FF","C042" = "#8FD744FF")) + labs(title = "Electricity Usage May 2022") + ylab("") + xlab("") + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(), plot.title = element_text(hjust = 0.5), panel.border = element_blank(), panel.background = element_blank(), strip.background = element_blank(), axis.text= element_blank(), axis.ticks= element_blank())
可复现示例数据集
usage.may <- structure(list(Level = structure(c(2L, 3L, 3L, 3L, 3L, 2L, 3L, 2L, 3L, 3L, 3L, 2L, 3L, 3L, 1L), levels = c("0", "1", "2"), class = "factor"), Category = structure(c(2L, 2L, 2L, 2L, 2L, 3L, 3L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 1L), levels = c("C00", "C01", "C02", "C03", "C04"), class = "factor"), Subcategory = structure(c(2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L, 14L, 15L, 1L), levels = c("C00", "C01", "C011", "C012", "C013", "C014", "C02", "C021", "C03", "C031", "C032", "C033", "C04", "C041", "C042"), class = "factor"), Colour = structure(c(2L, 3L, 3L, 3L, 3L, 4L, 5L, 6L, 7L, 7L, 7L, 8L, 9L, 9L, 1L), levels = c("0", "1", "2", "3", "4", "5", "6", "7", "8"), class = "factor"), Label = c("Cafe (R1 & R2) ", "Non-checked ", "Spider Monkey ", "Signing-in Cabin", "Solar (cafe)", "Vet Room", "Non-checked", "Butchery", "Non-checked", "Solar (lynx)", "Solar (butchery)", "Tiger Block", "Farm", "Non-checked", ""), Value = c(5323L, 921L, 2611L, 34L, 1791L, 534L, 534L, 8479L, 6689L, 1371L, 419L, 3596L, 87L, 3247L, 0L), Percent = c(30L, 5L, 15L, 0L, 10L, 3L, 3L, 47L, 37L, 8L, 2L, 20L, 2L, 18L, 0L), Per_label = c("30%", "5%", "15%", "0%", "10%", "3%", "3%", "47%", "37%", "8%", "2%", "20%", "2%", "18%", "0%")), row.names = c(NA, -15L), class = "data.frame")
实现方案
核心修改逻辑是拆分内外环的文本图层,不再用同一个geom_text统一设置位置:
- 内环(Level=1)文本保持原有参数,放在环内部居中位置
- 外环(Level=2)单独设置文本层,将x轴位置偏移到环形外侧,自动计算每个扇区的中点位置和文本旋转角度,保证文字正向可读,避免拥挤
修改后可直接运行的完整代码:
library(ggplot2) library(dplyr) # 提前计算外环每个扇区的累计位置中点,用于标签对齐 outer_label_pos <- usage.may %>% filter(Level == 2) %>% arrange(desc(Subcategory)) %>% mutate( cum_y = cumsum(Percent), y_pos = cum_y - Percent/2 ) ggplot(usage.may, aes(x = Level, y = Percent, fill = Subcategory)) + geom_bar(stat = "identity", color='white', show.legend = FALSE) + # 内环标签:保持原有内部显示逻辑 geom_text(data = . %>% filter(Level == 1), aes(label = paste0(Label, "\n", Value, " (", Per_label, ")")), size = 2.5, colour = "white", position = position_stack(vjust = 0.5)) + # 外环标签:移到环外侧,自动调整角度 geom_text(data = outer_label_pos, aes(x = 2.4, y = y_pos, label = paste0(Label, "\n", Value, " (", Per_label, ")"), angle = 90 - 360*(y_pos/100)), size = 2.5, hjust = 0.5, colour = "black") + coord_polar('y') + # 扩展x轴范围,给外侧标签留出显示空间 scale_x_discrete(expand = c(0, 0.6)) + scale_fill_manual(values = c("C01" = "#404688FF", "C011" = "#3B528BFF","C012" = "#3B528BFF","C013" = "#3B528BFF","C014" = "#3B528BFF", "C02" = "#287C8EFF", "C021" = "#287C8EFF", "C03" = "#27AD81FF", "C031" = "#35B779FF","C032" = "#35B779FF","C033" = "#35B779FF", "C04" = "#8FD744FF", "C041" = "#8FD744FF","C042" = "#8FD744FF")) + labs(title = "Electricity Usage May 2022") + ylab("") + xlab("") + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(), plot.title = element_text(hjust = 0.5), panel.border = element_blank(), panel.background = element_blank(), strip.background = element_blank(), axis.text= element_blank(), axis.ticks= element_blank())
参数调整说明:
- 修改外环
geom_text中x=2.4的数值可以调整外环标签和环形的距离,数值越大距离越远 - 如果存在占比极小的标签仍然重叠,可以把
check_overlap = TRUE加到外环的geom_text参数中自动隐藏重叠标签,也可以用ggrepel包的geom_text_repel替换外环文本层,自动添加引导线避免重叠 scale_x_discrete(expand = c(0, 0.6))用于扩展x轴边界,避免外侧标签被画布边缘截断
内容的提问来源于stack exchange,提问作者Lisa B
相关产品推荐
相关产品推荐

