R语言 ggplot2 与 ggforce 绘制空心饼图:3步代码实现环形图与标签自动布局
2026/7/11 19:27:57 网站建设 项目流程

R语言 ggplot2 与 ggforce 绘制空心饼图:3步代码实现环形图与标签自动布局

在数据可视化领域,饼图(Pie Chart)是最常见的图表类型之一,用于展示各类别在整体中的占比关系。然而传统实心饼图存在标签重叠、空间利用率低等问题,而空心饼图(环形图)不仅能有效解决这些问题,还能在中心区域添加额外信息,提升图表的信息密度和美观度。本文将系统介绍如何利用R语言中的ggplot2ggforce包,通过3步核心代码实现专业级空心饼图,并彻底解决标签自动布局难题。

1. 基础准备与环境搭建

在开始绘制高级饼图之前,我们需要确保工作环境准备就绪。R语言拥有丰富的可视化生态系统,其中ggplot2是最受欢迎的绘图系统之一,而ggforce作为其扩展包,提供了更强大的几何图形和坐标系统控制能力。

首先安装并加载必要的R包:

# 安装必要包(如果尚未安装) if(!require(ggplot2)) install.packages("ggplot2") if(!require(ggforce)) install.packages("ggforce") if(!require(dplyr)) install.packages("dplyr") if(!require(scales)) install.packages("scales") # 用于百分比格式化 # 加载包 library(ggplot2) library(ggforce) library(dplyr) library(scales)

接下来准备示例数据。我们使用一个包含疾病类型及其占比的数据集:

# 创建示例数据集 disease_data <- data.frame( disease = c("Heart disease", "Cancer", "Injuries", "CPD", "Stroke", "Type2 diabetes", "AD", "Suicide", "IP", "Chronic liver disease", "Other"), ratio = c(24.2, 21.9, 7.6, 5.2, 4.3, 3.2, 2.6, 2.6, 1.8, 1.8, 24.8) ) # 计算角度信息(为后续标签布局做准备) disease_data <- disease_data %>% arrange(desc(disease)) %>% mutate( end_angle = 2 * pi * cumsum(ratio)/sum(ratio), start_angle = lag(end_angle, default = 0), mid_angle = 0.5 * (start_angle + end_angle) )

2. 从基础饼图到高级环形图

2.1 传统饼图的局限性

R基础包中的pie()函数可以快速绘制简单饼图:

# 基础饼图 pie(disease_data$ratio, labels = disease_data$disease, col = rainbow(nrow(disease_data)), main = "疾病分布饼图")

然而这种方法存在明显缺陷:

  • 颜色管理不便
  • 标签容易重叠
  • 自定义选项有限
  • 难以添加复杂注释

2.2 ggplot2实现基础饼图

使用ggplot2可以创建更灵活的饼图。本质上,饼图是堆叠条形图经极坐标转换后的结果:

# ggplot2基础饼图 ggplot(disease_data, aes(x = "", y = ratio, fill = disease)) + geom_bar(stat = "identity", width = 1, color = "white") + coord_polar("y", start = 0) + theme_void() + scale_fill_brewer(palette = "Set3")

这种实现方式虽然美观,但仍未解决标签重叠问题。手动添加标签需要计算每个扇区的位置:

# 手动添加标签(不推荐) ggplot(disease_data, aes(x = "", y = ratio, fill = disease)) + geom_bar(stat = "identity", width = 1, color = "white") + coord_polar("y", start = 0) + geom_text(aes(label = paste0(ratio, "%")), position = position_stack(vjust = 0.5)) + theme_void()

2.3 使用ggforce创建空心饼图

ggforce包的geom_arc_bar()函数专为环形图设计,通过设置内外半径参数r0r轻松创建空心效果:

# 空心饼图核心代码 ggplot() + geom_arc_bar(data = disease_data, aes(x0 = 0, y0 = 0, r0 = 0.5, r = 1, start = start_angle, end = end_angle, fill = disease), color = "white") + coord_fixed() + theme_void() + scale_fill_brewer(palette = "Set3")

关键参数说明:

  • x0,y0: 圆心坐标
  • r0: 内半径(设为0时为实心饼图)
  • r: 外半径
  • start,end: 扇区的起始和结束角度

