R语言ggplot2绘制热力地图复合气泡饼图:多维度数据可视化实战

1. 项目概述:当热力地图遇上气泡饼图

在数据可视化的世界里,我们总是在寻找更高效、更直观的方式去呈现复杂多维度的信息。如果你经常用R语言做数据分析,尤其是涉及地理空间、生物信息学或者商业分析,那么对热力地图(Heatmap)和气泡图(Bubble Chart)一定不陌生。热力地图擅长展示二维平面上的密度或强度分布,比如某个地区的人口密度、基因表达水平;而气泡图则能在二维坐标的基础上,通过气泡的大小来编码第三个维度的数值。

但有时候,数据的故事不止三个维度。比如,在研究不同城市的经济指标时,你不仅想知道每个城市的GDP总量(气泡大小),还想同时展示其产业结构——第一、二、三产业的占比。这时候,传统的单一气泡图就有点力不从心了。“热力地图复合气泡饼图”正是为了解决这类问题而生。它本质上是在热力地图的“底图”上,将每个坐标点(如城市中心点)的气泡,替换成一个微型的饼图(Pie Chart),从而在一个视图中同时呈现地理位置、数值强度以及内部组成结构这三个核心信息层。

这个想法听起来很酷,但实现起来在R语言中一度是个挑战。传统的ggplot2虽然强大,但其图形语法(Grammar of Graphics)对在散点图上叠加饼图的支持并不原生。这正是geom_scatterpie这个扩展包大显身手的地方。它允许你将饼图当作“点”一样,绘制在由xy定义的坐标系上,完美契合了我们的需求。结合ggplot2绘制热力地图底图的能力,我们就能构建出信息密度极高的复合可视化图表。这种图表在展示多组学数据共定位、区域经济成分比较、生态物种组成分布等场景下,具有无可替代的优势。

2. 核心思路与工具选型解析

2.1 为什么是“热力地图+气泡饼图”?

在深入代码之前,我们先厘清设计思路。选择这种复合图表,而非并列的多张子图,核心目的是为了揭示空间关联性组成关联性

假设你有一份全国各省会城市的数据,包含经纬度(用于定位)、年度总营收(用于决定饼图大小)、以及营收中来自A、B、C三个业务线的占比(用于决定饼图扇形)。如果分开画,你需要一张中国地图用颜色表示总营收的热力图,旁边再配一堆各省会的饼图。读者需要在大脑中进行艰难的“地图-饼图”匹配,极易丢失空间上下文。而复合图表将饼图直接“钉”在地图上的对应城市,读者一眼就能看出:“哦,东部沿海那个大城市不仅体量大(气泡大),而且业务C占比特别高(饼图中某一块扇形很大);而西部某个城市体量小,但业务A是绝对主力。”这种空间与组成的即时联动,是任何其他图表形式难以提供的。

2.2 核心工具链:ggplot2 + scatterpie

要实现这个效果,我们的工具链非常清晰:

  1. 底图(热力地图)绘制:使用ggplot2及其空间扩展(如sfmapsmapdata)或栅格数据处理包(如rasterterra)来生成。热力图层通常通过geom_tile(对于网格数据)或geom_sf(对于矢量面数据)配合颜色梯度实现。
  2. 饼图图层叠加:使用geom_scatterpie包。这个包的核心函数geom_scatterpie()能够接受一个数据框,其中必须包含定义位置的xy列,以及一系列数值列(这些列的值将对应饼图的各个扇形)。通过aes(r=...)映射可以指定每个饼图(气泡)的半径。
  3. 数据整合:这是关键且容易出错的一步。热力地图的数据(通常是网格或面数据)与气泡饼图的数据(点数据)必须是坐标系兼容的。通常都需要转换为相同的坐标参考系统(CRS),例如WGS84(经纬度)或某个投影坐标系。

工具选型理由ggplot2是R语言可视化的事实标准,其图层叠加思想天然适合构建复合图表。geom_scatterpie是专门为解决“散点饼图”而生的扩展,其API设计遵循ggplot2的语法,学习成本低,集成度高。相比于尝试用基础图形函数或其它复杂包来自定义绘制饼图,这个组合是最高效、最稳定的。

注意:在安装geom_scatterpie时,它可能会依赖ggforce等包。请使用install.packages(“geom_scatterpie”)进行安装,并确保一并安装所有依赖。

