Reading the vignette Programming with dplyr I tried to use the ...
and !!!
operators to implement a function that would wrap around ggplot functions and would accept an arbitrary number of arguments that would define which variables in a dataframe were to be mapped to each aesthetic.
I wanted to define a function plot_points2()
such that
plot_points2(df, x = x, y = y, color = z)
would be equivalent to df %>% ggplot( mapping = aes(x = x, y = y, color = z) ) + geom_point(alpha = 0.1)
plot_points2(df, x = x, y = z, color = y)
would be equivalent to df %>% ggplot( mapping = aes(x = x, y = z, color = y) ) + geom_point(alpha = 0.1)
plot_points2(df, x = x, y = z)
would be equivalent to df %>% ggplot( mapping = aes(x = x, y = z) ) + geom_point(alpha = 0.1)
require(tidyverse)
require(rlang)
df <- tibble(g1= sample(x = c(1,2,3), replace = T, size = 10000),
g2= sample(x = c("a","b","c"), replace = T, size = 10000),
x = rnorm(10000, 50, 10),
y = rnorm(10000, 0, 20) + x*2,
z = rnorm(10000, 10, 5))
df
plot_points2 <- function(d, ...){
args <- quos(...)
print(args)
ggplot(data = d, mapping = aes(!!!args)) + geom_point(alpha = 0.1)
}
plot_points2(df, x = x, y = y, color = z)
Error: Can't use `!!!` at top level
Call `rlang::last_error()` to see a backtrace
I figure what I wanted to acomplish isn't much different from an example in the vignette that uses these operators to make a function that wraps around mutate()
, and passes multiple arguments that defined the grouping variables (in deed I was able to implement a function that does that to the example dataset above I'm posting as an example), but somehow the latter works and the former doesn't:
add_dif_to_group_mean <- function(df, ...) {
groups <- quos(...)
df %>% group_by(!!!groups) %>% mutate(x_dif = x-mean(x),
y_dif = y-mean(y),
z_dif = z-mean(z))
}
df %>% add_dif_to_group_mean(g1)
df %>% add_dif_to_group_mean(g1, g2)
plot_points2 <- function(d, ...){
args <- quos(...)
print(args)
ggplot(data = d, mapping = aes(!!!args)) + geom_point(alpha = 0.1)
}
plot_points2(df, x = x, y = y, color = z)
I also read that the problem could be related with aes()
being evaluated only when the plot is printed, but in that case I think using !!
and unpacking manually should raise the same error but it doesn't:
plot_points2b <- function(d, ...){
args <- quos(...)
print(args)
ggplot(data = d, mapping = aes(x = !!args[[1]],
y = !!args[[2]],
color = !!args[[3]])) +
geom_point(alpha = 0.1)
}
plot_points2b(df, x = x, y = y, color = z)
In deed this last example works fine if you plot 3 variables, but it doesn't allow you to plot a number of variables different from 3
eg: plot_points2b(df, x = x, y = z)
is not equivalent to
df %>% ggplot( mapping = aes(x = x, y = z) ) + geom_point(alpha = 0.1)
In stead it raises the error:
Error in args[[3]] : subscript out of bounds
Anyone knows what concept am I missing here? Thank you in advance!
Your specific use case is an example in ?aes
. aes
automatically quotes its arguments. One can simply directly pass the dots. Try:
plot_points3 <- function(d, ...){
print(aes(...))
ggplot(d, aes(...)) + geom_point(alpha = 0.1)
}
plot_points3(df, x = x, y = y, color = z)
This nicely prints:
Aesthetic mapping: * `x` -> `x` * `y` -> `y` * `colour` -> `z`
And yields the required plot.