Как использовать оператор rlang `!!!` для определения функции, которая оборачивается вокруг вызова ggplot?(Ошибка: невозможно использовать `!!!` на верхнем уровне) - PullRequest
0 голосов
/ 30 декабря 2018

Контекст

Чтение виньетки Программирование с помощью dplyr Я пытался использовать операторы ... и !!! для реализации функции, которая обернута вокруг функций ggplot и примет произвольныйколичество аргументов, которые будут определять, какие переменные в кадре данных должны отображаться в каждой эстетике.

Моя цель

Я хотел определить функцию plot_points2() так, чтобы

  1. plot_points2(df, x = x, y = y, color = z) было бы эквивалентно df %>% ggplot( mapping = aes(x = x, y = y, color = z) ) + geom_point(alpha = 0.1)
  2. plot_points2(df, x = x, y = z, color = y) будет эквивалентно df %>% ggplot( mapping = aes(x = x, y = z, color = y) ) + geom_point(alpha = 0.1)
  3. plot_points2(df, x = x, y = z) будет эквивалентно 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понять, что я хотел сделать, не сильно отличается от примера в виньетка , который использует эти операторы для создания функции, которая обтекает mutate() и передает несколько аргументов, которые определяют переменные группировки (на самом делеЯ смог реализовать функцию, которая делает это с примером набора данных выше, который я публикую в качестве примера), но каким-то образом последний работает, а первый нет:

это работает

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)

Я также читал, что проблема может быть связана с aes() оценкойd только при печати графика, но в этом случае я думаю, что использование !! и при ручной распаковке должно вызывать ту же ошибку, но это не :

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)

На самом деле этоПоследний пример работает нормально, если вы строите 3 переменные, но не позволяет отображать число переменных, отличных от 3

, например: plot_points2b(df, x = x, y = z) не эквивалентно

df %>% ggplot( mapping = aes(x = x, y = z) ) + geom_point(alpha = 0.1)

Вместо этого возникает ошибка:

Error in args[[3]] : subscript out of bounds 

Кто-нибудь знает, какую концепцию я здесь упускаю?Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 31 декабря 2018

Ваш конкретный пример использования приведен в ?aes.aes автоматически цитирует свои аргументы.Можно просто напрямую передать точки.Попробуйте:

plot_points3 <- function(d, ...){
  print(aes(...))
  ggplot(d, aes(...)) + geom_point(alpha = 0.1)
}
plot_points3(df, x = x, y = y, color = z)

Это приятно печатает:

Aesthetic mapping: 
* `x`      -> `x`
* `y`      -> `y`
* `colour` -> `z`

И дает требуемый участок.

0 голосов
/ 30 декабря 2018

Как уже упоминалось в моем комментарии, я думаю, что у вас, возможно, уже есть x и y в вашей среде, и именно поэтому часть вашего кода работает.Я не совсем уверен, чего вы пытаетесь достичь, но я думаю, что вы слишком много делаете для того, чтобы ваш код работал без ошибок.

Например:

plot_points <- function(d, ...){    
    ggplot(data = d, mapping = aes(x = x, y = y)) + 
     geom_point(alpha = 0.1)
}
plot_points (df, x, y)

сделаетваш график без какой-либо причины, чтобы добавить издержки и сложность !!! или enquo().

Вы тоже были на этом пути, где этот гораздо более простой код работает отлично:

add_dif_to_group_mean <- function(., ...) {

    df %>% group_by(g1) %>% mutate(x_dif = x-mean(x), 
                                          y_dif = y-mean(y), 
                                          z_dif = z-mean(z))
}

df %>% add_dif_to_group_mean(g1)

Аналогично:

plot_points2 <- function(d, ...){
    ggplot(data = d, mapping = aes(x=x, y=y, color=z)) + 
    geom_point(alpha = 0.1)
}
plot_points2(df, x = x, y = y, color = z)

отлично работает из того, что я могу сказать.

Итак, я понимаю, что, возможно, вы работаете с примерами в книге, и это здорово.Но я думаю, что где-то отсутствует проблема, из-за которой вам придется выполнять все дополнительные функции в реальном мире.Например, может быть, вы хотите передать строки типа "x" и "y" вместо x и y?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...