3. 标签自动布局的终极解决方案

3.1 标签重叠问题分析

饼图标签布局面临两大挑战:

  1. 空间有限,特别是当扇区较小时
  2. 标签需要清晰指向对应的扇区

传统解决方案如geom_text()会导致以下问题:

  • 小扇区的标签挤在一起
  • 长标签超出绘图区域
  • 标签与扇区对应关系不明确

3.2 智能标签布局算法

我们实现一个基于极坐标的标签布局系统,包含以下功能:

  • 自动计算标签最佳位置
  • 智能调整标签偏移量避免重叠
  • 添加引导线增强可读性
# 计算标签位置 label_position <- 1.2 # 标签基准半径 label_data <- disease_data %>% mutate( x = label_position * sin(mid_angle), y = label_position * cos(mid_angle), hjust = ifelse(mid_angle > pi, 1, 0), vjust = ifelse(mid_angle < pi/2 | mid_angle > 3*pi/2, 0, 1) ) # 完整绘图代码 ggplot() + # 绘制空心饼图 geom_arc_bar(data = disease_data, aes(x0 = 0, y0 = 0, r0 = 0.5, r = 1, start = start_angle, end = end_angle, fill = disease), color = "white") + # 添加标签 geom_text(data = label_data, aes(x = x, y = y, label = paste0(disease, "\n", ratio, "%"), hjust = hjust, vjust = vjust), size = 3) + # 添加引导线 geom_segment(data = label_data, aes(x = 1.05 * sin(mid_angle), y = 1.05 * cos(mid_angle), xend = 0.9 * sin(mid_angle), yend = 0.9 * cos(mid_angle))) + coord_fixed() + theme_void() + scale_fill_brewer(palette = "Set3") + labs(title = "疾病分布环形图") + theme(plot.title = element_text(hjust = 0.5, size = 14))

3.3 高级优化技巧

对于更复杂的场景,我们可以进一步优化:

1. 标签自动避让

使用ggrepel包实现标签自动避让:

if(!require(ggrepel)) install.packages("ggrepel") library(ggrepel) ggplot() + geom_arc_bar(data = disease_data, aes(x0 = 0, y0 = 0, r0 = 0.5, r = 1, start = start_angle, end = end_angle, fill = disease), color = "white") + geom_text_repel(data = label_data, aes(x = 1.1 * sin(mid_angle), y = 1.1 * cos(mid_angle), label = paste0(disease, ": ", ratio, "%")), size = 3, point.padding = NA, box.padding = 0.4, segment.color = "grey50") + coord_fixed() + theme_void()

2. 交互式可视化

使用plotly创建交互式环形图:

if(!require(plotly)) install.packages("plotly") library(plotly) p <- ggplot() + geom_arc_bar(data = disease_data, aes(x0 = 0, y0 = 0, r0 = 0.5, r = 1, start = start_angle, end = end_angle, fill = disease, text = paste(disease, "<br>占比:", ratio, "%")), color = "white") + coord_fixed() + theme_void() ggplotly(p, tooltip = "text")

4. 实战案例:多层环形图与高级应用

4.1 多层环形图绘制

通过组合多个geom_arc_bar图层,可以创建展示层级结构的多层环形图:

# 创建包含分组信息的数据 grouped_data <- data.frame( group = rep(c("Group1", "Group2", "Group3"), times = c(4, 3, 4)), category = LETTERS[1:11], value = c(10, 15, 12, 8, 20, 15, 10, 5, 12, 18, 15) ) %>% group_by(group) %>% mutate( group_total = sum(value), group_ratio = value/group_total, end_angle = 2 * pi * cumsum(group_ratio)/sum(group_ratio), start_angle = lag(end_angle, default = 0) ) %>% ungroup() %>% mutate( global_end = 2 * pi * cumsum(value)/sum(value), global_start = lag(global_end, default = 0) ) # 绘制多层环形图 ggplot() + # 外层(分组级别) geom_arc_bar(data = grouped_data, aes(x0 = 0, y0 = 0, r0 = 0.7, r = 1, start = global_start, end = global_end, fill = group), color = "white", alpha = 0.7) + # 内层(类别级别) geom_arc_bar(data = grouped_data, aes(x0 = 0, y0 = 0, r0 = 0.2, r = 0.6, start = start_angle, end = end_angle, fill = category), color = "white") + coord_fixed() + theme_void() + scale_fill_manual(values = c(brewer.pal(3, "Set1"), brewer.pal(11, "Set3")))