3. 数据准备与预处理实战

任何可视化项目,80%的精力可能都花在数据准备上。这里我们以一个模拟的生态学场景为例:展示某个湖泊区域内,不同采样点的水温热力分布(底图),以及各采样点浮游植物群落组成(气泡饼图)。

3.1 模拟热力地图数据(湖泊水温)

热力数据通常是连续的表面。我们可以模拟一个网格数据。

# 生成模拟的湖泊网格坐标和温度数据 set.seed(123) # 确保结果可重现 library(tidyverse) # 定义湖泊大致范围(模拟为矩形区域) x_grid <- seq(100, 200, length.out = 30) # 经度方向网格 y_grid <- seq(300, 400, length.out = 30) # 纬度方向网格 # 创建网格并模拟温度(中心温度高,向四周递减) grid_data <- expand_grid(x = x_grid, y = y_grid) %>% mutate( # 模拟一个以(150, 350)为中心的二维正态分布温度场 dist_from_center = sqrt((x - 150)^2 + (y - 350)^2), temperature = 25 * exp(-dist_from_center / 50) + rnorm(n(), mean = 0, sd = 0.5) # 添加一些随机噪声 ) %>% filter(temperature > 20) # 简单模拟湖泊边界,温度低于20度的区域视为“陆地” head(grid_data)

这段代码生成了一个包含x,y,temperature三列的数据框,代表30x30网格上每个点的“水温”。filter(temperature > 20)是一个粗糙的边界模拟,在实际应用中,你的边界数据应来自地理信息文件(如shapefile)。

3.2 模拟气泡饼图数据(采样点群落组成)

假设我们在湖泊中设置了5个采样点,每个点测量了三种浮游植物(绿藻、硅藻、蓝藻)的生物量占比。

# 模拟5个采样点数据 sampling_sites <- tibble( site_id = paste0("Site_", LETTERS[1:5]), x = c(120, 150, 160, 140, 180), # 采样点经度 y = c(320, 350, 380, 310, 340), # 采样点纬度 total_biomass = c(50, 150, 80, 30, 120) # 总生物量,用于决定饼图大小 ) # 模拟群落组成比例(绿藻、硅藻、蓝藻) # 注意:比例之和必须为1(或100%) set.seed(456) composition <- matrix(runif(5*3), nrow=5, ncol=3) composition <- composition / rowSums(composition) # 归一化使每行和为1 colnames(composition) <- c("Green_Algae", "Diatom", "Cyanobacteria") # 合并数据 pie_data <- bind_cols(sampling_sites, as_tibble(composition)) pie_data

现在pie_data数据框包含了每个站点的位置(x,y)、总生物量(total_biomass)以及三种藻类的比例。geom_scatterpie要求将各类别的比例(或绝对值)以单独的列存放,这正是我们准备好的格式。

3.3 数据一致性检查

在绘图前,务必检查两类数据的坐标范围是否匹配。

summary(grid_data$x) summary(grid_data$y) summary(pie_data$x) summary(pie_data$y)

确保气泡饼图的数据点都落在热力地图的数据范围内,否则点会画在“图外”。如果范围不匹配,可能需要检查坐标参考系统或进行适当的缩放平移。

4. 基础图表绘制与图层叠加

数据准备妥当后,绘图就是按部就班地叠加图层。

4.1 绘制热力地图底图

我们使用geom_tile来绘制网格热力图。

library(ggplot2) base_heatmap <- ggplot() + geom_tile(data = grid_data, aes(x = x, y = y, fill = temperature)) + scale_fill_gradientn( colours = c("blue", "cyan", "yellow", "red"), name = "Temperature (°C)" ) + labs(x = "Longitude (simulated)", y = "Latitude (simulated)") + theme_minimal() + theme( panel.grid = element_blank(), axis.text = element_text(size = 10), legend.position = "right" ) base_heatmap

此时,你会得到一张漂亮的、显示模拟水温分布的热力图。颜色从蓝(低温)到红(高温)渐变。

4.2 叠加气泡饼图图层

接下来是关键步骤,加载geom_scatterpie并添加饼图。

