尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

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

R语言ggplot2绘制热力地图复合气泡饼图:多维度数据可视化实战 1. 项目概述当热力地图遇上气泡饼图在数据可视化的世界里我们总是在寻找更高效、更直观的方式去呈现复杂多维度的信息。如果你经常用R语言做数据分析尤其是涉及地理空间、生物信息学或者商业分析那么对热力地图Heatmap和气泡图Bubble Chart一定不陌生。热力地图擅长展示二维平面上的密度或强度分布比如某个地区的人口密度、基因表达水平而气泡图则能在二维坐标的基础上通过气泡的大小来编码第三个维度的数值。但有时候数据的故事不止三个维度。比如在研究不同城市的经济指标时你不仅想知道每个城市的GDP总量气泡大小还想同时展示其产业结构——第一、二、三产业的占比。这时候传统的单一气泡图就有点力不从心了。“热力地图复合气泡饼图”正是为了解决这类问题而生。它本质上是在热力地图的“底图”上将每个坐标点如城市中心点的气泡替换成一个微型的饼图Pie Chart从而在一个视图中同时呈现地理位置、数值强度以及内部组成结构这三个核心信息层。这个想法听起来很酷但实现起来在R语言中一度是个挑战。传统的ggplot2虽然强大但其图形语法Grammar of Graphics对在散点图上叠加饼图的支持并不原生。这正是geom_scatterpie这个扩展包大显身手的地方。它允许你将饼图当作“点”一样绘制在由x和y定义的坐标系上完美契合了我们的需求。结合ggplot2绘制热力地图底图的能力我们就能构建出信息密度极高的复合可视化图表。这种图表在展示多组学数据共定位、区域经济成分比较、生态物种组成分布等场景下具有无可替代的优势。2. 核心思路与工具选型解析2.1 为什么是“热力地图气泡饼图”在深入代码之前我们先厘清设计思路。选择这种复合图表而非并列的多张子图核心目的是为了揭示空间关联性和组成关联性。假设你有一份全国各省会城市的数据包含经纬度用于定位、年度总营收用于决定饼图大小、以及营收中来自A、B、C三个业务线的占比用于决定饼图扇形。如果分开画你需要一张中国地图用颜色表示总营收的热力图旁边再配一堆各省会的饼图。读者需要在大脑中进行艰难的“地图-饼图”匹配极易丢失空间上下文。而复合图表将饼图直接“钉”在地图上的对应城市读者一眼就能看出“哦东部沿海那个大城市不仅体量大气泡大而且业务C占比特别高饼图中某一块扇形很大而西部某个城市体量小但业务A是绝对主力。”这种空间与组成的即时联动是任何其他图表形式难以提供的。2.2 核心工具链ggplot2 scatterpie要实现这个效果我们的工具链非常清晰底图热力地图绘制使用ggplot2及其空间扩展如sf、maps、mapdata或栅格数据处理包如raster、terra来生成。热力图层通常通过geom_tile对于网格数据或geom_sf对于矢量面数据配合颜色梯度实现。饼图图层叠加使用geom_scatterpie包。这个包的核心函数geom_scatterpie()能够接受一个数据框其中必须包含定义位置的x和y列以及一系列数值列这些列的值将对应饼图的各个扇形。通过aes(r...)映射可以指定每个饼图气泡的半径。数据整合这是关键且容易出错的一步。热力地图的数据通常是网格或面数据与气泡饼图的数据点数据必须是坐标系兼容的。通常都需要转换为相同的坐标参考系统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), nrow5, ncol3) 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_tile和geom_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)代码关键点解析new_scale_fill()这是ggnewscale包的核心函数。它在热力图的fill标度定义后调用告诉ggplot2“接下来的图层如果要映射fill请使用一套全新的标度规则”。这完美解决了冲突。aes(r total_biomass / 50)半径r的映射。这里将total_biomass除以50是为了缩放使饼图大小在图上看起来合适。这个除数需要根据你实际数据的数值范围和绘图区域的大小反复调整没有固定值。pie_scale参数可以全局调整所有饼图的大小是r映射后的二次微调。coord_fixed()非常重要它确保图形区域中一个x单位和一个y单位的物理长度相等。如果不加当画布宽高比不是1:1时饼图会被压扁或拉长成椭圆破坏视觉准确性。运行这段代码你就能得到一张融合了水温热力分布和采样点浮游植物组成的气泡饼图。热力图提供了环境背景饼图揭示了具体位置的生物细节。5. 高级定制与美化技巧基础图表生成后为了让其更具可读性和发表质量还需要一系列美化。5.1 控制气泡饼图的大小与比例饼图大小是这种图表中最关键的视觉编码之一。如果大小不合适要么点之间重叠严重要么小到看不清。绝对半径与相对半径aes(r...)中的映射可以是数据的直接列也可以是经过计算的列。使用直接列如total_biomass意味着半径与数值成线性关系。但人眼对面积的感知更敏感因此有时会对数值开平方根sqrt(total_biomass)再进行缩放让气泡面积与数据成比例这更符合视觉习惯。避免重叠当数据点密集时饼图会严重重叠。解决方法有调整画布大小使用ggsave(width , height )输出更宽的图。使用geom_scatterpie_legend如果必须保留所有点可以考虑不画饼图而是用geom_scatterpie_legend先添加一个大小图例然后用引线将拥挤的饼图引出来标注但这会破坏空间直观性。数据聚合或抽样这是最根本的方法。如果点太密考虑在空间上进行聚类聚合用聚合后的大饼图代表一个区域。经验公式一个常用的起始尝试公式是r sqrt(value) / scaling_factor。scaling_factor可以从10开始尝试根据绘图区域调整。5.2 优化颜色与图例颜色是区分类别的关键。饼图颜色使用scale_fill_manual()手动指定清晰、区分度高的颜色。建议使用ColorBrewer的Set2、Set3或Paired调色板可通过RColorBrewer::brewer.pal()获取它们是为分类数据设计的。热图颜色连续变量使用渐变色。viridis、plasma、magma等色盲友好、感知均匀的色系是学术出版的首选可通过scale_fill_viridis_c()调用。图例排版使用theme(legend.position “bottom”)将图例放在底部可以节省横向空间。使用guides(fill guide_legend(nrow2))可以让图例分多行显示避免过长。5.3 添加地理背景与标注如果底图是真实地理地图效果会更专业。# 示例使用maps包绘制中国省份轮廓作为背景 library(maps) china_map - map_data(“china”) # 获取中国地图数据 ggplot() geom_polygon(data china_map, aes(xlong, ylat, groupgroup), fill“white”, color“grey60”, linewidth0.2) coord_fixed(ratio 1.3) # 中国地图的固定比例 # ... 然后叠加你的热力图层和饼图图层 ...注意地图数据china_map中的long,lat必须和你的业务数据采样点坐标在同一个坐标系下通常是WGS84。如果业务数据是别的坐标系如GCJ-02、BD-09需要先进行坐标转换。你还可以使用geom_text_repel或geom_label_repel来自ggrepel包为重要的采样点添加标签避免标签重叠。5.4 处理大量数据点与性能当采样点成百上千时绘制几百个饼图会非常缓慢。此时可以考虑抽样可视化只绘制一部分关键点。使用alpha透明度设置较低的alpha值如0.5让重叠部分可见展示整体分布模式而非单个点。简化饼图如果类别太多比如超过5类饼图会变得难以阅读。考虑只展示最主要的2-3个类别将其他合并为“其他”。改用“旭日图”sunburst变体但这需要更复杂的编码。放弃饼图回到气泡图但用嵌套圆环或堆叠条形图在气泡内表示组成不过这超出了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.out20), x runif(20, 0, 100), # 模拟通路图中的x坐标 y runif(20, 0, 100), # 模拟通路图中的y坐标 gene_log2fc rnorm(20, mean0, sd2), # 基因表达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(xx, yy, colorgene_log2fc), size12, shape15, alpha0.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(xx, yy, rsqrt(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(alpha1, colorblack)) ) # 添加节点ID标签 geom_text(data kegg_nodes, aes(xx, yy, labelnode_id), size2.5, colorwhite, fontfacebold) # 美化 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(hjust0.5, facebold), plot.subtitle element_text(hjust0.5, colorgrey40), 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”问题运行绘图代码后一片空白或直接报错。排查检查数据框和列名确保ggplot()和geom_*函数中data参数指定的数据框对象名正确且aes()内映射的列名如x,y,r,cols在数据框中确实存在。特别注意cols指定的列名必须完全匹配且均为数值列。检查包加载确认library(geomscatterpie)和library(ggnewscale)已成功执行没有报错。有时包名容易记错。检查数据格式geom_scatterpie要求cols指定的列是数值型numeric。用str(your_data)检查确保不是字符型character或因子型factor。7.2 饼图被压扁或拉伸成椭圆问题饼图看起来不圆成了椭圆。原因与解决根本原因是绘图区域的纵横比Aspect Ratio不是1:1。在ggplot2中x轴和y轴的单位长度不相等。强制等比例在绘图代码最后添加coord_fixed()。这是最直接有效的方法。调整画布如果使用ggsave()保存确保width和height参数设置合理。在RStudio的Plots面板中你可以手动拖动边框调整为一个正方形区域看看是否变圆。7.3 颜色标度冲突错误问题错误提示Error: Cannot add more than one scale for ‘fill’。解决这是最经典的错误。你必须使用ggnewscale::new_scale_fill()或new_scale_color()来分隔不同图层对同一种美学fill或color的标度。牢记顺序在定义完第一个fill标度如scale_fill_gradientn后立即调用new_scale_fill()然后再添加使用fill美学的第二个图层如geom_scatterpie及其对应的标度如scale_fill_manual。7.4 饼图大小不合适太大重叠或太小看不见问题所有饼图挤成一团或小如针尖。调整策略修改aes(r...)中的映射这是主要调节手段。如果直接映射原始值太大就除以一个系数如/100。尝试使用sqrt()或log()转换让面积与数据成比例视觉上更均衡。使用pie_scale参数这是整体缩放因子。例如pie_scale0.5会让所有饼图半径减半。调整绘图区域大小在ggsave(width10, height8, units“in”)中增加尺寸给点更多空间。数据本身问题检查你的半径映射数据如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(nrow2, byrowTRUE))来控制图例位置和行数。多个图例顺序在guide_colorbar(order1)和guide_legend(order2)中指定order参数可以控制多个图例的排列顺序。7.6 性能缓慢绘图卡顿问题当数据点超过50个时绘图和渲染速度明显变慢。优化建议减少数据点这是最有效的方法。考虑是否所有点都必须显示能否进行空间聚合或抽样简化图形设置alpha1去掉透明度计算colorNA去掉边框绘制。关闭抗锯齿在ggsave()中设置dpi72屏幕显示而非300印刷或使用ragg包作为图形设备。分步绘制先绘制静态背景热力图保存为图片。再在另一个图中单独绘制饼图不带背景保存为PNG带透明通道。最后在图像处理软件如Inkscape、Adobe Illustrator或使用magick包进行叠加。这适用于超大规模数据。绘图本身是一个迭代和调试的过程。我的习惯是先用一个最小的子数据集比如3-5个点把整个绘图流程跑通确保坐标、颜色、大小映射都正确。然后再将完整数据代入逐步调整美化参数。遇到复杂问题时在搜索引擎中输入错误信息和关键包名如“ggplot2 scatterpie error”通常能在Stack Overflow或GitHub issues中找到解决方案。记住geom_scatterpie的官方文档和示例是解决问题的第一站。
返回列表