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

如何在ggplot2饼图中为细扇区添加引导线与外部标注

解决ggplot2饼图细扇区外部标注的引导线问题

问题背景

用ggplot2生成饼图时,占比仅6%的"Not Reported"扇区过细,已将其标注移至外部,但无法添加从饼图到标注的引导线。尝试过geom_segment但参数调整无效,也试过坐标转换未成功,希望仅为该小扇区添加引导线。

解决方案

方法1:手动计算极坐标位置添加引导线

核心是先计算每个扇区的中点角度,再转换为笛卡尔坐标,以此确定引导线的起点(饼图边缘)和终点(标注位置)。

修正后的完整代码

set.seed(123)

df <- data.frame(letters = character(100))
df$letters <- sample(c("Reported", "Not Reported"), size = 100, replace = TRUE, prob = c(0.94, 0.06))

create_counts_dataframe <- function(df, column_name) {
  counts <- table(df[, column_name])
  dat_counts <- data.frame(counts)
  
  dat_counts$percent <- round(100 * dat_counts$Freq / sum(dat_counts$Freq), 2)
  # 按占比降序排序
  dat_counts$Var1 <- factor(dat_counts$Var1, levels = dat_counts$Var1[order(dat_counts$percent, decreasing = TRUE)])
  
  # 计算累计百分比、中点角度(转换为弧度)
  dat_counts$cum_percent <- cumsum(dat_counts$percent)
  dat_counts$mid_angle <- (dat_counts$cum_percent - dat_counts$percent/2) * 2 * pi / 100
  
  # 计算饼图边缘坐标(引导线起点)
  dat_counts$x_start <- cos(dat_counts$mid_angle)
  dat_counts$y_start <- sin(dat_counts$mid_angle)
  
  # 计算外部标注坐标(引导线终点,半径设为1.2,比饼图大)
  dat_counts$x_end <- 1.2 * cos(dat_counts$mid_angle)
  dat_counts$y_end <- 1.2 * sin(dat_counts$mid_angle)
  
  return(dat_counts)
}

colour_mapping2 <- c("Reported" = "#F4A582", "Not Reported" = "#B2182B")
gg_letters <- create_counts_dataframe(df, "letters")

# 绘制饼图
ggplot(gg_letters, aes(x = "", y = percent, fill = Var1)) +
  geom_bar(width = 1, stat = "identity") +
  coord_polar(theta = "y") +
  labs(fill = "letters") +
  scale_fill_manual(values = colour_mapping2) +
  theme_void() +
  theme(legend.title = element_text(face = "bold")) +
  # 大扇区的内部标注
  geom_text(data = subset(gg_letters, Var1 == "Reported"), 
            aes(label = paste0(percent, "%")), 
            position = position_stack(vjust = 0.7)) +
  # 小扇区的外部标注
  geom_text(data = subset(gg_letters, Var1 == "Not Reported"), 
            aes(x = x_end, y = y_end, label = paste0(percent, "%")),
            hjust = ifelse(subset(gg_letters, Var1 == "Not Reported")$mid_angle > pi, 1, 0)) +
  # 添加小扇区的引导线
  geom_segment(data = subset(gg_letters, Var1 == "Not Reported"),
               aes(x = x_start, y = y_start, xend = x_end, yend = y_end))

代码解释

  • 在统计函数中新增中点角度计算:通过累计百分比减去当前扇区百分比的一半,再转换为弧度,确保引导线从扇区中心位置延伸出去。
  • 转换极坐标为笛卡尔坐标:用cos()和sin()计算饼图边缘和外部标注的坐标,保证引导线方向准确。
  • 针对小扇区单独设置标注和引导线,大扇区保留内部标注,避免视觉干扰。

方法2:使用geom_label_repel自动生成引导线

如果不想手动计算坐标,可以用ggrepel包的geom_label_repel,仅对小扇区应用该函数:

代码示例

library(ggrepel)

ggplot(gg_letters, aes(x = "", y = percent, fill = Var1)) +
  geom_bar(width = 1, stat = "identity") +
  coord_polar(theta = "y") +
  labs(fill = "letters") +
  scale_fill_manual(values = colour_mapping2) +
  theme_void() +
  theme(legend.title = element_text(face = "bold")) +
  # 大扇区内部标注
  geom_text(data = subset(gg_letters, Var1 == "Reported"), 
            aes(label = paste0(percent, "%")), 
            position = position_stack(vjust = 0.7)) +
  # 小扇区用geom_label_repel自动生成带引导线的标注
  geom_label_repel(data = subset(gg_letters, Var1 == "Not Reported"),
                   aes(label = paste0(percent, "%")),
                   nudge_x = 1.2,  # 向外偏移调整位置
                   nudge_y = 0.5,
                   show.legend = FALSE)

说明

  • geom_label_repel会自动计算最优位置并添加引导线,无需手动处理坐标。
  • 通过nudge_x和nudge_y调整标注的偏移距离,避免和饼图重叠。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 10:17:50