问题
Inside some functions as dplyr::mutate_at
or purrr::map
, it seems that one can use tilde operator ~
to build anonymous functions.
For example, one can do as in the linked question: map(iris, ~length(unique(.)))
Or also: mtcars %>% mutate_all(~.*2)
I tried to imitate this inside sapply
, to avoid sapply(list, function(item) {something_with_item})
. I was writing sapply(list, ~ something_with_.)
but I get an error
Error in match.fun(FUN) :
'~ something_with_.' is not a function, character or symbol
A reproducible example:
> sapply(names(mtcars),function(item) mean(mtcars[[item]]))
mpg cyl disp hp drat wt qsec
20.090625 6.187500 230.721875 146.687500 3.596563 3.217250 17.848750
vs am gear carb
0.437500 0.406250 3.687500 2.812500
> sapply(names(mtcars),~mean(mtcars[[.]]))
Error in match.fun(FUN) :
'~mean(mtcars[[.]])' is not a function, character or symbol
Why? Understanding this syntaxis as a function is just a behaviour of some packages as dplyr
and purrr
? Is it understood by base R
for some special cases?
Thank you!
回答1:
As caldwellst says above, the tilda ~
is used to create a formula object, which is a bit of unevaluated code that can be used later. Formulae are not the same as functions, and the purrr
functions work with formulae because they make a call to rlang::as_function
(via purrr::as_mapper
) in the background. The *apply
functions obviously don't do this, though you can imitate this behavior by calling one of the above functions in your own modified *apply
function (it's better to just use the map
functions, the following is just to illustrate the point):
# Write function with `as_mapper()`.
fapply <- function(x, func) {
sapply(x, purrr::as_mapper(func))
}
# Now we can use formulae.
fapply(names(mtcars), ~ mean(mtcars[[.]]))
#### OUTPUT ####
mpg cyl disp hp drat wt qsec vs am gear carb
20.090625 6.187500 230.721875 146.687500 3.596563 3.217250 17.848750 0.437500 0.406250 3.687500 2.812500
回答2:
A formula object is not a function and a formula will only be regarded as a function if the called function that the formula is being passed to has been written to interpret formula arguments as functions. Many of the tidyverse functions are written that way but in general a formula is not by default a function.
fn$
The gusbfn package does have fn$
which will allow just about any function which accepts a function argument to accept formulas. Preface the function call with fn$
and then formula arguments are interpreted as functions (subject to certain rules).
In terms of the example in the question we can do this which does not require writing a special version of sapply
to interpret formulas as functions:
library(gsubfn)
fn$sapply(names(mtcars), item ~ mean(mtcars[[item]]))
giving:
mpg cyl disp hp drat wt qsec
20.090625 6.187500 230.721875 146.687500 3.596563 3.217250 17.848750
vs am gear carb
0.437500 0.406250 3.687500 2.812500
See ?fn
for more information and examples.
match.funfn
The gsubfn package also has match.funfn
which is like match.fun
in base R except it will also interpret formulas as functions. This lets one write their own functions that accept formula arguments interpreting them as functions.
In terms of the example in the question:
library(gsubfn)
sapplyfn <- function(X, FUN, ...) {
FUN <- match.funfn(FUN)
sapply(X, FUN, ...)
}
sapplyfn(names(mtcars), item ~ mean(mtcars[[item]]))
giving:
mpg cyl disp hp drat wt qsec
20.090625 6.187500 230.721875 146.687500 3.596563 3.217250 17.848750
vs am gear carb
0.437500 0.406250 3.687500 2.812500
as.function.formula
The gsubfn package also has as.function.formula
which will convert a formula to a function. It is used by fn$
and match.funfn
. For example,
library(gsubfn)
as.function(item ~ mean(mtcars[[item]]))
giving:
function (item)
mean(mtcars[[item]])
来源:https://stackoverflow.com/questions/58377184/with-which-generality-can-tilde-operator-be-used-to-build-anonymous-functions