Вопрос по ggplot2, r – Локальные переменные в пределах aes

45

Я пытаюсь использовать локальную переменную вaes когда я заговор с ggplot. Это моя проблема, сводящаяся к сути:

xy <- data.frame(x=1:10,y=1:10)

plotfunc <- function(Data,YMul=2){
    ggplot(Data,aes(x=x,y=y*YMul))+geom_line()
}

plotfunc(xy)

Это приводит к следующей ошибке:

Error in eval(expr, envir, enclos) : object 'YMul' not found

Кажется, что я не могу использовать локальные переменные (или аргументы функции) вaes, Может быть, это происходит из-за содержанияaes выполняется позже, когда локальная переменная выходит из области видимости? Как я могу избежать этой проблемы (кроме использования локальной переменной внутриaes)?

Я запускаю приведенный выше код и не получаю никакой ошибки (23 октября 2017 г.), было ли обновление доggplot2 чтобы объяснить, почему это будет работать сейчас? PatrickT
неправда, следует использовать значение по умолчанию baptiste
Я думаю, потому что он все еще ожидает, что вы передадите Ymul, но вы даете только plotfunc (xy) zhan2383

Ваш Ответ

6   ответов
1

https: //github.com/hadley/ggplot2/issues/74

Я думаю, что это лучше

по сути аналогичен @baptiste, но включает ссылку на окружение непосредственно в вызове ggplot

Я сообщаю об этом здесь

g <- function() {
  foo3 <- 4
  ggplot(mtcars, aes(x = wt + foo3, y = mpg),
         environment = environment()) +
    geom_point()
}

g()
# Works
Это дубликат ответа @ baptiste, и IMO излишне использует другой пример, поскольку OP предоставил воспроизводимый пример. Я бы посоветовал удалить (и, возможно, прокомментировать крещение с проблемой Github, которую вы связали). Max Ghenis
10

которая позволяет передавать любое значение черезYMul аргумент без необходимости добавлять его кData data.frame или в глобальную среду:

plotfunc <- function(Data, YMul = 2){
    eval(substitute(
        expr = {
            ggplot(Data,aes(x=x,y=y*YMul)) + geom_line()
        }, 
        env = list(YMul=YMul)))
    }

plotfunc(xy, YMul=100)

Чтобы увидеть, как это работает, попробуйте следующую строку в отдельности:

substitute({ggplot(Data, aes(x=x, y=y*YMul))}, list(YMul=100))
+ 1 То же самое с bquote: eval (bquote (ggplot (Data, aes (x = x, y = y *. (YMul)))) + geom_line ())). Wojciech Sobala
+ 1 Спасибо! Я знал, что есть способ сделать это, но никогда не удосужился разобраться с этим. Очень круто Justin
@ Джастин - Рад, что это полезно. Какая Я никогда не занимал время, чтобы разобраться, как работает основанная на прото область видимости ggplot. В качестве примера я просто поместилbrowser() заявление внутриaes() позвони и набравsys.frames(), Я получаю список из 23 сред, ни одна из которых (??), по-видимому, не разрешает прямой доступ к значениюYMul. Хм. Josh O'Brien
@ Джастин и Джош: вот связанное обсуждение на github: Github.com / Hadley / ggplot2 / вопросы / 248 Кажется, это обсуждалось, но не реализовано. jthetzel
Да ... это далеко от меня. У меня было немногопользовательский ко любезность Kohske, которая порвала с новым выпуском, и это совершенно непостижимо для меня! Когда-нибудь я хотел бы понять прото ... Justin
5

ggplot() saes надеетсяYMul быть переменной внутриdata фрейм данных. Попробуйте включитьYMull вместо этого:

Спасибо @Justin:ggplot() saes, кажется, ищетYMul вdata сначала фрейм данных, а если не найден, то в глобальной среде. Мне нравится добавлять такие переменные во фрейм данных следующим образом, поскольку это имеет смысл для меня концептуально. Мне также не нужно беспокоиться об изменениях глобальных переменных, имеющих неожиданные последствия для функций. Но все остальные ответы тоже верны. Так что пользуйтесь тем, что вам подходит.

require("ggplot2")
xy <- data.frame(x = 1:10, y = 1:10)
xy <- cbind(xy, YMul = 2)

