ggplot2 函数 - 检查用户输入变量是否应该是映射美学
ggplot2 function - checking whether user input variable should be a mapped aesthetic
我写了一个函数来制作散点图,允许用户输入点的大小作为数值(保存在 aes()
调用之外)或作为变量要映射的数据框(需要进入 aes()
调用)。我远不是 NSE 方面的专家,虽然我已经开始工作了,但我觉得一定有更好的方法来做到这一点?
函数简化版如下:
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)
# works as a variable
test_func(data, y)
由 reprex package (v2.0.0)
于 2021-04-08 创建
你是对的。 NSE 可以有点 fiddle。但是如果你使用 is.object()
,处理 NSE,然后在你的 geom_point
调用中使用 show.legend
可能会作弊......
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()
映射中使用该列。如果不是,则评估变量并将结果提供给 aes()
之外的 size
:
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
我写了一个函数来制作散点图,允许用户输入点的大小作为数值(保存在 aes()
调用之外)或作为变量要映射的数据框(需要进入 aes()
调用)。我远不是 NSE 方面的专家,虽然我已经开始工作了,但我觉得一定有更好的方法来做到这一点?
函数简化版如下:
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)
# works as a variable
test_func(data, y)
由 reprex package (v2.0.0)
于 2021-04-08 创建你是对的。 NSE 可以有点 fiddle。但是如果你使用 is.object()
,处理 NSE,然后在你的 geom_point
调用中使用 show.legend
可能会作弊......
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()
映射中使用该列。如果不是,则评估变量并将结果提供给 aes()
之外的 size
:
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