英文:
Aesthetics by column named with variable, how to use embrace operator in aesthetics?
问题
我有一个数据框,我想告诉ggplot2根据我用字符串变量命名的列进行着色
cubbins <- tibble(x=1:10, y=c(1:5, 1:5), title=rep(c("500 hats", "Oobleck"), 5))
my_col <- "title"
cubbins %>% ggplot(aes_string(x="x", y="y", color=my_col)) + geom_point() ## 正常工作,已弃用
cubbins %>% ggplot(aes(x=x, y=y, color=!!my_col)) + geom_point() ## 错误
cubbins %>% ggplot(aes(x=x, y=y, color={{my_col}})) + geom_point() ## 仍然错误
cubbins %>% ggplot(aes(x=x, y=y, color=sym(my_col))) + geom_point() ## 无效的美学警告
cubbins %>% ggplot(aes(x=x, y=y, color=enquo(my_col))) + geom_point() ## 无效的美学警告
cubbins %>% ggplot(aes(x=x, y=y, color=.data[[my_col]])) + geom_point() ## 这是当前aes_string文档中推荐的方式
cubbins %>% ggplot(aes(x=x, y=y, color=!!sym(my_col))) + geom_point() ## 正常工作,请解释
最后一个版本可以正常工作,但我对其他版本为什么无法工作感到困惑。感谢您的帮助。
背景信息:aes_string
已被弃用,但在我的简短搜索中没有找到关于正确解决方法的任何解释。
一般参考:https://rlang.r-lib.org/reference/topic-metaprogramming.html
英文:
I have a data frame and I want to tell ggplot2 to color according to a column I name with a string variable
cubbins <- tibble(x=1:10, y=c(1:5, 1:5), title=rep(c("500 hats", "Oobleck"), 5))
my_col <- "title"
cubbins %>% ggplot(aes_string(x="x", y="y", color=my_col)) + geom_point() ## works, deprecated
cubbins %>% ggplot(aes(x=x, y=y, color=!!my_col)) + geom_point() ## wrong
cubbins %>% ggplot(aes(x=x, y=y, color={{my_col}})) + geom_point() ## same wrong
cubbins %>% ggplot(aes(x=x, y=y, color=sym(my_col))) + geom_point() ## invalid aesthetics warning
cubbins %>% ggplot(aes(x=x, y=y, color=enquo(my_col))) + geom_point() ## invalid aesthetics warning
cubbins %>% ggplot(aes(x=x, y=y, color=.data[[my_col]])) + geom_point() ## this is what's recommended in the current aes_string documentation
cubbins %>% ggplot(aes(x=x, y=y, color=!!sym(my_col))) + geom_point() ## WORKS, please explain
The last version works, but I'm confused about why all the other versions aren't working. Your assistance is appreciated.
wrong version:
correct version:
For background, aes_string
is deprecated, but I didn't find any explanation for the proper workaround anywhere in my brief googling.
General refernce: https://rlang.r-lib.org/reference/topic-metaprogramming.html
答案1
得分: 3
我们实际上可以单独使用 aes
函数来更好地理解正在发生的事情。
基本上,我们希望得到与以下输出相同的内容:
library(ggplot2)
aes(colour = title)
#> Aesthetic mapping:
#> * `colour` -> `title`
在这里,美学映射 colour
与 符号 title
相关联。但我们希望使用存储的预定义字符字符串来实现这一点:
my_col <- "title"
以前,我们可以简单地使用 aes_string
:
aes_string(color = my_col)
#> Aesthetic mapping:
#> * `colour` -> `title`
#> 警告信息:
#> `aes_string()` 在 ggplot2 3.0.0 中已弃用。
#> 请使用 tidy evaluation ideoms 与 `aes()`
这个方法可以工作,将 colour
映射到符号 title
,但正如警告信息所告诉我们的那样,此函数已被弃用。
为了完整起见,让我们看看 aes(color = my_col)
,你明智地没有尝试:
aes(color = my_col)
#> Aesthetic mapping:
#> * `colour` -> `my_col`
当然,aes
函数只是捕获了 符号 my_col
,这不会起作用,因为你的数据框中没有叫做 my_col
的列。
现在我们继续尝试使用非引用的方法。!!
和 {{
运算符都评估其操作数并将其内联到 AST 中,因此它们都评估为 字符串 "title"
,而不是 符号 title
。请注意引号:
aes(color = !!my_col)
#> Aesthetic mapping:
#> * `colour` -> "title"
#> aes(color = {{my_col}})
#> Aesthetic mapping:
#> * `colour` -> "title"
这意味着你的图上的所有点都将具有相同的颜色,因为它们都映射到一个单一的字符串,该字符串将被循环使用到数据的长度。
你可能认为 sym
或 enquo
应该可以工作,但问题是,没有非引用运算符,整个 未评估的表达式 被捕获。看看使用 sym
和 enquo
会发生什么:
aes(color = sym(my_col))
#> Aesthetic mapping:
#> * `colour` -> `sym(my_col)`
aes(color = enquo(my_col))
#> Aesthetic mapping:
#> * `colour` -> `enquo(my_col)`
现在我们正在尝试将颜色映射到一个 调用,这甚至没有意义,ggplot
将抛出一个错误。我们需要将我们的映射映射到一个符号,而不是一个调用。但是,我们可以使用 !!
运算符来评估和内联 sym(my_col)
以获得符号 title
。如果我们使用 enquo
,那么我们将再次得到一个字符串作为评估的输出,而 {{
运算符只能用于名称,而不是调用,所以解决方法是:
aes(color = !!sym(my_col))
#> Aesthetic mapping:
#> * `colour` -> `title`
实际上,我更喜欢这种方法,这与推荐的方法相比,推荐的方法会产生:
aes(color = .data[my_col])
#> Aesthetic mapping:
#> * `colour` -> `.data[my_col]`
这也可以正常工作,但只因为 ggplot
识别到 .data
代词并以不同的方式处理它。
还有其他选项。任何能够得到正确符号的方法都可以工作:
aes(color = !!str2lang(my_col))
#> Aesthetic mapping:
#> * `colour` -> `title`
aes(color = !!as.name(my_col))
#> Aesthetic mapping:
#> * `colour` -> `title`
甚至可以只使用基本的 R 函数从头开始构建美学映射,如果你真的想要:
ggplot(cubbins,
structure(
list(colour = structure(
`attr<-`(call("~", str2lang(my_col)), ".Environment", .GlobalEnv),
class = c("quosure", "formula")),
x = structure(
`attr<-`(call("~", str2lang("x")), ".Environment", .GlobalEnv),
class = c("quosure", "formula")),
y = structure(
`attr<-`(call("~", str2lang("y")), ".Environment", .GlobalEnv),
class = c("quosure", "formula"))
),
class = "uneval")) + geom_point()
英文:
We can actually use the aes
function on its own to get a better handle on what's going on.
Essentially, we want something that gives us the same output as:
library(ggplot2)
aes(colour = title)
#> Aesthetic mapping:
#> * `colour` -> `title`
Here, the aesthetics colour
is linked to the symbol title
. But we want to achieve this using a stored, pre-defined character string:
my_col <- "title"
Previously, we could simply have used aes_string
:
aes_string(color = my_col)
#> Aesthetic mapping:
#> * `colour` -> `title`
#> Warning message:
#> `aes_string()` was deprecated in ggplot2 3.0.0.
#> i Please use tidy evaluation ideoms with `aes()`
This works, mapping colour
to the symbol title
, but as the warning tells us, this function is deprecated.
For completeness, let's look at aes(color = my_col)
, which you sensibly didn't even try
aes(color = my_col)
#> Aesthetic mapping:
#> * `colour` -> `my_col`
Of course, the aes
function has simply captured the symbol my_col
, which won't work because you have no column called my_col
in your data frame.
Now we move on to the attempts using unquoting. Both the !!
and {{
operators evaluate their operand and inline it in the AST, so they both evaluate to the string "title"
, not the symbol title
. Note the quotation marks:
aes(color = !!my_col)
#> Aesthetic mapping:
#> * `colour` -> "title"
#> aes(color = {{my_col}})
#> Aesthetic mapping:
#> * `colour` -> "title"
This means that all the points on your plot will be given the same colour, since they are all mapped to a single string, which will just be recycled to the length of the data.
You might think that sym
or enquo
should work, but the problem is that without an unquoting operator, the whole unevaluated expression is captured. Look what happens with sym
and enquo
:
aes(color = sym(my_col))
#> Aesthetic mapping:
#> * `colour` -> `sym(my_col)`
aes(color = enquo(my_col))
#> Aesthetic mapping:
#> * `colour` -> `enquo(my_col)`
Now we are trying to map colour to a call, which doesn't even make sense, and ggplot
will just throw an error. We need our mapping to be to a symbol, not a call. However, we can evaluate and inline sym(my_col)
using the !!
operator to get the symbol title
. If we use enquo
then we will again get a string as the output of the evaluation, and the {{
operator only works on names, not calls, so the solution is:
aes(color = !!sym(my_col))
#> Aesthetic mapping:
#> * `colour` -> `title`
I actually prefer this to the recommended method, which results in:
aes(color = .data[my_col])
#> Aesthetic mapping:
#> * `colour` -> `.data[my_col]`
This works perfectly well, but only because ggplot
recognises the .data
pronoun and treats it differently.
There are other options. Anything that gets us the correct symbol will work:
aes(color = !!str2lang(my_col))
#> Aesthetic mapping:
#> * `colour` -> `title`
aes(color = !!as.name(my_col))
#> Aesthetic mapping:
#> * `colour` -> `title`
And it's even possible to build the aesthetic mapping from scratch using only base R functions if you really wanted to:
ggplot(cubbins,
structure(
list(colour = structure(
`attr<-`(call("~", str2lang(my_col)), ".Environment", .GlobalEnv),
class = c("quosure", "formula")),
x = structure(
`attr<-`(call("~", str2lang("x")), ".Environment", .GlobalEnv),
class = c("quosure", "formula")),
y = structure(
`attr<-`(call("~", str2lang("y")), ".Environment", .GlobalEnv),
class = c("quosure", "formula"))
),
class = "uneval")) + geom_point()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论