【问题标题】:Local Variables Within aesaes 中的局部变量
【发布时间】:2012-05-26 09:53:06
【问题描述】:

当我使用 ggplot 绘图时,我试图在 aes 中使用局部变量。这是我的问题归结为本质:

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 中的局部变量)?

【问题讨论】:

  • 我认为是因为它仍然希望你传递 Ymul 但你只给 plotfunc(xy)
  • 不正确,应该使用默认值
  • 我正在运行上面的代码并且没有收到任何错误(2017 年 10 月 23 日),ggplot2 是否有更新来解释为什么现在可以工作?

标签: r ggplot2


【解决方案1】:

我会捕捉本地环境,

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)

【讨论】:

  • 我认为这可能是官方(但未记录)的方式。
  • 老实说,我认为它确实应该是默认值,不知何故。与 plyr 一样,当 **ply 找不到其他具有通常范围规则的 R 函数会找到的变量时,我总是感到非常困惑。
  • +1 -- @kohske 和 @baptiste。我也最喜欢这个。不过值得注意的是,它的作用与我的解决方案不同,如下所示: (1) 从 data.frame xy 中删除 y=1:10; (2)将y&lt;-1:10放到全球环境中; (3) 在调用 ggplot 之前将y&lt;-10:1 放入函数体中。本质上,我的解决方案允许传入选定的参数,而无需更改范围规则。你的完全改变了ggplot() 的作用域行为(这就是我喜欢它的原因)。
  • @JoshO'Brien 是的。我还遵守 aes() 的参数应始终来自 data.frame 的规则,在这种情况下,我可能想从周围环境中挑选的唯一对象是 YMu 之类的参数。
  • 这实际上是直截了当的 - 但我同意它应该是默认值。我认为 ggplot 中的 aes 就像一个闭包,保留周围所有使用过的对象;显然不是。谢谢!
【解决方案2】:

这是一种替代方法,允许您通过 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 谢谢!我知道有办法做到这一点,但从来没有花时间解决这个问题。很酷。
  • @Justin -- 很高兴这很有帮助。 从来没有花时间理清过 ggplot 的基于原型的作用域是如何工作的。例如,我刚刚在aes() 调用中放置了一个browser() 语句,在输入sys.frames() 时,我得到了一个包含23 个环境的列表,其中没有一个(??) 似乎允许直接访问该值的YMul。嗯。
  • 是的……它超越了我。我有一些 custom code 由 Kohske 提供的,它打破了新版本,我完全无法理解!总有一天我很想了解 proto...
  • @Justin 和 Josh:这里有 github 上的相关讨论:github.com/hadley/ggplot2/issues/248 似乎已经讨论过但没有实现。
  • +1 与 bquote 相同:eval(bquote(ggplot(Data,aes(x=x,y=y*.(YMul)))+geom_line()))。
【解决方案3】:

ggplot()aes 期望 YMuldata 数据框中的一个变量。尝试在其中包含YMull

感谢@Justin:ggplot()aes 似乎首先在data 数据框中寻找YMul,如果没有找到,则在全局环境中。我喜欢将这样的变量添加到数据框中,如下所示,因为这在概念上对我来说是有意义的。我也不必担心全局变量的更改会对函数产生意想不到的后果。但所有其他答案也是正确的。所以,选择适合你的。

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对象的范围内定义,它是全局的,而不是在函数中。
  • @贾斯汀:谢谢。我没有意识到这一点。有趣的是ggplot() 似乎首先在数据框中寻找YMul,然后如果在全局环境中没有找到,显然跳过了函数的参数。我还没有找到任何关于 ggplot() 如何搜索命名空间的信息,但是我又没怎么努力。
  • 这是迄今为止更容易记住和输入的选项。如果您的数据框有 10 亿行,可能不是很好,但在其他情况下很方便。
【解决方案4】:

我正在使用 ggplot2,您的示例似乎适用于当前版本。

但是,很容易想出仍然会造成麻烦的变体。我自己也被类似的行为弄糊涂了,这就是我找到这篇文章的原因(“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

【讨论】:

    【解决方案5】:

    你看过@wch (W. Chang) 给出的解决方案吗?

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

    我觉得这个更好

    本质上类似于@baptiste,但在调用 ggplot 时直接包含对环境的引用

    我在这里举报

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

    【讨论】:

    • 这是@baptiste 答案的副本,鉴于 OP 提供了可重现的示例,IMO 不必要地使用了不同的示例。我建议删除(也许用你链接的 Github 问题评论 baptiste)。
    【解决方案6】:

    如果您在函数之外执行代码,它会起作用。如果你在函数中执行代码并在全局定义 YMul,它就可以工作。我不完全理解 ggplot 的内部工作原理,但这有效...

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-04
      • 2013-10-06
      • 2012-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多