ggplot(xy, aes(x = x, y = y * YMul)) + geom_line()

Или, если вам нужна функция в вашем примере:

plotfunc <- function(Data, YMul = 2)
{
    ggplot(cbind(Data, YMul), aes(x = x, y = y * YMul)) + geom_line()
}

plotfunc(xy)
YMul не обязательно должен быть частью data.frame. Это просто нужно определить в области, гдеggplot объект оценивается, который является глобальным, а не в функции. Justin
@ Джастин: Спасибо. Я не понял этого. Интересно, чтоggplot(), кажется, ищетYMul сначала в кадре данных, затем, если он не найден в глобальной среде, очевидно, пропуская аргументы функции. Я не нашел никакой информации о том, какggplot() обыскивает пространства имен, но опять же, я не выглядел очень усердно. jthetzel
Это гораздо проще запомнить и напечатать. Возможно, не очень хорошо, если ваш фрейм данных содержит миллиард строк, но удобно для других ситуаций. PatrickT
4

и ваш пример, кажется, работает с текущей версией.

Однако легко придумать варианты, которые до сих пор создают проблемы. Я был смущен подобным поведением, и вот как я нашел этот пост (лучший результат Google для "ggplot, как оценивать переменные при прохождении"). Например, если мы переместим ggplot из plotfunc:

xy <- data.frame(x=1:10,y=1:10)

plotfunc <- function(Data,YMul=2){
  geom_line(aes(x=x,y=y*YMul))
}

ggplot(xy)+plotfunc(xy)
# Error in eval(expr, envir, enclos) : object 'YMul' not found

В приведенном выше варианте «захват локальной среды» не является решением, поскольку ggplot не вызывается из функции, и только ggplot имеет аргумент «environment =».

Но теперь есть семейство функций "aes_", "aes_string", "aes_q", которые похожи на "aes", но захватывают локальные переменные. Если мы используем «aes_» выше, мы все равно получим ошибку, потому что теперь он не знает о «x». Но легко обратиться к данным напрямую, что решает проблему:

plotfunc <- function(Data,YMul=2){
  geom_line(aes_(x=Data$x,y=Data$y*YMul))
}
ggplot(xy)+plotfunc(xy)
# works
0

это работает. И если вы выполняете код внутри функции сYMul определяется глобально, это работает. Я не полностью понимаю внутреннюю работуggplot но это работает ...

YMul <- 2

plotfunc <- function(Data){
    ggplot(Data,aes(x=x,y=y*YMul))+geom_line()
}

plotfunc(xy)
37

Я бы запечатлел местную среду,

xy <- data.frame(x=1:10,y=1:10)

plotfunc <- function(Data, YMul = 2){
    .e <- environment()
    ggplot(Data, aes(x = x, y = y*YMul), environment = .e) + geom_line()
}

plotfunc(xy)
Это на самом деле просто - но я согласен, что это должно быть по умолчанию. Я думал, что aes в ggplot работает как замыкание, сохраняя все использованные объекты со стороны; очевидно, это не так. Спасибо fabb
+ 1 - @kohske и @baptiste. Мне тоже это нравится больше всего. Стоит отметить, однако, что он делает что-то отличное от моего решения, что можно увидеть по: (1) удалениюy=1:10 из data.framexy; (2) положитьy<-1:10 в глобальной среде; и (3) положитьy<-10:1 в теле функции перед вызовом ggplot. По сути, мое решение позволяет передавать выбранные аргументы, не изменяя правила видимости. Ваш полностью меняет обзорное поведениеggplot() (вот почему мне это нравится). Josh O'Brien
Честно говоря, я думаю, это должно быть по умолчанию. То же самое с plyr, я всегда сильно смущаюсь, когда ** ply не находит переменную, которую могли бы найти другие функции R с обычными правилами области видимости. baptiste
@ ДжошО'Брайен да. Я также придерживаюсь правила, что аргументы aes () всегда должны исходить из data.frame, и в этом случае единственными объектами, которые я могу захотеть выбрать из окружающей среды, являются такие параметры, как YMu. baptiste
Возможно, это официальный (но недокументированный) способ, я думаю. kohske

Похожие вопросы