gpt4 book ai didi

r - 如何在ggplot geoms中编写具有整洁评估的函数?

转载 作者:行者123 更新时间:2023-12-04 09:44:47 25 4
gpt4 key购买 nike

我想编写一个在 ggplot 中执行美学映射的函数。该函数应该有两个参数:var应该映射到 aesthetic .下面的第一个代码块实际上有效。

但是,我想不在初始 ggplot 函数中进行映射,而是在 geom_point 函数中进行映射。在这里,我收到以下错误消息:

Error: := can only be used within a quasiquoted argument



1. block :工作正常
library(ggplot2)
myfct <- function(aesthetic, var){
aesthetic <- enquo(aesthetic)
var <- enquo(var)
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, !! (aesthetic) := !!var)) +
geom_point()
}
myfct(size, Petal.Width)

2. 阻塞:抛出错误
library(ggplot2)
myfct <- function(aesthetic, var){
aesthetic <- enquo(aesthetic)
var <- enquo(var)
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point(aes(!! (aesthetic) := !!var))
}
myfct(size, Petal.Width)

如果使用 sym 将参数审美作为字符串传递,也会发生相同的行为。 .
# 1. block
myfct <- function(aesthetic, var){
aesthetic <- sym(aesthetic)
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, !! aesthetic := {{var}})) +
geom_point()
}
myfct("size", Petal.Width)
# works

# 2. block
myfct <- function(aesthetic, var){
aesthetic <- sym(aesthetic)
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point(aes(!! aesthetic := {{var}}))
}
myfct("size", Petal.Width)
# doesn't work

最佳答案

原因

如果我们看一下aes源码,可以发现第1和第2位是被x和y预留的

> ggplot2::aes
function (x, y, ...)
{
exprs <- enquos(x = x, y = y, ..., .ignore_empty = "all")
aes <- new_aes(exprs, env = parent.frame())
rename_aes(aes)
}

让我们定义一个函数来看看它将如何影响 aes() :

testaes <- function(aesthetic, var){
aesthetic <- enquo(aesthetic)
var <- enquo(var)

print("with x and y:")
print(aes(Sepal.Length,Sepal.Width,!!(aesthetic) := !!var))
print("without x and y:")
print(aes(!!(aesthetic) := !!var))

}

> testaes(size, Petal.Width)
[1] "with x and y:"
Aesthetic mapping:
* `x` -> `Sepal.Length`
* `y` -> `Sepal.Width`
* `size` -> `Petal.Width`
[1] "without x and y:"
Aesthetic mapping:
* `x` -> ``:=`(size, Petal.Width)`

如您所见,当使用 :=没有 x 和 y, aestheticvar而是分配给 x。

为了系统地解决这个问题,需要更多关于 NSE 和 ggplot2 源代码的知识。

变通方法

始终将值分配给第一和第二位

library(ggplot2)
myfct <- function(aesthetic, var){
aesthetic <- enquo(aesthetic)
var <- enquo(var)
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point(aes(x = Sepal.Length, y = Sepal.Width,!! (aesthetic) := !!var))
}
myfct(size, Petal.Width)

或者在 aes 上写一个包装器

library(ggplot2)

myfct <- function(aesthetic, var){
aesthetic <- enquo(aesthetic)
var <- enquo(var)

# wrapper on aes
myaes <- function(aesthetic, var){
aes(x = Sepal.Length, y = Sepal.Width,!! (aesthetic) := !!var)
}

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point(mapping = myaes(aesthetic,var))
}
myfct(size, Petal.Width)

或者修改源代码

由于 x 和 y 是原因,我们可以修改 aes()通过删除 x 和 y 进行源编码。自从 geom_*()通过默认 inherit.aes = TRUE 继承 aes ,所以你应该能够运行它。

aes_custom <- function(...){
exprs <- enquos(..., .ignore_empty = "all")
aes <- ggplot2:::new_aes(exprs, env = parent.frame())
ggplot2:::rename_aes(aes)
}

myfct <- function(aesthetic, var){
aesthetic <- enquo(aesthetic)
var <- enquo(var)
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point(aes_custom(!!(aesthetic) := !!var))
}
myfct(size, Petal.Width)

更新

简而言之,论证顺序很重要。使用 NSE 时,我们应该始终放置 !!x := !!y在未命名参数的位置(例如 ... )并且从不在命名参数的位置。

我能够在 ggplot 之外重现该问题,因此根本原因来自 NSE。好像是 :=仅在处于未命名参数( ...)的位置时有效。在命名参数的位置(下例中为 x)时,它不会被正确评估。

library(rlang)
# test funciton
testNSE <- function(x,...){
exprs <- enquos(x = x, ...)
print(exprs)
}

# test data
a = quo(size)
b = quo(Sepal.Width)
:=用于代替未命名的参数( ...)

它工作正常

> testNSE(x,!!a := !!b)
<list_of<quosure>>

$x
<quosure>
expr: ^x
env: global

$size
<quosure>
expr: ^Sepal.Width
env: global
:=用于代替命名参数

它不起作用,因为 !!a := !!b用于 testNSE() 的第一个位置,并且第一个位置已经有名称 x .所以它尝试分配 size := Sepal.Widthx而不是分配 Sepal.Widthsize .

> testNSE(!!a := !!b)
<list_of<quosure>>

$x
<quosure>
expr: ^^size := ^Sepal.Width
env: global

关于r - 如何在ggplot geoms中编写具有整洁评估的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58307478/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com