在map()中使用ggplot2,如何在sec_axis()公式中引用对象?
在map()中为ggplot2的sec_axis()引用外部对象的解决方法
你遇到的问题其实是**作用域(scope)**的问题:sec_axis()里的公式默认会在ggplot的全局/顶层环境中查找变量,而map()迭代过程中的临时变量(比如你要引用的其他对象)并不在这个环境里,所以会出现“无法解析”的错误。下面给你几种实用的解决办法,结合你的示例代码来演示:
第一步:先构造完整的示例数据
首先我们把你的代码补全,同时添加一个需要在次Y轴中引用的对象(比如每年的n的均值mean_n):
library(tidyverse) set.seed(1) # 构造嵌套数据,并计算每个年份的n的均值 d <- tibble( n = sample(500:1000, 15), group = letters[rep(1:5, 3)], year = rep(2011:2013, each = 5) ) %>% nest(data = -year) %>% mutate(mean_n = map_dbl(data, ~mean(.$n)))
方法1:用准引用(!!)注入变量(最推荐)
使用!!(bang-bang操作符)把map()迭代中的变量直接注入到sec_axis()的公式里,这样公式就不需要去外部查找变量,而是直接使用变量的值:
# 生成带次Y轴的ggplot对象 d <- d %>% mutate(gg1 = map2(data, mean_n, ~{ ggplot(.x, aes(x = group, y = n)) + geom_col(fill = "#2c3e50") + scale_y_continuous( name = "原始n值", # 用!! .y把当前迭代的mean_n注入到公式中 sec.axis = sec_axis(~ . / !! .y, name = "n / 年度均值") ) + labs(title = str_glue("年度:{.x$year[1]}")) })) # 查看其中一张图 d$gg1[[1]]
方法2:传递自定义转换函数
如果你不习惯用准引用,也可以把次Y轴的转换逻辑写成一个普通函数,然后通过sec_axis()的args参数传递外部对象:
d <- d %>% mutate(gg1 = map2(data, mean_n, ~{ # 定义转换函数,接收y值和均值参数 normalize_by_mean <- function(y, mean_val) { y / mean_val } ggplot(.x, aes(x = group, y = n)) + geom_col(fill = "#e74c3c") + scale_y_continuous( name = "原始n值", sec.axis = sec_axis( normalize_by_mean, args = list(mean_val = .y), # 传递mean_n的值 name = "n / 年度均值" ) ) }))
方法3:用local()创建本地环境
通过local()创建一个临时的本地环境,把需要引用的变量绑定到这个环境里,这样sec_axis()的公式就能找到它:
d <- d %>% mutate(gg1 = map2(data, mean_n, ~{ local({ # 在本地环境中绑定mean_n的值 current_mean <- .y ggplot(.x, aes(x = group, y = n)) + geom_col(fill = "#f39c12") + scale_y_continuous( name = "原始n值", sec.axis = sec_axis(~ . / current_mean, name = "n / 年度均值") ) }) }))
为什么会出现这个问题?
简单来说,sec_axis()的公式是延迟求值的——它不会在map()迭代的时候立即解析,而是等到ggplot渲染时才去查找变量。而map()里的临时变量(比如mean_n的每一个值)只存在于迭代的临时环境中,ggplot渲染时找不到这些变量,就会报错。上面的三种方法都是通过不同方式把变量的值“固定”到公式里,或者让公式能找到正确的环境。
内容的提问来源于stack exchange,提问作者cuttlefish44
相关产品推荐
相关产品推荐

