如何在ggplot个人函数之间传递变量?

huangapple go评论63阅读模式
英文:

How can I pass variables between ggplot personal functions?

问题

I understand your request. Here is the translated text:

我试图基于ggplot创建一些自定义绘图函数。函数的创建非常简单直接,但是我无法找到在这些函数之间共享信息的方法,以帮助绘图。

这里是一个简化的例子:

library(ggplot2)

my_ggplot <- function(dat, x, y, cols) {
  ggplot(dat, aes(!!sym(x), !!sym(y))) + 
    geom_point(color = cols$dots)
}

my_geom <- function(dat, x, cols) {
  xmean <- mean(dat[[x]], na.rm = T)
  exit <- list(
    geom_smooth(aes(color = cols$line), method = "loess", se = FALSE),
    geom_vline(xintercept = xmean, color = cols$line)
  )
}

mycolors <- list(dots = "blue", line = "red")

在这里,my_ggplot创建了基本绘图,如果我愿意的话,可以使用my_geom为它添加一些线条。我需要一种控制颜色的方法,所以我定义了一个S3类对象,在这个例子中它只是列表mycolors

所以,当将所有参数传递给每个函数时,结果是完全正常的:

my_ggplot(mpg, 'displ', 'hwy', mycolors) +
  my_geom(mpg, "displ", mycolors)

但是我希望能够从my_ggplot“继承”值到my_geom,使得以下代码能够工作:

my_ggplot(mpg, 'displ', 'hwy', mycolors) +
  my_geom()

但是,my_geom仍然保持一定程度的独立性,以便我可以将它与不同的ggplot()函数一起使用。对我来说特别重要的是能够在函数之间传递数据集,在这个例子中,我计算了均值,并在后面的geom_vline中使用它来保持简单,但在实践中,我需要在将值传递给几何对象之前进行一些数据整理和计算。

英文:

I am trying to create some functions based on ggplot to create custom plots. The creation of functions is very easy and straight forward, but I cannot figure out the way to share information between the functions to help in the plot creation.

Here is a simplified example

library(ggplot2)

my_ggplot &lt;- function(dat, x, y, cols) {
  ggplot(dat, aes(!!sym(x), !!sym(y))) + 
    geom_point(color = cols$dots)
}


my_geom &lt;- function(dat, x, cols) {
  xmean &lt;- mean(dat[[x]], na.rm = T)
  exit &lt;- list(
    geom_smooth(aes(color = cols$line), method = &quot;loess&quot;, se = FALSE),
    geom_vline(xintercept = xmean, color = cols$line)
  )
}


mycolors &lt;- list(dots = &quot;blue&quot;, line = &quot;red&quot;)

Here, my_plot creates the base plot and, if I want to, I can add couple of lines to it using my_geom. I need a way to control the colors so, I have defined an S3 class object, which in this example is simply the list mycolors.

So, when passing all the parameters to each function, the result is perfectly fine:

