【问题标题】:writing R function with ggplot用ggplot写R函数
【发布时间】:2020-05-27 08:19:22
【问题描述】:

我必须以相同的格式绘制多个数据集,在多次复制粘贴代码后,我决定编写一个函数。 我理解 R 中的简单函数,并设法编写了以下内容:

testplot <- function(data, mapping){
output <- ggplot(data) +
  geom_bar(mapping,
           stat="identity", 
             position='stack')
}
p <- testplot(df, aes(x=xvar, y=yvar, fill=type))

这很好,但是,我的情节更复杂,需要“数据”参数单独进入每个组件:

output <- ggplot() +
  geom_bar(df1, mapping,
           stat="identity", 
             position='stack')+
geom_errorbar(df1, ...)+
geom+bar(df2, mapping,
...+
geom_errorbar(df2, ...)

但是当我编写函数并尝试将其运行为

output <- ggplot() +
  geom_bar(data, mapping,
           stat="identity", 
             position='stack')
}
p <- testplot(df, aes(x=xvar, y=yvar, fill=type))

它给了我一个错误:

Error: `data` must be a data frame, or other object coercible by `fortify()`, not an S3 object with class uneval Did you accidentally pass `aes()` to the `data` argument?

有办法解决吗?

编辑:当我尝试包含 2 个这样的数据框时:

testplot <- function(data, data2, mapping){
output <- ggplot() +
 geom_bar(data=data, mapping=mapping,
           stat="identity", 
             position='stack',
           width = barwidth)+
geom_bar(data2=data2, mapping=mapping,
           stat="identity", 
             position='stack',
           width = barwidth)
}



p <- testplot(data=df, data2=df2, mapping=aes(x=norms_number, y=coeff.BLDRT, fill=type))

它说“忽略未知参数:data2”

【问题讨论】:

  • 我很困惑。您实际运行的是哪个版本的testplot?你在哪里定义了df?我在这里没有看到output 与p 有什么关系。
  • data2 不是参数,只有data,因此您的第二行应该是geom_bar(data=data2, mapping = ...,请参阅我编辑的其余部分以获取完整说明。

标签: r function dataframe ggplot2


【解决方案1】:

ggplot2 层函数的大多数第一个参数保留给来自aes 的mapping 参数。 因此,在您的函数定义中,您有一个数据框“数据”被隐式分配给 mapping 变量。 要解决此问题,请在函数定义中明确分配 data = data。

例如

output <- ggplot() +
 geom_bar(data = data, mapping = mapping,
          stat="identity", 
            position='stack')
}

编辑:

有很多方法可以做到这一点,这实际上取决于您希望您的功能有多复杂。如果您要坚持全局美学映射,那么您可以将映射留在主 ggplot 调用中并分配data = NULL,然后指定哪个数据框将与哪个图层相关联。 考虑以下可重现的示例

library(ggplot2)
data1 <- data.frame(v1=rnorm(10, 50, 20), v2=rnorm(10,30,5))
data2 <- data.frame(v1=rnorm(10, 100, 20), v2=rnorm(10,50,10))
plot_custom_ggplot <- function(df1, df2, mapping) {
    ggplot(data = NULL, mapping = mapping) +
      geom_point(data = df1, color = "blue") +
      geom_line(data = df2, color = "red")
}

plot_custom_ggplot(data1,data2, aes(x = v1,y = v2))

在此示例中,每个 geom_* 层函数的映射变量留空,而是从主 ggplot 调用继承映射。

这通常是每个层函数知道使用什么数据的方式,因为通常它是在主ggplot函数中继承的。每当您指定 data 参数或 mapping 参数时,您通常会覆盖继承的值。尝试在主调用中找到任何缺少的必需 aes 映射。

library(ggplot2)
data1 <- data.frame(v1=rnorm(10, 50, 20), v2=rnorm(10,30,5))
data2 <- data.frame(v1=rnorm(10, 100, 20), v2=rnorm(10,50,10), z = c("A","B"))
plot_custom_ggplot <- function(df1, df2, mapping) {
    ggplot(data = NULL, mapping = mapping) +
      geom_point(data = df1, color = "blue") +
      geom_line(data = df2, mapping = aes(color = z)) #inherits x and y mapping from main ggplot call.
}