library(geomscatterpie) # 注意包名是‘geomscatterpie’,但函数是geom_scatterpie # 首先,需要将饼图数据从“宽格式”转换为geom_scatterpie需要的格式吗? # 不需要!geom_scatterpie可以直接处理宽格式数据。 # 它需要知道哪些列代表饼图的组成部分。 combined_plot <- base_heatmap + geom_scatterpie( data = pie_data, aes(x = x, y = y, r = total_biomass / 50), # r 定义饼图半径,这里根据总生物量缩放 cols = c("Green_Algae", "Diatom", "Cyanobacteria"), # 指定组成成分的列名 color = "grey40", # 饼图扇形边的颜色 alpha = 0.8 # 设置一定透明度,避免完全遮盖底图 ) + # 为饼图部分添加图例 scale_fill_manual( name = "Phytoplankton Composition", values = c("Green_Algae" = "#2ca02c", "Diatom" = "#ff7f0e", "Cyanobacteria" = "#1f77b4"), guide = guide_legend(override.aes = list(r = 3)) # 调整图例中标识的大小 ) + # 注意:这里fill标度发生了冲突!热力图和饼图都用了fill美学。 # 我们需要使用ggnewscale包来管理两个独立的fill标度。 ggnewscale::new_scale_fill() # 为饼图声明一个新的fill标度起点 combined_plot

如果直接运行上述代码,你很可能会得到一个关于fill标度冲突的错误。这是因为geom_tilegeom_scatterpie都映射了fill美学(一个映射给温度,一个映射给藻类),而ggplot2默认一个图只能有一个fill标度。这就是为什么我们需要ggnewscale包。

4.3 解决颜色标度冲突:使用ggnewscale

正确的、完整的绘图代码如下:

library(ggplot2) library(geomscatterpie) library(ggnewscale) # 关键包,用于添加新的颜色标度 final_plot <- ggplot() + # 第一层:热力地图,使用第一个fill标度 geom_tile(data = grid_data, aes(x = x, y = y, fill = temperature)) + scale_fill_gradientn( colours = c("blue", "cyan", "yellow", "red"), name = "Water Temperature (°C)" ) + # 声明热力图的fill标度到此结束,开始新的fill标度 new_scale_fill() + # 第二层:散点饼图,使用第二个独立的fill标度 geom_scatterpie( data = pie_data, aes(x = x, y = y, r = total_biomass / 50), cols = c("Green_Algae", "Diatom", "Cyanobacteria"), color = "grey40", alpha = 0.8, pie_scale = 0.9 # 可选,微调饼图整体缩放 ) + scale_fill_manual( name = "Phytoplankton Composition", values = c("Green_Algae" = "#2ca02c", "Diatom" = "#ff7f0e", "Cyanobacteria" = "#1f77b4"), guide = guide_legend(override.aes = list(r = 3)) ) + # 美化坐标轴和主题 labs( title = "Lake Ecosystem: Temperature Heatmap with Phytoplankton Composition", subtitle = "Bubble size represents total biomass at sampling sites", x = "Longitude (Simulated Units)", y = "Latitude (Simulated Units)" ) + theme_minimal(base_size = 12) + theme( plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5, color = "grey50"), panel.grid.major = element_line(color = "grey90", linewidth = 0.2), panel.grid.minor = element_blank(), legend.box = "vertical", # 图例垂直排列 legend.spacing.y = unit(0.3, "cm") ) + coord_fixed() # 保持x和y轴比例一致,防止饼图被拉伸变形 print(final_plot)

代码关键点解析

  1. new_scale_fill():这是ggnewscale包的核心函数。它在热力图的fill标度定义后调用,告诉ggplot2:“接下来的图层如果要映射fill,请使用一套全新的标度规则”。这完美解决了冲突。
  2. aes(r = total_biomass / 50):半径r的映射。这里将total_biomass除以50是为了缩放,使饼图大小在图上看起来合适。这个除数需要根据你实际数据的数值范围和绘图区域的大小反复调整,没有固定值
  3. pie_scale参数:可以全局调整所有饼图的大小,是r映射后的二次微调。
  4. coord_fixed():非常重要!它确保图形区域中一个x单位和一个y单位的物理长度相等。如果不加,当画布宽高比不是1:1时,饼图会被压扁或拉长成椭圆,破坏视觉准确性。

运行这段代码,你就能得到一张融合了水温热力分布和采样点浮游植物组成的气泡饼图。热力图提供了环境背景,饼图揭示了具体位置的生物细节。

5. 高级定制与美化技巧