4.2 环形图最佳实践

根据实际项目经验,总结以下环形图使用建议:

  1. 类别数量控制

    • 理想情况:3-7个类别
    • 最多不超过12个类别
    • 过多样式会导致视觉混乱
  2. 颜色选择原则

    • 使用色盲友好配色
    • 相邻扇区使用对比色
    • 重要数据使用突出颜色
# 专业配色方案示例 professional_colors <- c( "#4E79A7", "#A0CBE8", "#F28E2B", "#FFBE7D", "#59A14F", "#8CD17D", "#B6992D", "#F1CE63", "#499894", "#86BCB6", "#E15759", "#FF9D9A", "#79706E", "#BAB0AC", "#D37295", "#FABFD2", "#B07AA1", "#D4A6C8", "#9D7660", "#D7B5A6" )
  1. 标签优化技巧

    • 重要数据添加百分比
    • 长名称使用缩写
    • 极小扇区合并为"其他"
  2. 动画与交互增强

    • 添加悬停提示
    • 实现点击筛选
    • 创建动画过渡效果

4.3 环形图与饼图的对比选择

虽然环形图更加美观,但选择图表类型时应考虑以下因素:

比较维度传统饼图空心环形图
空间利用率较低较高(中心可用)
标签清晰度容易重叠更易布局
多维度展示单一维度支持多层结构
视觉重点整体比例强调部分对比
小数据展示适合可能显得空洞
动态效果一般更易实现动效

在实际项目中,我通常会根据以下标准决策:

  • 展示3-5个主要类别时使用饼图
  • 需要对比多个相关维度时使用多层环形图
  • 有大量小类别时考虑堆叠条形图替代

5. 性能优化与常见问题解决

5.1 大数据量优化

当数据量较大时,环形图渲染可能变慢。以下优化策略效果显著:

  1. 数据聚合
    • 合并小扇区(如<2%合并为"其他")
    • 使用数据分箱
# 小类别合并示例 disease_data_optimized <- disease_data %>% mutate( disease = ifelse(ratio < 3, "Other", as.character(disease)) ) %>% group_by(disease) %>% summarise(ratio = sum(ratio)) %>% mutate( end_angle = 2 * pi * cumsum(ratio)/sum(ratio), start_angle = lag(end_angle, default = 0) )
  1. 渲染优化
    • 减少不必要的图形元素
    • 简化颜色方案
    • 关闭抗锯齿(对于静态导出)

5.2 常见问题排查

问题1:扇区间距不均匀

解决方案:检查角度计算是否正确,确保start_angleend_angle连续无重叠

问题2:标签位置错乱

解决方案:确认mid_angle计算正确,检查极坐标转换

# 正确的角度计算流程 data <- data %>% arrange(desc(category)) %>% # 确保排序正确 mutate( cum_ratio = cumsum(ratio), end_angle = 2 * pi * cum_ratio/sum(ratio), start_angle = lag(end_angle, default = 0), mid_angle = 0.5*(start_angle + end_angle) )

问题3:图例与颜色不符

解决方案:统一颜色映射,确保scale_fill_*与数据匹配

# 确保颜色映射一致 ggplot() + geom_arc_bar(aes(..., fill = category)) + scale_fill_manual(values = setNames(color_vector, unique(data$category)))

5.3 输出与分享

高质量输出需要注意以下参数:

# PDF输出(适合印刷) ggsave("ring_chart.pdf", width = 10, height = 8, units = "in", dpi = 300) # PNG输出(适合网页) ggsave("ring_chart.png", width = 1600, height = 1200, units = "px", bg = "white") # 交互式HTML输出 library(htmlwidgets) saveWidget(ggplotly(p), "interactive_ring.html")

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询