在 R 中的另一个函数中使用 ggplot()

发布于 2024-10-19 07:58:15 字数 834 浏览 5 评论 0原文

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

考虑一个名为 means 的 data.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 函数,但最终我希望让我的绘图函数做更复杂的事情!:))

I'm trying to write a simple plot function, using the ggplot2 library. But the call to ggplot doesn't find the function argument.

Consider a data.frame called means that stores two conditions and two mean values that I want to plot (condition will appear on the X axis, means on the 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

So it seems that ggplot is calling eval, which can't find the argument meansdf. Does anyone know how I can successfully pass the function argument to ggplot?

(Note: Yes I could just call the ggplot function directly, but in the end I hope to make my plot function do more complicated stuff! :) )

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

千鲤 2024-10-26 07:58:15

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

对于更多编程用途,例如,如果您希望用户能够为各种美观指定列名称作为参数,或者如果此函数位于需要传递 < 的包中code>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")
}

The "proper" way to use ggplot programmatically is to use aes_string() instead of aes() and use the names of the columns as characters rather than as objects:

For more programmatic uses, for example if you wanted users to be able to specify column names for various aesthetics as arguments, or if this function is going in a package that needs to pass R CMD CHECK without warnings about variable names without definitions, you can use aes_string(), with the columns needed as characters.

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")
}
是伱的 2024-10-26 07:58:15

正如 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 的数据框,即 Meansdf 本身。


事实上,在全局环境中而不是在调用环境中查找变量实际上是 ggplot2 中的一个已知错误 哈德利认为目前无法修复。
如果希望使用局部变量(例如 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)

As Joris and Chase have already correctly answered, standard best practice is to simply omit the meansdf$ part and directly refer to the data frame columns.

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

This works, because the variables referred to in aes are looked for either in the global environment or in the data frame passed to ggplot. That is also the reason why your example code - using meansdf$condition etc. - did not work: meansdf is neither available in the global environment, nor is it available inside the data frame passed to ggplot, which is meansdf itself.


The fact that the variables are looked for in the global environment instead of in the calling environment is actually a known bug in ggplot2 that Hadley does not consider fixable at the moment.
This leads to problems, if one wishes to use a local variable, say, scale, to influence the data used for the plot:

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")
}

A very nice workaround for this case is provided by Winston Chang in the referenced GitHub issue: Explicitly setting the environment parameter to the current environment during the call to ggplot.
Here's what that would look like for the above example:

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)
初雪 2024-10-26 07:58:15

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

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 轴标签也会发生变化。

Here is a simple trick I use a lot to define my variables in my functions environment (second line):

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")

Note how the y-axis label also changes when different variables or data-sets are used.

屌丝范 2024-10-26 07:58:15

我认为您不需要在函数调用本身中包含 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)

生成:

在此处输入图像描述

I don't think you need to include the meansdf$ part in your function call itself. This seems to work on my machine:

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)

to produce:

enter image description here

马蹄踏│碎落叶 2024-10-26 07:58:15

这是讨论的问题的示例 早些时候。基本上,它归结为 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 包后,上面的代码可以工作。正如评论中所解释的,原因是 ggplot 将在全局环境中(当数据帧专门添加为 Meandf$... 时)或在提到的环境中查找 aes 中的变量。

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

This is an example of a problem that is discussed earlier. Basically, it comes down to ggplot2 being coded for use in the global environment mainly. In the aes() call, the variables are looked for either in the global environment or within the specified dataframe.

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")
}

EDIT:

update: After seeing the other answer and updating the ggplot2 package, the code above works. Reason is, as explained in the comments, that ggplot will look for the variables in aes in either the global environment (when the dataframe is specifically added as meandf$... ) or within the mentioned environment.

For this, be sure you work with the latest version of ggplot2.

日记撕了你也走了 2024-10-26 07:58:15

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

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 创建于 2019-01-10 package (v0.2.1)

另一种解决方法,但将带引号的变量传递给自定义绘图函数是使用 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")

创建于 2019-01-10通过 reprex 包 (v0.2.1)

If is important to pass the variables (column names) to the custom plotting function unquoted, while different variable names are used within the function, then another workaround that I tried, was to make use of match.call() and eval (like here as well):

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)

Created on 2019-01-10 by the reprex package (v0.2.1)

Another workaround, but with passing quoted variables to the custom plotting function is using 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")

Created on 2019-01-10 by the reprex package (v0.2.1)

悲凉≈ 2024-10-26 07:58:15

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

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)

This frustrated me for some time. I wanted to send different data frames with different variable names and I wanted the ability to plot different columns from the data frame. I finally got a work around by creating some dummy (global) variables to handle plotting and forcing assignment inside the function

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)

}

then in the main code I can just call the function

plotgraph(data,data$time,data$Y1)
plotgraph(data,data$time,data$Y2)
天煞孤星 2024-10-26 07:58:15

简短答案:使用 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')

Short answer: Use qplot

Long answer:
In essence you want something like this:

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

But that lacks flexibility because you must stick to consistent column naming to avoid the annoying R scope idiosyncrasies. Of course the next logic step is:

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

But then that starts looking suspiciously like a call to qplot(), right?

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

Of course now you'd like to change things like scale titles but for that a function comes handy... the good news is that scoping issues are mostly gone.

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')
简单爱 2024-10-26 07:58:15

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

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

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

Another workaround is to define the aes(...) as a variable of your function :

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

This just worked fine for me on a similar topic

花之痕靓丽 2024-10-26 07:58:15

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

myplot <- ggplot(......) + 无论你想要什么
print(myplot)

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

You don't need anything fancy. Not even dummy variables. You only need to add a print() inside your function, is like using cat() when you want something to show in the console.

myplot <- ggplot(......) + Whatever you want here
print(myplot)

It worked for me more than one time inside the same function

把人绕傻吧 2024-10-26 07:58:15

基于精彩信息(2023 年)
https://thomasadventure.blog/posts/turning-your -ggplot2-code-into-a-function/,您可以在函数内使用{{argument}}。因此,

scatter_plot2 <- function(data, x, y) {
  ggplot(data, aes({{x}}, {{y}})) +
    geom_point(color = "yellow")
}
scatter_plot2(mtcars, hp, mpg)

希望这可以帮助那些浏览此页面寻找解决方案的人。 {{}} 来自 rlang 包

Based on the wonderful information (in 2023) at
https://thomasadventure.blog/posts/turning-your-ggplot2-code-into-a-function/, you can use {{argument}} within the function. So,

scatter_plot2 <- function(data, x, y) {
  ggplot(data, aes({{x}}, {{y}})) +
    geom_point(color = "yellow")
}
scatter_plot2(mtcars, hp, mpg)

Hope this helps someone who comes across this page looking for a solution. {{}} is from the rlang package

空名 2024-10-26 07:58:15

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

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")
    }

I just generate new data frame variables with the desired names inside the function:

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")
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文