R语言绘制热力地图复合气泡饼图:空间数据多维度可视化实战 1. 从数据到洞察为什么需要热力地图复合气泡饼图在数据分析的日常工作中我们常常面临一个挑战如何在一张图上同时清晰地展示多个维度的信息比如我们有一组地理坐标数据每个坐标点代表一个城市或区域我们不仅想知道这些区域的“热度”分布例如某项指标的整体强度还想了解每个区域内不同类别的构成比例。这时候传统的单一图表就显得捉襟见肘了。热力地图擅长展示连续变量的空间密度或强度变化颜色深浅直观地反映了数值大小。气泡图则可以通过气泡的大小来编码另一个连续变量。而饼图是展示构成比例的经典选择。那么有没有一种方法能将这三者巧妙地融合在一起呢答案是肯定的这就是我们今天要探讨的“热力地图复合气泡饼图”。想象一下这样的场景你是一家全国性连锁零售品牌的数据分析师手头有各个门店的经纬度坐标、月度总销售额以及各类商品如食品、服装、电子产品的销售额占比。你希望在一张中国地图上一眼看出哪些区域整体销售“热度”高热力底色。哪些门店的销售规模大气泡大小。每家门店的主力商品构成是什么气泡内的饼图。这种复合图表将地理信息、规模信息和结构信息三者合一极大地提升了数据表达的效率和洞察的深度。它特别适用于地理空间数据分析、生态学、社会学、商业智能等领域任何需要在空间背景下同时比较“强度”和“成分”的场景它都能大显身手。而实现这一强大可视化效果的利器正是R语言及其强大的图形生态系统。尤其是ggplot2包以其“图形语法”的哲学为我们搭建这种复杂但精美的图表提供了坚实的基础。结合geom_scatterpie这个专门用于绘制散点饼图的扩展包我们就能轻松地将饼图“放置”在地图上的指定坐标点再叠加热力图层最终生成我们想要的复合图表。2. 构建复合图表的基石核心工具包解析在动手绘制之前我们必须先理解并准备好所需的工具。R语言的强大很大程度上源于其丰富的扩展包生态。对于热力地图复合气泡饼图我们需要以下几类工具包协同工作。2.1 图形语法核心ggplot2ggplot2是R语言中最著名、应用最广泛的数据可视化包。它的核心思想是“图形语法”一张图是由数据、几何对象、美学映射、标度、坐标系、分面等图层叠加而成的。这种图层化的思想正是我们构建复合图表的理论基石。在ggplot2中我们通过ggplot()函数初始化一个图形对象然后使用号不断地添加图层geom_*函数和修饰scale_*,theme_*,labs等。例如geom_tile()或geom_raster()可以生成热力图的色块geom_point()可以生成散点气泡而我们要用的geom_scatterpie()则是一个特殊的几何对象它将在指定坐标绘制饼图。理解ggplot2的另一个关键是“美学映射”aes。我们将数据框中的变量通过aes()函数映射到图形的视觉属性上如x轴位置、y轴位置、颜色、大小、填充色等。在我们的复合图中经纬度会映射到x和y某个强度指标会映射到热力填充色总规模指标会映射到气泡饼图的大小而分类占比则映射到饼图各扇区的填充色。2.2 散点饼图专家geom_scatterpie 与 scatterpiegeom_scatterpie并不是ggplot2自带的几何对象它来自scatterpie这个扩展包。这个包的核心功能就是在ggplot2的框架内在指定的x, y坐标点上绘制饼图。它的工作原理非常直观你需要提供一个数据框其中必须包含用于定位的x和y列通常是经纬度以及一系列数值列这些数值列代表了饼图各个部分的绝对数值注意不是百分比。geom_scatterpie()会读取这些列在每一个x, y点上根据这些数值列的比例绘制一个饼图。一个关键参数是pie_scale它控制着所有饼图的整体大小。而每个饼图的半径则可以通过aes(r ...)进行映射从而实现气泡大小的效果——这里的r代表半径我们可以将其映射到另一个表示总规模的变量例如各数值列之和。这样饼图的大小就编码了总规模信息。2.3 空间背景的提供者地图数据与 sf/ggspatial我们的图表需要一张地图作为背景以提供地理空间参考。在R中获取地图数据有多种途径maps和mapdata包提供了世界、国家、美国州界等基础地图数据格式通常是数据框。ggplot2::map_data()函数一个便捷函数可以从maps包中提取数据并转化为ggplot2友好的数据框格式。sf(Simple Features) 包这是现代R语言空间数据分析的事实标准。sf对象是一种更强大、更标准化的空间矢量数据格式支持复杂的空间运算。ggplot2可以通过geom_sf()函数直接绘制sf对象。ggspatial包它在ggplot2和sf的基础上提供了更便捷的地图绘制功能如添加比例尺、指北针、从在线资源如OpenStreetMap, Stamen获取底图等能让我们的地图看起来更专业。对于入门级应用使用map_data()获取基础地图数据就足够了。但如果需要进行更复杂的空间操作或使用更精美的在线底图sf和ggspatial是更好的选择。2.4 数据塑形助手dplyr 与 tidyr在准备数据的过程中我们经常需要对数据进行筛选、汇总、变形。dplyr包提供了一套直观易用的“动词”函数来完成这些任务如filter()筛选、select()选择列、mutate()创建新列、summarise()汇总、group_by()分组以及强大的管道操作符%%。tidyr包则擅长处理数据的“整洁”变形特别是pivot_longer()和pivot_wider()函数可以轻松地在长格式和宽格式之间转换数据。我们的饼图数据通常需要是宽格式每一行是一个观测点每一列是一个分类的数值而其他数据处理可能要求长格式tidyr在这之间的转换至关重要。3. 实战演练一步步绘制全国零售门店销售复合图理论说得再多不如亲手做一遍。让我们以一个模拟的全国零售门店数据集为例完整地走一遍从数据准备到图表生成的流程。假设我们有一个包含100家门店的数据。3.1 第一步模拟与准备数据首先我们创建模拟数据。我们需要为每个门店生成随机的经纬度模拟在中国范围内的分布、总销售额、以及三大品类食品、服装、电子产品的销售额。# 加载必要的包 library(ggplot2) library(scatterpie) library(dplyr) library(tidyr) library(maps) library(ggspatial) # 用于添加地图元素 # 设置随机种子以保证结果可复现 set.seed(123) # 模拟100家门店的数据 n_stores - 100 # 1. 模拟经纬度大致在中国范围内 # 经度范围73°E ~ 135°E 纬度范围18°N ~ 54°N store_data - data.frame( store_id 1:n_stores, # 使用均匀分布模拟更真实的模拟可以用正态分布或从城市列表中抽样 longitude runif(n_stores, 73, 135), latitude runif(n_stores, 18, 54) ) # 2. 模拟总销售额万元和品类占比 # 假设总销售额在50到2000万元之间 store_data$total_sales - runif(n_stores, 50, 2000) # 模拟每个品类的销售额基于总销售额和随机比例 # 首先生成每个门店三个品类的随机比例使用狄利克雷分布更合理这里简化用均匀分布 for (i in 1:n_stores) { # 生成三个随机数作为比例的基数 props - runif(3, 0.5, 2) # 归一化为比例 props - props / sum(props) # 计算各品类销售额 store_data$food_sales[i] - store_data$total_sales[i] * props[1] store_data$clothing_sales[i] - store_data$total_sales[i] * props[2] store_data$electronics_sales[i] - store_data$total_sales[i] * props[3] } # 查看前几行数据 head(store_data)现在我们有了一个宽格式的数据框store_data每一行是一家门店列包括位置信息和各品类的绝对销售额。这正是geom_scatterpie()所需要的格式。注意geom_scatterpie()要求用于绘制饼图的各组成部分的列是绝对数值而不是百分比。它会自动计算比例。如果你的原始数据是百分比需要先乘以一个总量如总销售额转换回绝对数值。3.2 第二步创建基础热力图层销售热度热力图层旨在展示整体销售强度的空间分布。一种简单的方法是使用二维核密度估计但这需要大量的点。对于门店数据我们可以采用将地图网格化后计算网格内销售总和的方法来模拟“热度”。首先我们需要获取中国地图的轮廓数据并创建一个覆盖中国的网格。# 获取中国地图数据不含南海诸岛等细节如需详细地图需使用更专业的包如chinamap china_map - map_data(world, region China) # 定义网格大小经纬度步长 grid_step - 2 # 度 # 创建覆盖中国范围的网格点 lon_range - range(china_map$long, na.rm TRUE) lat_range - range(china_map$lat, na.rm TRUE) lon_seq - seq(from floor(lon_range[1]), to ceiling(lon_range[2]), by grid_step) lat_seq - seq(from floor(lat_range[1]), to ceiling(lat_range[2]), by grid_step) # 生成所有网格中心点 grid_points - expand.grid(lon_center lon_seq, lat_center lat_seq) # 将门店数据匹配到最近的网格中心简化处理实际应用可用空间连接 # 这里我们计算每个网格内所有门店的总销售额作为该网格的热度值 # 这是一个简化的近似更精确的方法需要使用空间连接如sf::st_join heat_data - grid_points heat_data$grid_sales - 0 # 初始化热度值 # 简单循环匹配仅用于演示数据量大时效率低应使用向量化或空间索引方法 for (i in 1:nrow(grid_points)) { lon_c - grid_points$lon_center[i] lat_c - grid_points$lat_center[i] # 找出落在当前网格中心点±grid_step/2内的门店 in_grid - store_data$longitude (lon_c - grid_step/2) store_data$longitude (lon_c grid_step/2) store_data$latitude (lat_c - grid_step/2) store_data$latitude (lat_c grid_step/2) if (any(in_grid)) { heat_data$grid_sales[i] - sum(store_data$total_sales[in_grid]) } } # 移除完全没有销售额的网格点减少绘图负担 heat_data - heat_data[heat_data$grid_sales 0, ] # 查看热力数据 head(heat_data)现在heat_data数据框包含了网格中心坐标和对应的总销售额热度。我们可以用geom_tile()来绘制热力图将grid_sales映射到填充色。3.3 第三步叠加气泡饼图图层门店规模与构成接下来我们在热力图层之上添加代表每家门店的气泡饼图。我们将使用geom_scatterpie()。# 开始构建ggplot对象 p - ggplot() # 1. 首先绘制中国地图轮廓背景 geom_polygon(data china_map, aes(x long, y lat, group group), fill gray95, color gray60, linewidth 0.2) # 2. 绘制热力图层注意热力图层在地图轮廓之上但在饼图之下 geom_tile(data heat_data, aes(x lon_center, y lat_center, fill grid_sales), width grid_step, height grid_step, alpha 0.7) # alpha设置透明度避免遮盖地图轮廓 # 设置热力颜色梯度这里使用从黄到红的渐变色 scale_fill_gradient(low yellow, high red, name 区域销售热度\n(万元)) # 3. 绘制散点饼图图层 geom_scatterpie(data store_data, aes(x longitude, y latitude, r total_sales/500), # r控制饼图大小除以500是为了缩放 cols c(food_sales, clothing_sales, electronics_sales), # 指定构成饼图的列 color grey40, # 饼图边缘颜色 alpha 0.85) # 饼图整体透明度 # 为饼图各扇区设置填充色 scale_fill_manual(name 品类构成, values c(food_sales #4DAF4A, # 绿色代表食品 clothing_sales #377EB8, # 蓝色代表服装 electronics_sales #E41A1C), # 红色代表电子产品 labels c(食品, 服装, 电子产品)) # 4. 调整坐标轴和主题 coord_fixed(ratio 1.2) # 固定纵横比使地图不变形 labs(x 经度, y 纬度, title 全国零售门店销售情况热力-气泡饼图, subtitle 热力底色区域总销售热度 | 气泡大小门店总销售额 | 饼图品类销售额构成) theme_minimal() theme(legend.position right, plot.title element_text(hjust 0.5, face bold), plot.subtitle element_text(hjust 0.5)) # 打印图形 print(p)代码关键点解析图层顺序我们按照geom_polygon地图背景→geom_tile热力层→geom_scatterpie饼图层的顺序叠加。这确保了饼图在最上层不会被热力色块完全遮盖。geom_scatterpie参数aes(r total_sales/500)这是实现“气泡”效果的关键。r美学映射到饼图的半径。我们将total_sales除以一个系数这里是500来缩放半径避免饼图过大或过小。这个系数需要根据你的数据范围和图形尺寸反复调整直到视觉效果最佳。cols c(...)指定数据框中哪些列是饼图的组成部分。这些列的值应该是数值型。color和alpha控制饼图边缘线的颜色和整体透明度。设置透明度可以让底下的热力图层隐约可见增强图表的层次感。scale_fill_*冲突注意我们使用了两个scale_fill_*函数。scale_fill_gradient作用于geom_tile的热力填充色而scale_fill_manual作用于geom_scatterpie的饼图扇区填充色。ggplot2能够很好地处理这种为不同几何对象设置不同填充标度的情况。coord_fixed对于地图保持经纬度的正确比例纵横比非常重要否则地图会严重变形。ratio参数需要根据地图的经纬度范围调整1.2是一个常用的近似值你可以通过尝试找到最合适的值。运行上述代码你应该能得到一张初步的复合图表。热力底色展示了销售密集的热区气泡的大小显示了门店的规模而每个气泡内的饼图则清晰揭示了该门店的商品销售结构。4. 进阶优化与美化让图表更具表现力第一版的图表已经传达了核心信息但在美观度和信息传达效率上还有很大的提升空间。下面我们从几个方面进行优化。4.1 优化热力图层使用核密度估计与更好的色阶之前我们用网格求和模拟热度方法比较粗糙。对于点数据使用核密度估计Kernel Density Estimation, KDE来生成平滑的热力分布是更专业的选择。我们可以使用ggplot2的geom_density_2d_filled()或stat_density_2d()结合geom_raster()来实现。同时热力的颜色梯度也可以优化。viridis、plasma、magma等色盲友好、感知均匀的配色方案是更好的选择它们可以通过scale_fill_viridis_c()函数轻松应用。# 方法使用stat_density_2d计算密度并用geom_raster绘制 # 首先用stat_density_2d生成一个密度估计的数据框 # 注意这里使用门店位置和总销售额作为权重使热力反映销售总额而不仅仅是门店数量 kde_data - ggplot_build( ggplot(store_data, aes(x longitude, y latitude, weight total_sales)) stat_density_2d(aes(fill after_stat(density)), geom raster, contour FALSE, n 200) )$data[[1]] # 提取计算出的栅格数据 # 重新绘图 p_advanced - ggplot() # 中国地图背景 geom_polygon(data china_map, aes(x long, y lat, group group), fill gray97, color gray70, linewidth 0.25) # 使用KDE生成的热力图层更平滑 geom_raster(data kde_data, aes(x x, y y, fill density), alpha 0.6) # 使用viridis配色 scale_fill_viridis_c(option plasma, name 销售密度热度, guide guide_colorbar(barwidth 0.8, barheight 10)) # 气泡饼图图层保持不变 geom_scatterpie(data store_data, aes(x longitude, y latitude, r total_sales/500), cols c(food_sales, clothing_sales, electronics_sales), color grey30, alpha 0.9) scale_fill_manual(name 品类构成, values c(food_sales #66C2A5, # 更柔和的颜色 clothing_sales #8DA0CB, electronics_sales #FC8D62), labels c(食品, 服装, 电子产品)) # 添加比例尺和指北针使用ggspatial annotation_scale(location bl, width_hint 0.3, style ticks) annotation_north_arrow(location tr, which_north true, style north_arrow_fancy_orienteering) # 调整坐标和主题 coord_sf(xlim range(china_map$long), ylim range(china_map$lat), datum NA) # 使用coord_sf并限制范围 labs(x NULL, y NULL, # 移除坐标轴标签让地图更干净 title 全国零售门店销售深度洞察图, subtitle 平滑热力层加权销售密度 | 气泡饼图门店规模与品类结构, caption 数据来源模拟数据 | 制图工具R ggplot2) theme_void() # 使用极简主题 theme(plot.title element_text(hjust 0.5, size 16, face bold, margin margin(b5)), plot.subtitle element_text(hjust 0.5, size 11, color gray40, margin margin(b15)), plot.caption element_text(hjust 1, size 9, color gray60, margin margin(t10)), legend.title element_text(size 10, face bold), legend.text element_text(size 9), panel.background element_rect(fill aliceblue, colour NA), # 添加淡蓝色背景模拟海洋 plot.margin unit(c(0.5, 0.5, 0.5, 0.5), cm)) print(p_advanced)优化点说明平滑热力stat_density_2d结合geom_raster生成了基于核密度估计的平滑热度表面weight total_sales参数使得密度估计考虑了每家店的销售额权重结果更准确。专业配色scale_fill_viridis_c(option plasma)提供了美观且色盲友好的渐变色。地图元素annotation_scale和annotation_north_arrow为地图添加了比例尺和指北针使其更专业。主题美化theme_void()移除了所有坐标轴线和标签让视觉焦点完全集中在数据和地图上。通过panel.background设置了淡蓝色背景模拟海洋使陆地轮廓更突出。对标题、图例的字体和边距进行了精细调整。4.2 处理饼图重叠与视觉混乱抽样与过滤当数据点非常密集时饼图会严重重叠导致图表无法阅读。有几种策略可以应对过滤小规模点只显示规模大于某个阈值的门店。这能突出重点减少杂乱。store_data_filtered - store_data %% filter(total_sales 300) # 只显示销售额大于300万的门店 # 然后在绘图时使用 store_data_filtered抽样显示在密集区域随机抽取部分点显示。library(dplyr) # 假设我们只想显示最多50个点可以随机抽样 set.seed(456) store_data_sampled - store_data %% sample_n(min(50, nrow(.)))**调整饼图透明度(alpha)和边缘线(color、linewidth)降低透明度、加深边缘线颜色、加粗边缘线可以在重叠时提供更好的区分度。geom_scatterpie(..., alpha 0.7, color black, linewidth 0.3)使用pie_scale全局缩放如果所有饼图都太大可以调整geom_scatterpie()的pie_scale参数默认为1整体缩小饼图尺寸。geom_scatterpie(..., pie_scale 0.8) # 缩小到80%在实际项目中通常需要结合使用以上几种方法。我的经验是先过滤再调整视觉参数。优先保证核心信息主要区域、大规模点的清晰展示牺牲一些细节是可接受的。4.3 添加交互性与可读性plotly动态化静态图虽然精美但交互性可以极大提升探索数据的能力。我们可以使用plotly包将ggplot2图形轻松转化为交互式图表。library(plotly) # 假设 p_advanced 是我们优化后的ggplot对象 interactive_plot - ggplotly(p_advanced) # 可以进一步定制plotly的悬停文本tooltip # 我们需要为store_data创建一个包含自定义文本的列 store_data_for_plotly - store_data %% mutate(hover_text paste( sprintf(b门店ID:/b %dbr, store_id), sprintf(b位置:/b (%.2fE, %.2fN)br, longitude, latitude), sprintf(b总销售额:/b %.1f 万元br, total_sales), sprintf(b食品:/b %.1f 万元 (%.1f%%)br, food_sales, 100*food_sales/total_sales), sprintf(b服装:/b %.1f 万元 (%.1f%%)br, clothing_sales, 100*clothing_sales/total_sales), sprintf(b电子产品:/b %.1f 万元 (%.1f%%), electronics_sales, 100*electronics_sales/total_sales) )) # 重新绘制ggplot但使用新的数据并添加text美学plotly会识别 p_for_plotly - ggplot() # ... [之前的图层但热力图层和地图背景保持不变] ... geom_scatterpie(data store_data_for_plotly, # 使用包含悬停文本的数据 aes(x longitude, y latitude, r total_sales/500, text hover_text), # 添加text映射 cols c(food_sales, clothing_sales, electronics_sales), color grey30, alpha 0.9) # ... [其他图层和主题设置] ... theme(legend.position none) # plotly有时会重复图例可以先隐藏 interactive_plot - ggplotly(p_for_plotly, tooltip text) # 指定tooltip来自text美学 # 隐藏plotly默认的模式栏如果需要 interactive_plot - interactive_plot %% config(displayModeBar FALSE) interactive_plot现在当鼠标悬停在任何一个气泡饼图上时都会弹出一个信息框详细显示该门店的ID、位置、总销售额以及各品类的销售额和占比。这使图表从一个静态的展示工具变成了一个动态的数据探索工具。5. 避坑指南与性能调优在制作这种复杂图表的过程中你肯定会遇到一些“坑”。以下是我在实际项目中总结出的常见问题及解决方案。5.1 饼图大小失控与比例尺难题问题r美学映射的变量范围过大或过小导致饼图要么巨大无比覆盖整个地图要么小到看不见。手动除以一个常数如/500很笨拙且难以在不同数据集间复用。解决方案使用标度变换函数进行标准化。ggplot2的scale_radius()或scale_size_area()用于geom_point可以自动处理气泡大小的映射但geom_scatterpie的r美学默认没有对应的标度函数。我们可以手动计算一个合理的半径。一个稳健的方法是将规模变量映射到面积Area ∝ r²并限制在一个合理的像素范围内。例如我们希望最大饼图的半径对应地图宽度的某个比例比如1/30。# 计算一个自适应的半径 map_width - diff(range(china_map$long)) max_radius_desired - map_width / 30 # 期望的最大半径经度单位 # 假设我们希望面积与 total_sales 成正比 # 面积 A π * r² ∝ total_sales # 所以 r ∝ sqrt(total_sales) # 我们将其缩放到 [0, max_radius_desired] 区间内 store_data - store_data %% mutate(radius_scaled sqrt(total_sales)) # 先开方 max_sqrt - max(store_data$radius_scaled, na.rm TRUE) store_data$radius_scaled - store_data$radius_scaled / max_sqrt * max_radius_desired # 在绘图中使用 radius_scaled geom_scatterpie(data store_data, aes(x longitude, y latitude, r radius_scaled), ...)这种方法确保了饼图大小与数据成比例并且能自适应不同数据集和图形尺寸。5.2 图形渲染缓慢与内存占用过高问题当数据点很多比如上千个或者热力网格非常精细时图形渲染会变得极其缓慢保存为高分辨率图片时可能内存不足。解决方案数据聚合这是最有效的方法。对于门店数据可以将地理位置非常接近的点在绘图前进行聚合用其加总数据代表一个“超级门店”。可以使用空间聚类算法如DBSCAN或简单的网格聚合。简化几何对象对于地图背景使用简化后的低精度Shapefile。rnaturalearth包提供的ne_countries(scale medium)或scale small数据比高精度数据轻量得多。降低热力图分辨率减少stat_density_2d或geom_tile的网格数量n参数或网格步长grid_step。抽样绘制如前所述在密集区域只绘制一部分点。使用ragg或Cairo设备保存图片在保存高质量图片时使用ragg::agg_png()或Cairo::CairoPNG()代替默认的png()它们通常更快且内存效率更高。library(ragg) agg_png(composite_map.png, width 3000, height 2000, res 300) print(p_advanced) dev.off()5.3 图例冲突与自定义问题当图表中有多个填充色标度热力图的连续色标和饼图的分类色标时ggplot2会自动生成两个图例。但有时它们的位置、标题或样式需要调整。解决方案使用guides()函数和theme()函数中的legend.*参数进行精细控制。p_final - p_advanced guides( fill guide_colorbar( # 控制热力填充色图例 title 销售密度, barwidth unit(0.5, cm), barheight unit(3, cm), title.position top ), fill2 guide_legend( # 注意饼图的填充图例在内部被命名为fill2因为第一个fill已被热力图占用 title 产品品类, nrow 1, # 图例项排成一行 title.position top, keywidth unit(0.8, cm), keyheight unit(0.8, cm) ) ) theme( legend.position bottom, # 将两个图例都放在底部 legend.box horizontal, # 水平排列图例 legend.box.just center, legend.spacing.x unit(1, cm) # 调整两个图例之间的间距 )注意geom_scatterpie创建的图例在存在多个fill标度时其图例键legend key默认可能是正方形而不是圆形。要将其改为圆形可能需要更复杂的修改例如在绘制完成后使用grid或cowplot包对图形对象进行后期编辑这超出了基础调整的范围。一个折中的办法是接受方形图例或者考虑使用geom_point绘制气泡再用其他方式添加分类信息如分面或颜色。5.4 坐标系统与地图投影问题我们一直使用coord_fixed或coord_sf(datumNA)这实际上是未经投影的地理坐标系经纬度。对于中国全图变形尚可接受。但如果聚焦某个特定区域如省份或者需要更精确的距离/面积表示就需要使用适当的地图投影。解决方案使用sf包处理空间数据并通过coord_sf()指定投影。例如使用阿尔伯斯等积圆锥投影Albers适用于中国地区。library(sf) library(rnaturalearth) # 获取中国sf格式地图数据 china_sf - ne_countries(scale medium, country china, returnclass sf) # 将门店数据转换为sf对象 stores_sf - st_as_sf(store_data, coords c(longitude, latitude), crs 4326) # WGS84 # 定义阿尔伯斯投影参数需调整 crs_albers - projaea lat_125 lat_247 lon_0105 # 绘图 ggplot() geom_sf(data china_sf, fill gray95, color gray60) geom_sf(data stores_sf, aes(size total_sales, color food_sales/total_sales)) # 示例用颜色表示食品占比 coord_sf(crs crs_albers) # 应用投影 # ... 其他自定义 ...使用投影后饼图的形状在地图上可能会因为投影变形而不再是完美的圆形在边缘地区这是正常现象。geom_scatterpie在非笛卡尔坐标系下可能表现不稳定此时可能需要考虑其他可视化方案或者接受轻微的变形。绘制热力地图复合气泡饼图是一个将数据转化为空间洞察的强力过程。从最初的数据整理、工具包选择到核心图层的叠加、视觉参数的精细调整再到最后对重叠、性能、图例等棘手问题的处理每一步都需要结合数据特点和对图形语法的理解。我个人的体会是这种复杂图表成功的关键在于层次分明和重点突出。热力层是背景提供全局趋势气泡饼图是前景展示个体细节。一定要通过颜色、大小、透明度的调节确保前景信息不会被背景淹没同时背景又能有效衬托前景。最后一个小技巧在最终出图前务必在不同的设备上尤其是投影仪预览你的图表。有些在电脑屏幕上看起来不错的颜色对比在大屏幕上可能变得难以区分。多测试多调整才能做出既专业又实用的数据可视化作品。