基础图表生成后,为了让其更具可读性和发表质量,还需要一系列美化。

5.1 控制气泡饼图的大小与比例

饼图大小是这种图表中最关键的视觉编码之一。如果大小不合适,要么点之间重叠严重,要么小到看不清。

  • 绝对半径与相对半径aes(r=...)中的映射可以是数据的直接列,也可以是经过计算的列。使用直接列(如total_biomass)意味着半径与数值成线性关系。但人眼对面积的感知更敏感,因此有时会对数值开平方根sqrt(total_biomass)再进行缩放,让气泡面积与数据成比例,这更符合视觉习惯。
  • 避免重叠:当数据点密集时,饼图会严重重叠。解决方法有:
    1. 调整画布大小:使用ggsave(width= , height= )输出更宽的图。
    2. 使用geom_scatterpie_legend:如果必须保留所有点,可以考虑不画饼图,而是用geom_scatterpie_legend先添加一个大小图例,然后用引线将拥挤的饼图引出来标注,但这会破坏空间直观性。
    3. 数据聚合或抽样:这是最根本的方法。如果点太密,考虑在空间上进行聚类聚合,用聚合后的大饼图代表一个区域。
  • 经验公式:一个常用的起始尝试公式是r = sqrt(value) / scaling_factorscaling_factor可以从10开始尝试,根据绘图区域调整。

5.2 优化颜色与图例

颜色是区分类别的关键。

  • 饼图颜色:使用scale_fill_manual()手动指定清晰、区分度高的颜色。建议使用ColorBrewer的Set2、Set3或Paired调色板(可通过RColorBrewer::brewer.pal()获取),它们是为分类数据设计的。
  • 热图颜色:连续变量使用渐变色。viridisplasmamagma等色盲友好、感知均匀的色系是学术出版的首选,可通过scale_fill_viridis_c()调用。
  • 图例排版:使用theme(legend.position = “bottom”)将图例放在底部可以节省横向空间。使用guides(fill = guide_legend(nrow=2))可以让图例分多行显示,避免过长。

5.3 添加地理背景与标注

如果底图是真实地理地图,效果会更专业。

# 示例:使用maps包绘制中国省份轮廓作为背景 library(maps) china_map <- map_data(“china”) # 获取中国地图数据 ggplot() + geom_polygon(data = china_map, aes(x=long, y=lat, group=group), fill=“white”, color=“grey60”, linewidth=0.2) + coord_fixed(ratio = 1.3) + # 中国地图的固定比例 # ... 然后叠加你的热力图层和饼图图层 ...

注意,地图数据(china_map中的long,lat)必须和你的业务数据(采样点坐标)在同一个坐标系下(通常是WGS84)。如果业务数据是别的坐标系(如GCJ-02、BD-09),需要先进行坐标转换。

你还可以使用geom_text_repelgeom_label_repel(来自ggrepel包)为重要的采样点添加标签,避免标签重叠。

5.4 处理大量数据点与性能

当采样点成百上千时,绘制几百个饼图会非常缓慢。此时可以考虑:

  • 抽样可视化:只绘制一部分关键点。
  • 使用alpha透明度:设置较低的alpha值(如0.5),让重叠部分可见,展示整体分布模式而非单个点。
  • 简化饼图:如果类别太多(比如超过5类),饼图会变得难以阅读。考虑:
    1. 只展示最主要的2-3个类别,将其他合并为“其他”。
    2. 改用“旭日图”(sunburst)变体,但这需要更复杂的编码。
    3. 放弃饼图,回到气泡图,但用嵌套圆环堆叠条形图在气泡内表示组成,不过这超出了geom_scatterpie的能力,需要完全自定义。

6. 实战案例:多组学数据共定位可视化

让我们回到更接近搜索热词的生物信息学场景。假设你进行了转录组和代谢组学联合分析,得到了差异表达的基因和差异积累的代谢物,并通过KEGG通路富集分析,发现它们共同富集在几条通路上。你想在一张KEGG通路图上,同时用热力颜色表示基因表达变化,用气泡饼图表示该通路节点上代谢物的变化方向(上调、下调、不变)及其比例。

6.1 数据模拟与准备

