如何在分面饼图中正确定位带引导线的标签?
解决ggplot分面饼图内标签位置问题
我尝试复刻一张图表,但无法正确设置饼图内标签的位置。当前使用的代码如下:
ggplot(flying63, aes(x = '', y = percentage, fill = baby_on_plane)) + geom_col(width = 1, position = position_fill()) + coord_polar('y') + facet_grid(gender ~ age) + geom_label_repel(aes(label = sprintf("%.2f%%", percentage)), position = position_fill(), size = 3, max.time = 8) + theme(axis.text = element_blank(), axis.title = element_blank(), axis.ticks = element_blank())
当前效果:标签位置混乱,未对应到扇形中心;目标效果:每个扇形的标签居中显示在扇形内部。
更新后的输入数据:
flying63 <- structure(list(gender = c("Female", "Female", "Female", "Female", "Male", "Male", "Male", "Male", "Female", "Female", "Female", "Female", "Male", "Male", "Male", "Male", "Female", "Female", "Female", "Female", "Male", "Male", "Male", "Male"), age = structure(c(1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L), levels = c("18-29", "30-44", "45-60", "> 60"), class = c("ordered", "factor")), baby_on_plane = c("Yes, very rude", "Yes, very rude", "Yes, very rude", "Yes, very rude", "Yes, very rude", "Yes, very rude", "Yes, very rude", "Yes, very rude", "Yes, somewhat rude", "Yes, somewhat rude", "Yes, somewhat rude", "Yes, somewhat rude", "Yes, somewhat rude", "Yes, somewhat rude", "Yes, somewhat rude", "Yes, somewhat rude", "No, not at all rude", "No, not at all rude", "No, not at all rude", "No, not at all rude", "No, not at all rude", "No, not at all rude", "No, not at all rude", "No, not at all rude" ), n = c(5L, 5L, 6L, 6L, 8L, 11L, 15L, 5L, 23L, 11L, 14L, 10L, 19L, 34L, 19L, 17L, 47L, 62L, 75L, 71L, 35L, 50L, 74L, 55L), percentage = c(6.66666666666667, 6.41025641025641, 6.31578947368421, 6.89655172413793, 12.9032258064516, 11.5789473684211, 13.8888888888889, 6.49350649350649, 30.6666666666667, 14.1025641025641, 14.7368421052632, 11.4942528735632, 30.6451612903226, 35.7894736842105, 17.5925925925926, 22.0779220779221, 62.6666666666667, 79.4871794871795, 78.9473684210526, 81.6091954022989, 56.4516129032258, 52.6315789473684, 68.5185185185185, 71.4285714285714), label_position = c(3.33333333333333, 3.2051282051282, 3.1578947368421, 3.44827586206896, 6.4516129032258, 5.78947368421055, 6.94444444444445, 3.24675324675324, 15.3333333333333, 7.05128205128205, 7.3684210526316, 5.7471264367816, 15.3225806451613, 17.8947368421052, 8.7962962962963, 11.038961038961, 31.3333333333333, 39.7435897435898, 39.4736842105263, 40.8045977011494, 28.2258064516129, 26.3157894736842, 34.2592592592593, 35.7142857142857)), class = c("grouped_df", "tbl_df", "tbl", "data.frame"), row.names = c(NA, -24L), groups = structure(list( gender = c("Female", "Female", "Female", "Female", "Female", "Female", "Female", "Female", "Female", "Female", "Female", "Female", "Male", "Male", "Male", "Male", "Male", "Male", "Male", "Male", "Male", "Male", "Male", "Male"), age = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L), levels = c("18-29", "30-44", "45-60", "> 60"), class = c("ordered", "factor")), baby_on_plane = c("No, not at all rude", "Yes, somewhat rude", "Yes, very rude", "No, not at all rude", "Yes, somewhat rude", "Yes, very rude", "No, not at all rude", "Yes, somewhat rude", "Yes, very rude", "No, not at all rude", "Yes, somewhat rude", "Yes, very rude", "No, not at all rude", "Yes, somewhat rude", "Yes, very rude", "No, not at all rude", "Yes, somewhat rude", "Yes, very rude", "No, not at all rude", "Yes, somewhat rude", "Yes, very rude", "No, not at all rude", "Yes, somewhat rude", "Yes, very rude"), .rows = structure(list( 17L, 9L, 1L, 18L, 10L, 2L, 19L, 11L, 3L, 20L, 12L, 4L, 21L, 13L, 5L, 22L, 14L, 6L, 23L, 15L, 7L, 24L, 16L, 8L), ptype = integer(0), class = c("vctrs_list_of", "vctrs_vctr", "list"))), row.names = c(NA, -24L), .drop = TRUE, class = c("tbl_df", "tbl", "data.frame")))
解决方案
问题出在geom_label_repel的特性上——它会自动调整标签位置以避免重叠,不适合需要精准居中的场景。直接使用geom_label并利用数据中已有的label_position字段定位即可:
library(ggplot2) ggplot(flying63, aes(x = '', y = percentage, fill = baby_on_plane)) + geom_col(width = 1, position = position_fill()) + coord_polar('y') + facet_grid(gender ~ age) + # 使用预计算的label_position作为y轴位置,让标签居中 geom_label(aes(y = label_position, label = sprintf("%.2f%%", percentage)), size = 3) + theme(axis.text = element_blank(), axis.title = element_blank(), axis.ticks = element_blank())
说明
- 替换
geom_label_repel为geom_label:前者的自动位置调整功能不符合需求,后者能固定在指定位置 - 利用
label_position字段:这个字段已经计算好了每个扇形的中心位置(对应百分比的中间点),直接赋值给aes(y = label_position),就能让标签精准居中在扇形内部 - 移除
position = position_fill():因为label_position已经是正确的定位值,不需要再用位置调整函数
内容的提问来源于stack exchange,提问作者Ilse H
相关产品推荐
相关产品推荐