plot_custom_ggplot(data1,data2, aes(x = v1,y = v2))

但是,如果您还指定了data,则添加额外的 aes 映射是有风险的。这是因为您的 data 变量可能并不总是包含正确的列。

plot_custom_ggplot(df1 = data2, df2 = data1, aes(x = v1, y = v2))
#Error in FUN(X[[i]], ...) : object 'z' not found
#
#the column z is not present in data1 object - 
#R then looked globally for a z object and didnt find anything.

我认为在使用 ggplot 时使用 tidy data 是最佳实践,因为事情变得如此简单。 通常没有理由使用多个数据帧。特别是如果您计划对所有数据框使用一组映射。一个很好的例外是,如果您正在为自定义 R 对象编写绘图函数,并且您知道它是如何定义的。

否则,请考虑并比较这两个函数在此示例中的工作方式:

data1 <- data.frame(v1=rnorm(20, 50, 20), v2=rnorm(20,30,5), letters= letters[1:20], id = "df1")
data2 <- data.frame(v1=rnorm(20, 100, 20), v2=rnorm(20,50,10), letters = letters[17:26], id = "df2")

set.seed(76)
plot_custom_ggplot2 <- function(df, mapping) {
  ggplot(data = df, mapping = mapping) +
    geom_bar(stat = "identity",
             position="stack") 
}

plot_custom_ggplot <- function(df1, df2, mapping) {
  ggplot(data = NULL, mapping = mapping) +
    geom_bar(data = df1, stat = "identity",
         position="stack") +
    geom_bar(data = df2, stat = "identity",
         position="stack")
}

plot_custom_ggplot(data1,data2, aes(x = letters,y = v2, fill = id))
plot_custom_ggplot2(rbind(data1,data2), aes(x = letters, y = v2, fill = id))

在第一个图中,q、r、s 和 t 的红色条隐藏在蓝色条后面。这是因为它们作为layers 相互叠加。在第二个图中,这些值实际上是堆叠的,因为这些值是在一个层中添加在一起的,而不是两个单独的层。

我希望这能给你足够的信息来编写你的 ggplot 函数。

【讨论】:

  • 原则上可行,但我如何称呼第二个数据帧?我将编辑帖子以说明它
【解决方案2】:
library(tidyverse)

testplot <- function(df1, df2, mapping){

  a <- ggplot() + 
    geom_point(data = df1, mapping = mapping) +
    geom_point(data = df2, mapping = mapping)

  return(a)

}

mtcars2 <- mtcars / 100 # creating a separate dataframe to provide the function

testplot(mtcars, mtcars2, mapping = aes(x = drat, y = vs))

从您的示例中,您有“data2=data2” - geom_bar 没有参数“data2”,只有数据。我得到了上面的工作,所以适应你的目的也应该工作!

【讨论】:

  • 哦,这很有道理!它适用于 geom_bar,但由于某种原因与 geom_errorbar 的工作方式不同(一直说“无法添加 ggproto 对象”)。知道这意味着什么吗?
  • 你能提供一个可重现的例子吗?您确定要将所有内容添加到 ggplot() 中吗? (没有遗漏的加号)
【解决方案3】:

我拆分数据框的原因是因为我想要一个分组和堆叠图,并使用了这个问题: How to plot a Stacked and grouped bar chart in ggplot?

映射必须不同,这样它们才不会相互重叠(所以它是 x=var1,然后是 x=var1+barwidth)

无论如何,我可以用多个 geom_bar 绘制一个图,但它是随后的 geom_errorbar 在单个函数中不起作用。我只是在最后单独添加了错误栏,也许我会在其他时间研究其他选项。

我意识到这些已经是函数,所以可能不打算以这种方式使用,也许这就是我不能在一个函数中执行多个 geom_errorbar 的原因。我只是希望我的代码更具可读性,因为我必须绘制相同的东西 12 次,差别很小,而且很长。不过,也许有一种更优雅的方式来做到这一点。

【讨论】:

    猜你喜欢
    • 2011-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-06
    • 2011-07-03
    相关资源
    最近更新 更多