# 模拟KEGG通路节点数据(简化版) set.seed(789) kegg_nodes <- tibble( node_id = paste0("ko", sprintf("%05d", 1:20)), pathway_name = rep(c("Glycolysis", "TCA cycle", "Oxidative phosphorylation"), length.out=20), x = runif(20, 0, 100), # 模拟通路图中的x坐标 y = runif(20, 0, 100), # 模拟通路图中的y坐标 gene_log2fc = rnorm(20, mean=0, sd=2), # 基因表达log2FoldChange metabolite_up = runif(20, 0, 10), # 上调代谢物数量 metabolite_down = runif(20, 0, 8), # 下调代谢物数量 metabolite_ns = runif(20, 0, 5) # 不显著代谢物数量 ) %>% mutate( total_metabolite = metabolite_up + metabolite_down + metabolite_ns, # 计算比例 prop_up = metabolite_up / total_metabolite, prop_down = metabolite_down / total_metabolite, prop_ns = metabolite_ns / total_metabolite ) # 查看数据 head(kegg_nodes)

在这个模拟数据中,每个通路节点(node_id)有其在通路图中的位置(x, y),有基因表达变化值(gene_log2fc),以及三类代谢物的数量/比例。

6.2 绘制通路热力-气泡饼复合图

我们的目标是:背景用渐变色表示gene_log2fc(热力层),上面叠加气泡饼图,饼图的三块分别代表代谢物上调(prop_up)、下调(prop_down)、不显著(prop_ns)的比例,气泡大小由代谢物总数(total_metabolite)决定。

library(ggplot2) library(geomscatterpie) library(ggnewscale) library(viridis) # 使用viridis色系 kegg_plot <- ggplot() + # 第一层:基因表达热力背景(用散点模拟,实际可能是geom_tile) geom_point(data = kegg_nodes, aes(x=x, y=y, color=gene_log2fc), size=12, shape=15, alpha=0.6) + # 用方形色块 scale_color_viridis_c( option = "plasma", name = "Gene log2FC", guide = guide_colorbar(order = 1) # 指定图例顺序 ) + # 开始新的fill标度用于饼图 new_scale_fill() + # 第二层:代谢物组成气泡饼图 geom_scatterpie( data = kegg_nodes, aes(x=x, y=y, r=sqrt(total_metabolite)/3), # 半径与面积成比例 cols = c("prop_up", "prop_down", "prop_ns"), color = NA, # 去掉扇形边框 alpha = 0.85 ) + scale_fill_manual( name = "Metabolite Change", values = c("prop_up" = "#d62728", # 红色代表上调 "prop_down" = "#1f77b4", # 蓝色代表下调 "prop_ns" = "#7f7f7f"), # 灰色代表不显著 labels = c("Up-regulated", "Down-regulated", "Not significant"), guide = guide_legend(order = 2, override.aes = list(alpha=1, color="black")) ) + # 添加节点ID标签 geom_text(data = kegg_nodes, aes(x=x, y=y, label=node_id), size=2.5, color="white", fontface="bold") + # 美化 labs( title = "Multi-omics Integration: KEGG Pathway View", subtitle = "Heat color: Gene expression log2FC | Pie chart: Metabolite change proportion", x = NULL, y = NULL ) + theme_void() + # 通路图通常不需要坐标轴 theme( plot.title = element_text(hjust=0.5, face="bold"), plot.subtitle = element_text(hjust=0.5, color="grey40"), legend.position = "bottom", legend.box = "horizontal", legend.spacing.x = unit(0.5, "cm") ) + coord_fixed() print(kegg_plot)

这个案例的独特价值:它将两种组学数据(转录组、代谢组)在通路上下文中的关联一目了然地呈现出来。研究者可以快速识别出哪些通路节点在基因和代谢物层面都发生了剧烈变化(热力色深且气泡大),并且代谢物的变化方向是否一致(饼图中红蓝比例)。例如,一个深红色(基因高表达)的节点配上一个蓝色为主(代谢物主要下调)的饼图,可能暗示着转录后调控或代谢流重定向,这能直接启发下一步的生物学假设。

7. 常见问题与排查技巧实录

在实际操作中,你肯定会遇到各种报错和不如预期的效果。以下是我踩过坑后总结的排查清单。

