【问题标题】:Use of ggplot() within another function in R在 R 中的另一个函数中使用 ggplot()
【发布时间】:2011-07-03 16:03:16
【问题描述】:

我正在尝试使用 ggplot2 库编写一个简单的绘图函数。但是对 ggplot 的调用没有找到函数参数。

考虑一个名为meansdata.frame,它存储了我要绘制的两个条件和两个平均值(条件将出现在X 轴上,表示在Y 轴上)。

library(ggplot2)
m <- c(13.8, 14.8)
cond <- c(1, 2)
means <- data.frame(means=m, condition=cond)
means
# The output should be:
#     means    condition
#   1 13.8     1
#   2 14.8     2

testplot <- function(meansdf)
{
  p <- ggplot(meansdf, aes(fill=meansdf$condition, y=meansdf$means, x = meansdf$condition))
  p + geom_bar(position="dodge", stat="identity")
}

testplot(means)
# This will output the following error:
# Error in eval(expr, envir, enclos) : object 'meansdf' not found

看来ggplot调用的是eval,却找不到参数meansdf。有谁知道我如何成功地将函数参数传递给 ggplot?

(注意:是的,我可以直接调用 ggplot 函数,但最后我希望让我的 plot 函数做更复杂的事情!:) )

【问题讨论】:

    标签: r ggplot2


    【解决方案1】:

    正如 Joris 和 Chase 已经正确回答的那样,标准的最佳做法是简单地省略 meansdf$ 部分并直接引用数据框列。

    testplot <- function(meansdf)
    {
      p <- ggplot(meansdf, 
                  aes(fill = condition,
                      y = means,
                      x = condition))
      p + geom_bar(position = "dodge", stat = "identity")
    }
    

    这是可行的,因为aes 中引用的变量要么在全局环境中查找,要么在传递给ggplot 的数据框中查找。这也是您的示例代码 - 使用 meansdf$condition 等 - 不起作用的原因:meansdf 在全局环境中既不可用,也不在传递给 ggplot 的数据帧内可用,即 @987654329 @ 自己。


    在全局环境中而不是在调用环境中查找变量的事实实际上是 a known bug in ggplot2 Hadley 目前认为不可修复。 如果希望使用局部变量(例如 scale)来影响用于绘图的数据,这会导致问题:

    testplot <- function(meansdf)
    {
      scale <- 0.5
      p <- ggplot(meansdf, 
                  aes(fill = condition,
                      y = means * scale,   # does not work, since scale is not found
                      x = condition))
      p + geom_bar(position = "dodge", stat = "identity")
    }
    

    Winston Chang 在引用的 GitHub 问题中为这种情况提供了一个非常好的解决方法:在调用 ggplot 期间将environment 参数显式设置为当前环境。 以下是上述示例的样子:

    testplot <- function(meansdf)
    {
      scale <- 0.5
      p <- ggplot(meansdf, 
                  aes(fill = condition,
                      y = means * scale,
                      x = condition),
                  environment = environment())   # This is the only line changed / added
      p + geom_bar(position = "dodge", stat = "identity")
    }
    
    ## Now, the following works
    testplot(means)
    

    【讨论】:

    • 如果我只想在函数内使用局部变量调用 geom_bar/geom_line/geom_point 怎么办 - 环境是未知参数。
    • 我无法通过p &lt;- ggplot ... p + ggplot ... 工作获得这些建议。 ggplot 有什么变化吗?
    • 但是,这种类似的乐趣在我的机器上工作(ggplot2 2.2.1.9000):gg_fun &lt;- function(data, col){ p &lt;- ggplot(data, aes(x = col), environment = environment()) p + geom_histogram() }gg_fun(mtcars, hp)
    【解决方案2】:

    以编程方式使用ggplot 的“正确”方式是使用aes_string() 而不是aes(),并将列的名称用作字符而不是对象:

    对于更多的编程用途,例如,如果您希望用户能够为各种美学指定列名作为参数,或者如果此函数位于需要传递 R CMD CHECK 而没有警告变量名没有定义的包中,您可以使用aes_string(),将所需的列作为字符。

    testplot <- function(meansdf, xvar = "condition", yvar = "means",
                         fillvar = "condition") {
        p <- ggplot(meansdf,
                    aes_string(x = xvar, y= yvar, fill = fillvar)) +
                 geom_bar(position="dodge", stat="identity")
    }
    

    【讨论】:

      【解决方案3】:

      这是一个简单的技巧,我经常在我的函数环境中定义我的变量(第二行):

      FUN <- function(fun.data, fun.y) {
          fun.data$fun.y <- fun.data[, fun.y]
          ggplot(fun.data, aes(x, fun.y)) + 
              geom_point() + 
              scale_y_continuous(fun.y)    
      }
      
      datas <- data.frame(x = rnorm(100, 0, 1),
                          y = x + rnorm(100, 2, 2),
                          z = x + rnorm(100, 5, 10))
      FUN(datas, "y")
      FUN(datas, "z")
      

      请注意,当使用不同的变量或数据集时,y 轴标签也会发生变化。

      【讨论】:

      • 请注意(现在?不确定它是否一直存在。)一种不那么侵入性/hacky 的解决方案,它只适用于标准 ggplot 命令,并且在任何情况下都可以使用。见my answer below
      • @jhin,我同意这种方法是 hacky,但最好我可以告诉你的答案没有提供如何传递第二个参数的示例(例如 xy in aes) 到 ggplot。您能否在 testplot 函数中使用两个参数在您的答案中添加一个示例?
      • @r3robertson 不确定我明白你在问什么。在我的示例中,我将多个参数传递给 ggplot,其中包括 xy。您在示例中究竟缺少什么?如果您想从另一个数据帧传递,例如 xy,只需这样做 (aes(x=condition, y=otherdataframe$colname))...?
      【解决方案4】:

      我认为您不需要在函数调用本身中包含 meansdf$ 部分。这似乎适用于我的机器:

      meansdf <- data.frame(means = c(13.8, 14.8), condition = 1:2)
      
      testplot <- function(meansdf)
      {
      p <- ggplot(meansdf, aes(fill=condition, y=means, x = condition))
      p + geom_bar(position="dodge", stat="identity")
      }
      
      
      testplot(meansdf)
      

      生产:

      【讨论】:

      • 这里有个微妙的地方——意味着df是在全局环境中定义的。但是,即使不这样做,它也可以按照您的建议工作(没有方法 df$)。有谁知道这是为什么??
      • @trev :因为 ggplot 在数据框 meandf 的“环境”中查找变量。
      【解决方案5】:

      这是earlier 讨论的问题示例。基本上,归结为 ggplot2 被编码主要用于全球环境。在 aes() 调用中,变量要么在全局环境中要么在指定的数据帧中查找。

      library(ggplot2)
      means <- data.frame(means=c(13.8,14.8),condition=1:2)
      
      testplot <- function(meansdf)
      {
        p <- ggplot(meansdf, aes(fill=condition, 
                y=means, x = condition))
        p + geom_bar(position="dodge", stat="identity")
      }
      

      编辑:

      更新:在看到其他答案后并更新了 ggplot2 包,上面的代码就可以工作了。原因是,正如 cmets 中所解释的那样,ggplot 将在全局环境(当数据帧被特别添加为 meandf$... 时)或上述环境中查找 aes 中的变量。

      为此,请确保您使用最新版本的 ggplot2。

      【讨论】:

      • 这是不正确的。 ggplot 根据data 参数评估aes() 内的变量名称。您只需要按照其他答案所述进行操作即可。
      • @Aaron : ggplot 包必须更新到最新版本才能正常工作。请参阅编辑中的说明
      • 这里不应该用aes_string()吗??
      • 请注意,这不适用于传递给 ggplot 的数据中不包含的局部变量,例如在 testplot 函数中执行 scale &lt;- 0.5 然后 ggplot(meansdf, aes(fill = condition, y = means * scale, x = condition)) 失败。有关实际解决潜在问题的简单解决方法,请参阅下面的答案。
      【解决方案6】:

      这让我沮丧了一段时间。我想发送具有不同变量名称的不同数据帧,并且我希望能够从数据帧中绘制不同的列。我终于通过创建一些虚拟(全局)变量来处理函数内部的绘图和强制分配来解决问题

      plotgraph function(df,df.x,df.y) {
      
      dummy.df <<- df
      dummy.x <<- df.x
      dummy.y <<- df.y
      
      p = ggplot(dummy.df,aes(x=dummy.x,y=dummy.y,.....)
      print(p)
      
      }
      

      然后在主代码中我可以调用函数

      plotgraph(data,data$time,data$Y1)
      plotgraph(data,data$time,data$Y2)
      

      【讨论】:

      • 恕我直言,这是最实用的解决方案,即使我真的不喜欢全局变量。
      【解决方案7】:

      如果将变量(列名)不带引号传递给自定义绘图函数很重要,而函数中使用了不同的变量名,那么我尝试的另一个解决方法是使用match.call()eval (就像here 一样):

      library(ggplot2)
      
      meansdf <- data.frame(means = c(13.8, 14.8), condition = 1:2)
      
      testplot <- function(df, x, y) {
        arg <- match.call()
        scale <- 0.5
        p <- ggplot(df, aes(x = eval(arg$x),
                            y = eval(arg$y) * scale,
                            fill = eval(arg$x)))
        p + geom_bar(position = "dodge", stat = "identity")
      }
      
      testplot(meansdf, condition, means)
      

      reprex package (v0.2.1) 于 2019 年 1 月 10 日创建

      另一种解决方法,但将引用的变量传递给自定义绘图函数是使用get()

      meansdf <- data.frame(means = c(13.8, 14.8), condition = 1:2)
      
      testplot <- function(df, x, y) {
        scale <- 0.5
        p <- ggplot(df, aes(x = get(x),
                            y = get(y) * scale,
                            fill = get(x)))
        p + geom_bar(position = "dodge", stat = "identity")
      }
      
      testplot(meansdf, "condition", "means")
      

      reprex package (v0.2.1) 于 2019 年 1 月 10 日创建

      【讨论】:

      • 获得完美作品!比其他答案简单得多
      【解决方案8】:

      简答:使用 qplot

      长答案: 本质上你想要这样的东西:

      my.barplot <- function(x=this.is.a.data.frame.typically) {
         # R code doing the magic comes here
         ...
      }
      

      但这缺乏灵活性,因为您必须坚持一致的列命名以避免烦人的 R 范围特质。当然接下来的逻辑步骤是:

      my.barplot <- function(data=data.frame(), x=..., y....) {
         # R code doing something really really magical here
         ...
      }
      

      但是这开始看起来像调用 qplot() 一样可疑,对吧?

      qplot(data=my.data.frame, x=some.column, y=some.other column,
            geom="bar", stat="identity",...)
      

      当然,现在您想更改诸如比例标题之类的内容,但为此功能会派上用场……好消息是范围界定问题已基本消失。

      my.plot <- qplot(data=my.data.frame, x=some.column, y=some.other column,...)
      set.scales(p, xscale=scale_X_continuous, xtitle=NULL,
                 yscale=scale_y_continuous(), title=NULL) {
        return(p + xscale(title=xtitle) + yscale(title=ytitle))
      }
      my.plot.prettier <- set.scale(my.plot, scale_x_discrete, 'Days',
                                    scale_y_discrete, 'Count')
      

      【讨论】:

        【解决方案9】:

        另一种解决方法是将 aes(...) 定义为函数的变量:

        func<-function(meansdf, aes(...)){}
        

        这对我来说在类似的主题上效果很好

        【讨论】:

          【解决方案10】:

          你不需要任何花哨的东西。甚至没有虚拟变量。你只需要在你的函数中添加一个 print() ,就像你想在控制台中显示一些东西时使用 cat() 一样。

          myplot

          它在同一个函数中为我工作了不止一次

          【讨论】:

            【解决方案11】:

            我只是在函数内生成具有所需名称的新数据框变量:

            testplot <- function(df, xVar, yVar, fillVar) {
                df$xVar = df[,which(names(df)==xVar)]
                df$yVar = df[,which(names(df)==yVar)]
                df$fillVar = df[,which(names(df)==fillVar)]
                p <- ggplot(df,
                            aes(x=xvar, y=yvar, fill=fillvar)) +
                         geom_bar(position="dodge", stat="identity")
                }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-10-24
              • 1970-01-01
              • 1970-01-01
              • 2021-12-27
              • 1970-01-01
              • 2016-02-27
              • 2014-07-14
              • 1970-01-01
              相关资源
              最近更新 更多