问题描述
我编写了一个函数来制作散点图,允许用户将点的大小输入为数值(保留在 R-10
调用之外)或作为数据中的变量要映射的框架(需要进入 aes()
调用)。我远非 NSE 专家,虽然我已经开始使用它,但我觉得一定有更好的方法来做到这一点?
该函数的简化版本如下:
aes()
library(tidyverse)
data <- tibble(x = 1:10,y = 1:10)
test_func <- function(data,variable = 6){
# capture the variable in vars (I think quote would also work in this function)
variable <- vars({{variable}})
# convert it to a string and check if the string starts with a digit
# i.e. checking if this is a simple point size declaration not an aes mapping
is_number <- variable[[1]] %>%
rlang::as_label() %>%
str_detect("^[:digit:]*$")
# make initial ggplot object
p <- ggplot(data,aes(x = x,y = y))
# if variable is a simple number,add geom_point with no aes mapping
if(is_number){
variable <- variable[[1]] %>%
rlang::as_label() %>%
as.numeric()
p <- p + geom_point(size = variable)
} else{
# otherwise it must be intended as an aes mapping variable
variable <- variable[[1]] %>%
rlang::as_label()
p <- p + geom_point(aes(size = .data[[variable]]))
}
p
}
# works as a number
test_func(data,10)
由 reprex package (v2.0.0) 于 2021 年 4 月 8 日创建
解决方法
你说得对。 NSE 可能有点麻烦。但是,如果您使用 is.object()
,请处理 NSE,然后在您的 show.legend
调用中使用 geom_point
作弊...
test_func <- function(data,variable = 6){
qVariable = enquo(variable)
needLegend <- !is.object(qVariable)
data %>%
ggplot(aes(x = x,y = y)) +
geom_point(
aes(size = !! qVariable),show.legend=needLegend
)
}
在您的两个测试用例中提供与您的函数相同的输出。
,一种选择是检查用户提供给 variable
的内容是否是 data
中的列。如果是,请在 aes()
映射中使用该列。如果不是,则评估变量并将结果提供给 size
之外的 aes()
:
test_func <- function(data,variable = 6) {
v <- enquo(variable)
gg <- ggplot(data,aes(x=x,y=y))
if(exists(rlang::quo_text(v),data))
gg + geom_point(aes(size=!!v))
else
gg + geom_point(size = rlang::eval_tidy(v))
}
# All of these work as expected:
test_func(data) # Small points
test_func(data,10) # Bigger points
test_func(data,x) # Using column x
作为奖励,此解决方案允许您传递存储在变量中的值,而不是直接输入数字。只要变量名称不在 data
中,它就会被正确评估并提供给 size=
:
z <- 20
test_func(data,z) # Works