7.1 图表不显示或报错“object ‘xxx’ not found”

  • 问题:运行绘图代码后一片空白,或直接报错。
  • 排查
    1. 检查数据框和列名:确保ggplot()geom_*函数中data=参数指定的数据框对象名正确,且aes()内映射的列名(如x,y,r,cols)在数据框中确实存在。特别注意cols指定的列名,必须完全匹配,且均为数值列。
    2. 检查包加载:确认library(geomscatterpie)library(ggnewscale)已成功执行,没有报错。有时包名容易记错。
    3. 检查数据格式geom_scatterpie要求cols指定的列是数值型(numeric)。用str(your_data)检查,确保不是字符型(character)或因子型(factor)。

7.2 饼图被压扁或拉伸成椭圆

  • 问题:饼图看起来不圆,成了椭圆。
  • 原因与解决:根本原因是绘图区域的纵横比(Aspect Ratio)不是1:1。在ggplot2中,x轴和y轴的单位长度不相等。
    • 强制等比例:在绘图代码最后添加coord_fixed()。这是最直接有效的方法。
    • 调整画布:如果使用ggsave()保存,确保widthheight参数设置合理。在RStudio的Plots面板中,你可以手动拖动边框调整为一个正方形区域看看是否变圆。

7.3 颜色标度冲突错误

  • 问题:错误提示Error: Cannot add more than one scale for ‘fill’
  • 解决:这是最经典的错误。你必须使用ggnewscale::new_scale_fill()(或new_scale_color())来分隔不同图层对同一种美学(fillcolor)的标度。牢记顺序:在定义完第一个fill标度(如scale_fill_gradientn)后,立即调用new_scale_fill(),然后再添加使用fill美学的第二个图层(如geom_scatterpie)及其对应的标度(如scale_fill_manual)。

7.4 饼图大小不合适(太大重叠或太小看不见)

  • 问题:所有饼图挤成一团或小如针尖。
  • 调整策略
    1. 修改aes(r=...)中的映射:这是主要调节手段。如果直接映射原始值太大,就除以一个系数(如/100)。尝试使用sqrt()log()转换,让面积与数据成比例,视觉上更均衡。
    2. 使用pie_scale参数:这是整体缩放因子。例如pie_scale=0.5会让所有饼图半径减半。
    3. 调整绘图区域大小:在ggsave(width=10, height=8, units=“in”)中增加尺寸,给点更多空间。
    4. 数据本身问题:检查你的半径映射数据(如total_biomass)是否存在极端异常值。一个巨大的值会导致其他所有点看起来都一样小。考虑用scale=“size”进行标准化,或使用r = rank(total_biomass)按排名决定大小。

7.5 图例显示不正常或重叠

  • 问题:饼图的图例显示为奇怪的方块或线条,或者多个图例堆在一起。
  • 解决
    • 饼图图例样式:在scale_fill_manual()中使用guide = guide_legend(override.aes = list(r = 3))来指定图例中标识的半径大小。r的值需要根据你的图表大小调整。
    • 图例排列:使用theme(legend.position = “bottom”)guides(fill = guide_legend(nrow=2, byrow=TRUE))来控制图例位置和行数。
    • 多个图例顺序:在guide_colorbar(order=1)guide_legend(order=2)中指定order参数,可以控制多个图例的排列顺序。

7.6 性能缓慢,绘图卡顿

  • 问题:当数据点超过50个时,绘图和渲染速度明显变慢。
  • 优化建议
    1. 减少数据点:这是最有效的方法。考虑是否所有点都必须显示?能否进行空间聚合或抽样?
    2. 简化图形:设置alpha=1(去掉透明度计算),color=NA(去掉边框绘制)。
    3. 关闭抗锯齿:在ggsave()中设置dpi=72(屏幕显示)而非300(印刷),或使用ragg包作为图形设备。
    4. 分步绘制:先绘制静态背景(热力图),保存为图片。再在另一个图中单独绘制饼图(不带背景),保存为PNG(带透明通道)。最后在图像处理软件(如Inkscape、Adobe Illustrator)或使用magick包进行叠加。这适用于超大规模数据。

绘图本身是一个迭代和调试的过程。我的习惯是,先用一个最小的子数据集(比如3-5个点)把整个绘图流程跑通,确保坐标、颜色、大小映射都正确。然后再将完整数据代入,逐步调整美化参数。遇到复杂问题时,在搜索引擎中输入错误信息和关键包名(如“ggplot2 scatterpie error”),通常能在Stack Overflow或GitHub issues中找到解决方案。记住,geom_scatterpie的官方文档和示例是解决问题的第一站。