my_ggplot(mpg, &#39;displ&#39;, &#39;hwy&#39;, mycolors) +
  my_geom(mpg, &quot;displ&quot;,  mycolors)

But I want to be able to "inherit" values from my_ggplot to my_geom so that the following code could work:

my_ggplot(mpg, &#39;displ&#39;, &#39;hwy&#39;, mycolors) +
  my_geom()

But still, my_geom keeps certain level of independence in case I want to use it with different ggplot() functions. Especially important for me is to be able to pass the dataset between functions, in the example I calculate the mean and use it later in geom_vline to keep it simple, but in practice I need to do some data wrangling and calculations before I can pass the values to the geom.

答案1

得分: 2

另一个选择。通过将数据和颜色参数定义为NULL,并使用简单的if/else语句基于提供的数据的存在性创建列表,这可能起作用。这真的取决于用例。在我的例子中,有两个if else语句。一个用于数据,另一个用于颜色(如果数据未传递到第二个函数)。可能最好是创建自己的stat,这真的取决于你心中所想的数据转换和几何形状。geom_vline是一个有点特殊的情况,可能不是最好的选择示例。这个额外努力的优势在于它不需要为你的线硬编码y美学。我认为Stefan的颜色处理方法很棒 - 我在这里也用过这个方法。

library(ggplot2)

my_ggplot <- function(dat, x, y, cols) {
  ggplot(dat, aes(x = !!sym(x), y = !!sym(y))) +
    geom_point(aes(color = "dots"), show.legend = F) +
    scale_color_manual(values = cols) 
}

StatMyline <- ggproto("StatMyline", Stat,
  compute_group = function(data, scales) {
    data.frame(
      x = mean(data$x),
      xend = mean(data$x),
      y = -Inf, yend = Inf
    )
  },
  required_aes = c("x", "y")
)
stat_myline <- function(mapping = NULL, data = NULL, geom = "segment",
                        position = "identity", na.rm = FALSE, show.legend = NA,
                        inherit.aes = TRUE, ...) {
  layer(
    stat = StatMyline, data = data, mapping = mapping, geom = geom,
    position = position, show.legend = show.legend, inherit.aes = inherit.aes,
    params = list(na.rm = na.rm, ...)
  )
}

mycolors <- list(dots = "blue", line = "red")

my_geom <- function(dat = NULL, x, cols = NULL) {
  if (!is.null(dat)) {
    xmean <- mean(dat[[x]], na.rm = T)
    list(
      geom_smooth(aes(color = "line"), method = "loess", se = FALSE, show.legend = F),
      geom_vline(aes(color = "line", xintercept = xmean), show.legend = F)
    )
  } else {
    list(
      geom_smooth(method = "loess", se = FALSE, aes(color = "line"), show.legend = F),
      stat_myline(aes(color = "line"), show.legend = F), 
      if(!is.null(cols)) scale_color_manual(values = cols) else NULL
    )
  }
}

p1 <- my_ggplot(mpg, "displ", "hwy", mycolors) +
  my_geom(mpg, "displ", mycolors) +
  ggtitle("With data + color ")

p2 <- my_ggplot(mpg, "displ", "hwy", mycolors) +
  my_geom() +
  ggtitle("Inheriting data + color")

p3 <- ggplot(mtcars, aes(hp, mpg)) +
  geom_point() +
  my_geom(cols = mycolors) +
  ggtitle("without my_ggplot")

library(patchwork)
p1 + p2 + p3
英文:

Another option. This might work by defining your data and color arguments as NULL, and with a simple if/else statement to create a list based on presence of provided data, respectively. It really depends on the use case. In my example, there are two if else statements. One for the data, the other for the color (in case the data was not passed to the second function).

It might be best to create your own stat, it really depends on what type of data transformation and geometry you have in mind. geom_vline is a bit of a special situation and might not be the best chosen example.

The advantage of this little bit of extra effort is that it doesn’t need a hard coded y aesthetic for your line.

I think Stefan's approach with the color is excellent - I've used this here too.

library(ggplot2)

my_ggplot &lt;- function(dat, x, y, cols) {
  ggplot(dat, aes(x = !!sym(x), y = !!sym(y))) +
    geom_point(aes(color = &quot;dots&quot;), show.legend = F) +
    scale_color_manual(values = cols) 
}

StatMyline &lt;- ggproto(&quot;StatMyline&quot;, Stat,
  compute_group = function(data, scales) {
    data.frame(
      x = mean(data$x),
      xend = mean(data$x),
      y = -Inf, yend = Inf
    )
  },
  required_aes = c(&quot;x&quot;, &quot;y&quot;)
)
stat_myline &lt;- function(mapping = NULL, data = NULL, geom = &quot;segment&quot;,
                        position = &quot;identity&quot;, na.rm = FALSE, show.legend = NA,
                        inherit.aes = TRUE, ...) {
  layer(
    stat = StatMyline, data = data, mapping = mapping, geom = geom,
    position = position, show.legend = show.legend, inherit.aes = inherit.aes,
    params = list(na.rm = na.rm, ...)
  )
}

mycolors &lt;- list(dots = &quot;blue&quot;, line = &quot;red&quot;)

my_geom &lt;- function(dat = NULL, x, cols = NULL) {
  ## if dat is provided, compute using your provided data and the provided color
  if (!is.null(dat)) {
    xmean &lt;- mean(dat[[x]], na.rm = T)
    list(
      geom_smooth(aes(color = &quot;line&quot;), method = &quot;loess&quot;, se = FALSE, show.legend = F),
      geom_vline(aes(color = &quot;line&quot;, xintercept = xmean), show.legend = F)
    )
  } else {
    list(
      geom_smooth(method = &quot;loess&quot;, se = FALSE, aes(color = &quot;line&quot;), show.legend = F),
      stat_myline(aes(color = &quot;line&quot;), show.legend = F), 
      if(!is.null(cols)) scale_color_manual(values = cols) else NULL
    )
  }
}

p1 &lt;- my_ggplot(mpg, &quot;displ&quot;, &quot;hwy&quot;, mycolors) +
  my_geom(mpg, &quot;displ&quot;, mycolors) +
  ggtitle(&quot;With data + color &quot;)

p2 &lt;- my_ggplot(mpg, &quot;displ&quot;, &quot;hwy&quot;, mycolors) +
  my_geom() +
  ggtitle(&quot;Inheriting data + color&quot;)

p3 &lt;- ggplot(mtcars, aes(hp, mpg)) +
  geom_point() +
  my_geom(cols = mycolors) +
  ggtitle(&quot;without my_ggplot&quot;)

library(patchwork)
p1 + p2 + p3
#&gt; `geom_smooth()` using formula = &#39;y ~ x&#39;
#&gt; `geom_smooth()` using formula = &#39;y ~ x&#39;
#&gt; `geom_smooth()` using formula = &#39;y ~ x&#39;

如何在ggplot个人函数之间传递变量?<!-- -->

<sup>Created on 2023-04-13 with reprex v2.0.2</sup>

答案2

得分: 1

以下是翻译好的部分:

一种可能的方法来消除对 datx 参数的依赖是使用 stat_summary 来计算映射到 x aes 上的变量的均值,并添加类似于我在 这篇帖子 上的回答中的 vline。其次,对于颜色,一个选项是映射到 color aes 上,并通过 scale_color_manual 设置颜色调色板。这样,颜色也可以在 my_geom 中使用。当然,这仅在通过 my_ggplot 创建图形时有效。不是完美的。

最后,这是一个从头创建的 ggplot 应用 my_geom 的示例:

英文:

One possible approach to remove the dependency on the dat and the x argument would be to use stat_summary to compute the mean of the variable mapped on the x aes and to add the vline similar to my answer on this post. Second, for the colors one option would be to map on the color aes and to set the color palette via scale_color_manual. This way the colors would be available in my_geom too. Of course does this only work when you create your plot via my_ggplot. Not perfect.

library(ggplot2)

my_ggplot &lt;- function(dat, x, y, cols) {
  ggplot(dat, aes(!!sym(x), !!sym(y))) +
    geom_point(aes(color = &quot;dots&quot;), show.legend = FALSE) +
    scale_color_manual(values = cols)
}

my_geom &lt;- function() {
  list(
    geom_smooth(aes(color = &quot;line&quot;), method = &quot;loess&quot;, se = FALSE, show.legend = FALSE),
    stat_summary(aes(xintercept = after_stat(x), y = 0, color = &quot;line&quot;),
      fun = mean, geom = &quot;vline&quot;, orientation = &quot;y&quot;, show.legend = FALSE
    )
  )
}

mycolors &lt;- list(dots = &quot;blue&quot;, line = &quot;red&quot;)

my_ggplot(mpg, &quot;displ&quot;, &quot;hwy&quot;, mycolors) +
  my_geom()
#&gt; `geom_smooth()` using formula = &#39;y ~ x&#39;

如何在ggplot个人函数之间传递变量?<!-- -->

Finally here is an example of applying my_geom to a ggplot created from scratch:

ggplot(mtcars, aes(hp, mpg)) +
  geom_point() +
  my_geom()
#&gt; `geom_smooth()` using formula = &#39;y ~ x&#39;

如何在ggplot个人函数之间传递变量?<!-- -->

huangapple
  • 本文由 发表于 2023年4月13日 19:22:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76004837.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定