在ggplot分面绘图中利用scales::transformation访问变量实现分组自定义尺寸缩放的方法
实现分面独立尺度的点大小映射
这个问题的核心难点在于每个分面需要基于对应分析项的独立限值来缩放点大小,同时还要让图例显示实际测量值而非比例值。我推荐用patchwork拼接独立子图的方式,这种方法更直观可控,完全符合你的需求:
步骤1:预处理数据,提取每个分析项的关键参数
首先我们先把每个分析项的唯一限值和最大值提取出来,方便后续计算:
library(tidyverse); library(sf); library(ggplot2); library(patchwork) # 原始数据 sfc <- tribble(~analyte, ~value, ~guideline, ~lat, ~long, "pH", 4, 5, 21, 52, "pH", 7, 5, 22, 53, "EC", 50, 100, 21, 52, "EC", 200, 100, 22, 53) %>% st_as_sf(coords = c("lat", "long"), crs = 4326) # 提取每个分析项的限值和最大值 analyte_info <- sfc %>% group_by(analyte) %>% summarise( guideline = first(guideline), # 每个分析项的唯一限值 max_value = max(value), # 每个分析项的最大测量值 .groups = "drop" )
步骤2:为每个分析项创建独立的绘图函数
我们写一个循环,为每个分析项生成单独的地图,其中关键是用trans_new()创建自定义转换函数,实现:
- 测量值0 → 点大小0px
- 测量值=限值 → 点大小5px
- 测量值=最大值 → 点大小
(最大值/限值)*5px
同时让图例显示实际测量值:
# 生成每个分析项的子图 sub_plots <- map(analyte_info$analyte, function(analyte_name) { # 筛选当前分析项的数据 sub_data <- filter(sfc, analyte == analyte_name) # 获取当前分析项的限值和最大值 curr_guideline <- analyte_info$guideline[analyte_info$analyte == analyte_name] curr_max <- analyte_info$max_value[analyte_info$analyte == analyte_name] # 创建自定义转换函数:将实际值转换为点大小,反向转换用于图例标签 size_trans <- trans_new( name = "scaled_size", transform = function(x) (x / curr_guideline) * 5, # 实际值→点大小 inverse = function(x) (x * curr_guideline) / 5 # 点大小→实际值(用于图例) ) # 绘制当前分析项的地图 ggplot(sub_data) + geom_sf(aes(size = value)) + # 设置自定义的大小尺度 scale_size_continuous( trans = size_trans, breaks = c(0, curr_guideline, curr_max), # 图例显示0、限值、最大值 name = analyte_name, # 图例标题为分析项名称 range = c(0, (curr_max / curr_guideline)*5) # 确保点大小范围符合要求 ) + ggtitle(analyte_name) + theme_minimal() })
步骤3:拼接子图并调整布局
最后用patchwork把所有子图拼起来,还可以统一收集图例:
# 拼接子图,2列布局,统一收集图例 wrap_plots(sub_plots, ncol = 2) + plot_layout(guides = "collect") + plot_annotation(title = "各分析项采样点检测结果分布图")
可选方案:用ggh4x实现分面独立尺度
如果你不想拆分成多个子图,也可以用ggh4x包的分面独立尺度功能,核心是为每个分面指定单独的大小转换函数:
library(ggh4x) # 创建每个分析项的转换函数列表 trans_list <- map(analyte_info$analyte, function(analyte_name) { g <- analyte_info$guideline[analyte_info$analyte == analyte_name] trans_new( name = paste0("scaled_", analyte_name), transform = function(x) (x / g)*5, inverse = function(x) (x*g)/5, breaks = function(x) c(0, g, analyte_info$max_value[analyte_info$analyte == analyte_name]) ) }) names(trans_list) <- analyte_info$analyte # 绘制分面图并设置独立尺度 ggplot(sfc) + geom_sf(aes(size = value)) + facet_wrap2(~analyte) + facetted_scales( size = list( pH = scale_size_continuous(trans = trans_list[["pH"]], name = "pH"), EC = scale_size_continuous(trans = trans_list[["EC"]], name = "EC") ) )
两种方法都能满足你的需求:点大小按每个分析项的限值比例缩放,图例显示实际测量值,同时保留分面地图的结构。
内容的提问来源于stack exchange,提问作者vorpal
相关产品推荐
相关产品推荐

