当 R 中缺少参数时,如何调用另一个函数以使用其默认参数?
When an arguments is missing in R, how to call another function to use its default argument?
我有一个带有多个可选参数的函数,可以使用 ggplot2 修改绘图。如果缺少任何这些参数,我想在 ggplot2 中使用它们的默认值。如果只有一个参数,可以这样做:
simple_plot <- function (color) {
p <- ggplot(mtcars, aes(cyl, mpg))
if(!missing(color))
{
p <- p + geom_point(col = color)
} else {
p <- p + geom_point() # use its default if the argument is missing
}
}
然而,当有多个参数来修改绘图的点形状、填充、轴标题、中断、限制等时,这不是一个好的解决方案。有更好的方法吗?
[编辑以澄清我的问题]
感谢那些回答我的问题的人。我想通过包含以下代码来澄清我的问题。将有多个函数要调用,每个函数都有多个参数。使用 do.call
似乎不是一种简单的实现方式。
better_plot <- function(col.y1, shape.y1, col.y2, shape.y2,
title.y1, breaks.y1, title.y2, breaks.y2, title){
p <- ggplot(mtcars, aes(x = cyl)) +
# 1st y data points
case_when(
missing(col.y1) & missing(shape.y1) ~ geom_point(aes(y = mpg)),
missing(col.y1) & !missing(shape.y1) ~ geom_point(aes(y = mpg), shape = shape.y1),
!missing(col.y1) & missing(shape.y1) ~ geom_point(aes(y = mpg), col = col.y1),
!missing(col.y1) & !missing(shape.y1) ~ geom_point(aes(y = mpg), col = col.y1, shape = shape.y1)
) +
# 2nd y data points
case_when(
missing(col.y2) & missing(shape.y2) ~ geom_point(aes(y = hp)),
missing(col.y2) & !missing(shape.y2) ~ geom_point(aes(y = hp), shape = shape.y2),
!missing(col.y2) & missing(shape.y2) ~ geom_point(aes(y = hp), col = col.y2),
!missing(col.y2) & !missing(shape.y2) ~ geom_point(aes(y = hp), col = col.y2, shape = shape.y2)
) +
# y axises - labels and breaks
case_when(
!missing(title.y1) & !missing(breaks.y1) & !missing(title.y2) & !missing(breaks.y2) ~
scale_y_continuous(
name = title.y1, breaks = breaks.y1,
sec.axis = sec_axis(name = title.y2, breaks = breaks.y2)),
!missing(title.y1) & !missing(breaks.y1) & !missing(title.y2) & missing(breaks.y2) ~
scale_y_continuous(
name = title.y1, breaks = breaks.y1,
sec.axis = sec_axis(name = title.y2)),
!missing(title.y1) & !missing(breaks.y1) & missing(title.y2) & missing(breaks.y2) ~
scale_y_continuous(
name = title.y1, breaks = breaks.y1),
.... # the remaining combinations...
)
# add plot title if any
if(!missing(title)) p <- p + ggtitle(title)
return(p)
}
如果你调用一个没有参数的函数,它将使用默认值,所以如果你真的想在你的包装函数中有一些参数,一个选择是复制默认值,作为新的默认值。
您可以使用 formals()
提取函数的默认值,例如
formals(ggplot2::geom_point)
#> $mapping
#> NULL
#>
#> $data
#> NULL
#>
#> $stat
#> [1] "identity"
#>
#> $position
#> [1] "identity"
#>
#> $...
#>
#>
#> $na.rm
#> [1] FALSE
#>
#> $show.legend
#> [1] NA
#>
#> $inherit.aes
#> [1] TRUE
请注意 col
不是其中之一; col
通过 ...
传递,因此更难处理。
您可以检索这些默认值,但是要遍历您要使用的包装器中缺少的默认值 missing()
并且它有其自身的复杂性... 来自 ?missing
:
Currently missing can only be used in the immediate body of the function that defines the argument, not in the body of a nested function or a local call. This may change in the future.
This is a ‘special’ primitive function: it must not evaluate its argument.
(强调我的)。你不能给missing()
传递一个符号,它需要字符串,所以你不能做像sapply(args, missing)
这样的事情,它是行不通的。
不过,您可以通过在不需要时不评估自己的论点来保留缺失。例如
my_fun <- function(na.rm = formals(ggplot2::geom_point)[["na.rm"]],
position = formals(ggplot2::geom_point)[["position"]],
...) {
ggplot2::geom_point(na.rm = na.rm, position = position, ...)
}
my_fun(na.rm = TRUE, position = "jitter")
#> geom_point: na.rm = TRUE
#> stat_identity: na.rm = TRUE
#> position_jitter
my_fun(position = "jitter")
#> geom_point: na.rm = FALSE
#> stat_identity: na.rm = FALSE
#> position_jitter
my_fun(na.rm = TRUE)
#> geom_point: na.rm = TRUE
#> stat_identity: na.rm = TRUE
#> position_identity
all.equal(ggplot2::geom_point(), my_fun())
#> [1] TRUE
如果您指定它们,这将使用命名参数(如果您确实需要,您甚至可以更改它们的名称),否则使用默认值。请注意,我没有包装 all 默认值,只是我想在我的函数中包含的那些。您仍然可以将其他参数传递给 ...
,否则采用默认值。
ggplot2::ggplot(mtcars, ggplot2::aes(cyl, hp)) + my_fun(position = "jitter", col = "red")
由 reprex package (v0.3.0)
于 2020-12-28 创建
您可以使用 do.call
来调用带有参数列表的函数。因此,您需要做的就是将您的论点收集到一个列表中,并省略遗漏的。
只要你的参数与相应的“ggplot2”函数中的参数同名,一个简单的match.call()
就足够了:
simple_plot = function (color, shape, fill) {
args = as.list(match.call())[-1L]
ggplot(mtcars, aes(cyl, mpg)) + do.call('geom_point', args, envir = parent.frame())
}
对于更复杂的功能(例如不匹配的参数名称),您可以在将列表传递给 do.call
之前对其进行操作;您还可以使用 as.call
构造一个未计算的调用并稍后对其进行计算:
the_call = as.call(c(as.name('geom_point'), args))
# … evaluate it:
eval.parent(the_call)
请注意 args
可能包含 未计算的表达式 ,因此计算需要在调用环境中发生(在上述两种情况下都已完成)。如果你想注入你自己的函数局部名称,你需要在添加它们之前评估它们,否则它们将找不到。
我有一个带有多个可选参数的函数,可以使用 ggplot2 修改绘图。如果缺少任何这些参数,我想在 ggplot2 中使用它们的默认值。如果只有一个参数,可以这样做:
simple_plot <- function (color) {
p <- ggplot(mtcars, aes(cyl, mpg))
if(!missing(color))
{
p <- p + geom_point(col = color)
} else {
p <- p + geom_point() # use its default if the argument is missing
}
}
然而,当有多个参数来修改绘图的点形状、填充、轴标题、中断、限制等时,这不是一个好的解决方案。有更好的方法吗?
[编辑以澄清我的问题]
感谢那些回答我的问题的人。我想通过包含以下代码来澄清我的问题。将有多个函数要调用,每个函数都有多个参数。使用 do.call
似乎不是一种简单的实现方式。
better_plot <- function(col.y1, shape.y1, col.y2, shape.y2,
title.y1, breaks.y1, title.y2, breaks.y2, title){
p <- ggplot(mtcars, aes(x = cyl)) +
# 1st y data points
case_when(
missing(col.y1) & missing(shape.y1) ~ geom_point(aes(y = mpg)),
missing(col.y1) & !missing(shape.y1) ~ geom_point(aes(y = mpg), shape = shape.y1),
!missing(col.y1) & missing(shape.y1) ~ geom_point(aes(y = mpg), col = col.y1),
!missing(col.y1) & !missing(shape.y1) ~ geom_point(aes(y = mpg), col = col.y1, shape = shape.y1)
) +
# 2nd y data points
case_when(
missing(col.y2) & missing(shape.y2) ~ geom_point(aes(y = hp)),
missing(col.y2) & !missing(shape.y2) ~ geom_point(aes(y = hp), shape = shape.y2),
!missing(col.y2) & missing(shape.y2) ~ geom_point(aes(y = hp), col = col.y2),
!missing(col.y2) & !missing(shape.y2) ~ geom_point(aes(y = hp), col = col.y2, shape = shape.y2)
) +
# y axises - labels and breaks
case_when(
!missing(title.y1) & !missing(breaks.y1) & !missing(title.y2) & !missing(breaks.y2) ~
scale_y_continuous(
name = title.y1, breaks = breaks.y1,
sec.axis = sec_axis(name = title.y2, breaks = breaks.y2)),
!missing(title.y1) & !missing(breaks.y1) & !missing(title.y2) & missing(breaks.y2) ~
scale_y_continuous(
name = title.y1, breaks = breaks.y1,
sec.axis = sec_axis(name = title.y2)),
!missing(title.y1) & !missing(breaks.y1) & missing(title.y2) & missing(breaks.y2) ~
scale_y_continuous(
name = title.y1, breaks = breaks.y1),
.... # the remaining combinations...
)
# add plot title if any
if(!missing(title)) p <- p + ggtitle(title)
return(p)
}
如果你调用一个没有参数的函数,它将使用默认值,所以如果你真的想在你的包装函数中有一些参数,一个选择是复制默认值,作为新的默认值。
您可以使用 formals()
提取函数的默认值,例如
formals(ggplot2::geom_point)
#> $mapping
#> NULL
#>
#> $data
#> NULL
#>
#> $stat
#> [1] "identity"
#>
#> $position
#> [1] "identity"
#>
#> $...
#>
#>
#> $na.rm
#> [1] FALSE
#>
#> $show.legend
#> [1] NA
#>
#> $inherit.aes
#> [1] TRUE
请注意 col
不是其中之一; col
通过 ...
传递,因此更难处理。
您可以检索这些默认值,但是要遍历您要使用的包装器中缺少的默认值 missing()
并且它有其自身的复杂性... 来自 ?missing
:
Currently missing can only be used in the immediate body of the function that defines the argument, not in the body of a nested function or a local call. This may change in the future.
This is a ‘special’ primitive function: it must not evaluate its argument.
(强调我的)。你不能给missing()
传递一个符号,它需要字符串,所以你不能做像sapply(args, missing)
这样的事情,它是行不通的。
不过,您可以通过在不需要时不评估自己的论点来保留缺失。例如
my_fun <- function(na.rm = formals(ggplot2::geom_point)[["na.rm"]],
position = formals(ggplot2::geom_point)[["position"]],
...) {
ggplot2::geom_point(na.rm = na.rm, position = position, ...)
}
my_fun(na.rm = TRUE, position = "jitter")
#> geom_point: na.rm = TRUE
#> stat_identity: na.rm = TRUE
#> position_jitter
my_fun(position = "jitter")
#> geom_point: na.rm = FALSE
#> stat_identity: na.rm = FALSE
#> position_jitter
my_fun(na.rm = TRUE)
#> geom_point: na.rm = TRUE
#> stat_identity: na.rm = TRUE
#> position_identity
all.equal(ggplot2::geom_point(), my_fun())
#> [1] TRUE
如果您指定它们,这将使用命名参数(如果您确实需要,您甚至可以更改它们的名称),否则使用默认值。请注意,我没有包装 all 默认值,只是我想在我的函数中包含的那些。您仍然可以将其他参数传递给 ...
,否则采用默认值。
ggplot2::ggplot(mtcars, ggplot2::aes(cyl, hp)) + my_fun(position = "jitter", col = "red")
由 reprex package (v0.3.0)
于 2020-12-28 创建您可以使用 do.call
来调用带有参数列表的函数。因此,您需要做的就是将您的论点收集到一个列表中,并省略遗漏的。
只要你的参数与相应的“ggplot2”函数中的参数同名,一个简单的match.call()
就足够了:
simple_plot = function (color, shape, fill) {
args = as.list(match.call())[-1L]
ggplot(mtcars, aes(cyl, mpg)) + do.call('geom_point', args, envir = parent.frame())
}
对于更复杂的功能(例如不匹配的参数名称),您可以在将列表传递给 do.call
之前对其进行操作;您还可以使用 as.call
构造一个未计算的调用并稍后对其进行计算:
the_call = as.call(c(as.name('geom_point'), args))
# … evaluate it:
eval.parent(the_call)
请注意 args
可能包含 未计算的表达式 ,因此计算需要在调用环境中发生(在上述两种情况下都已完成)。如果你想注入你自己的函数局部名称,你需要在添加它们之前评估它们,否则它们将找不到。