如何在 ggplot 函数中传递可选参数

How To Pass An Optional Parameter Inside a function of ggplot

我想将 plot1 函数中的 ybreak 参数设为可选。如果未指定此参数(NULL 条件),那么我只想 return g ggplot,否则使用自定义 ybreak。我通过引用类似的答案尝试了以下代码,但它不起作用。

plot1 <- function(df, x, y, ybreak = NULL) {
  g <- ggplot(df, aes_string(x = x, y = y)) 

  if (is.na(ybreak) == F) {
    g + scale_y_continuous(breaks = ybreak)
  }
  else {
    g
  }
}

plot1(mtcars, x = "mpg", y = "disp")
plot1(mtcars, x = "mpg", y = "disp", ybreak = seq(70, 500, by = 50))


> plot1(mtcars, x = "mpg", y = "disp")
Error in if (is.na(ybreak) == F) { : argument is of length zero
> plot1(mtcars, x = "mpg", y = "disp", ybreak = seq(70, 500, by = 50))
Warning message:
In if (is.na(ybreak) == F) { :
  the condition has length > 1 and only the first element will be used

第一种情况:ybreak = NULL

is.na(NULL)

Returns:

logical(0)

因此(因为 logical(0) 什么都不是):

is.na(NULL) == FALSE

Returns:

logical(0)

但是如果我们使用 is.nullNULL 什么都不是)而不是 is.naNA 什么(只是不是数字)):

is.null(NULL)

Returns:

[1] TRUE

然后:

is.null(NULL) == FALSE
[1] FALSE

第二种情况:ybreak = seq(70, 500, by = 50)

is.na(seq(70, 500, by = 50))
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
if (is.na(seq(70, 500, by = 50)) == FALSE) print("something")
[1] "something"
Warning message:
  In if (is.na(seq(70, 500, by = 50)) == FALSE) print("something") :
  the condition has length > 1 and only the first element will be used

但我们可以使用 all 一次检查多个布尔值:

if (all(is.na(seq(70, 500, by = 50)) == FALSE)) print("something")
l[1] "something"

注意:请参阅下面的编辑

R 的省略号或三点功能旨在处理可选参数。如果您的函数将有更多可选参数,这可能很有用。在您提供的示例中,按以下方式构造输入参数。

plot1 <- function(df, x, y, ...) {

}

处理 ... 以查找特定变量名可能有点棘手,但使用函数 evalsubstitutealist 可以提供帮助有了这个。添加以下行会将可选参数放入列表中。

plot1 <- function(df, x, y, ...) {
    args <- eval(substitute(alist(...)))
    inputs <- purrr::map(args, as.list)
}

注意:需要 purrr 包。

要评估所有可选参数以查找特定名称,您可以使用与上述类似的代码。这是完整的例子。

library(ggplot2)

plot1 <- function(df, x, y, ...) {

    # eval inputs
    args <- eval(substitute(alist(...)))
    inputs <- purrr::map(args, as.list)
    print(args)
    print(inputs)

    # define base plot
    g <- ggplot(df, aes_string(x = x, y = y)) + geom_point()

    # return chart with breaks only if optional arguments are present
    # and if ybreaks exists
    if (length(inputs) > 0 && !is.na(inputs$ybreak)) {

        # rebuild seq
        breaks <- inputs$ybreak
        new_seq <- seq(breaks[[2]], breaks[[3]], by = breaks$by)

        # add to chart
        g <- g + scale_y_continuous(breaks = new_seq)
    }

    # return chart 
    return(g)
}

如果您有多个可选参数,请将 is.na(inputs$ybreak) 条件嵌套在 length(inputs) > 0 中。由于仅当提交了一个或多个可选参数时才需要评估可选参数。

根据您的功能以及您打算如何使用它,您可以使用更简单的方法,例如:

plot1 <- function(df, x, y, ...) {
   args <- list(ybreaks = ..1)
}

但是,对于包或生产代码,以前的方法可能是更好的选择。

有关详细信息,请参阅 Advanced R: Chapter 6 Functions

编辑:

原回复仍然有效。但是,我想建议一种处理可选参数的替代方法。 rlang 包中的函数 list2dots_list 更易于使用,并且可以更好地控制省略号 ...。例如,plot1 函数将重组为:

plot1 <- function(df, x, y, ...) {
-    args <- eval(substitute(alist(...)))
-    inputs <- purrr::map(args, as.list)
+    args <- rlang::list2(...)
     # evaluate arguments using
     # args$my_optional_argument or
     # args[["my_optional_argument"]]
}

希望对您有所帮助!