Beasterfield
Beasterfield

Reputation: 7123

apply over nested functions

This is not really a problem, but I'm wondering if there is a more elegant solution:

Lets say i have a vector vec <- rlnorm(10) and I want to apply a not vectorized function to it, e.g. exp (ignore for the moment that it is vectorized), I can do

sapply( vec, exp )

But when the function I want to apply is nested, the expression becomes directly less simple:

sapply( vec, function(x) exp( sqrt(x) ) )

This happens to me all the time with the apply and plyr family.

So my question is, is there in general an elegant way to nest (or pipe) functions without defining explicitly an (anonymous) function function(x){...}? Something like

# notrun
sapply( vec, sqrt | exp )

or similar.

Upvotes: 5

Views: 5243

Answers (2)

Matthew Plourde
Matthew Plourde

Reputation: 44634

The package functional includes a Compose function.

library(functional)
id <- Compose(exp, log)
id(2) # 2

Its implementation is simple enough to include in your source, if, say, you don't need the rest of the stuff in the functional package.

R> Compose
function (...) 
{
    fs <- list(...)
    if (!all(sapply(fs, is.function))) 
        stop("Argument is not a function")
    function(...) Reduce(function(x, f) f(x), fs, ...)
}
<environment: namespace:functional>

Upvotes: 5

Hong Ooi
Hong Ooi

Reputation: 57697

See the examples for ?Reduce:

## Iterative function application:
Funcall <- function(f, ...) f(...)
## Compute log(exp(acos(cos(0))
Reduce(Funcall, list(log, exp, acos, cos), 0, right = TRUE)

Here's a more bare-bones implementation with a slightly different interface:

Compose <- function(x, ...)
{
    lst <- list(...)
    for(i in rev(seq_along(lst)))
        x <- lst[[i]](x)
    x
}

sapply(0, Compose, log, exp, acos, cos)

Upvotes: 6

Related Questions