Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method chaining with R

Is it possible to chain functions in R?

Sample data:

m <- matrix(c(1:10, 11:20), nrow = 10, ncol = 2)

For example, I would like to replace the following statements below:

step1 <- mean(m)
step2 <- sum(step1)
res <- step2

Or,

res <- sum(mean(m))

With something like this :

res <- m@mean()@sum()

In some cases, that would clarify my code considerably.

EDIT1 This is a dummy example. I randomly picked 'sum' and 'mean'.

Ben has given a first piece of answer using %@% however, it prevents from using extra arguments within functions :

m %@% function1(arg1, arg2) %@% function2(arg1, arg2)

How can I work around that ?

EDIT2 Adding an example

require(xts)
require(PerformanceAnalytics)
xts.ts <- xts(rnorm(231),as.Date(13514:13744,origin="1970-01-01"))
plot(na.omit(lag( rollapply(xts.ts, width=rolling.per-1, FUN= function(x){sqrt(var(x))*sqrt(252)}), k=1)), main = "Dummy Example")

This example seems to work fine with Charles solution :

`%@%` <- function(x, f) eval.parent(as.call(append(as.list(substitute(f)), list(x), 1)))
xts.ts %@% rollapply( width = rolling.per-1, FUN= function(x) x%@%var%@%sqrt * sqrt(252) ) %@% lag( k=1) %@% na.omit %@% plot(main = "Dummy Example")

Less important to my case, but woth mentioning, the following statment fails with Charles's solution :

 xts.ts %@% names <- 'ts name' 
like image 463
Sam Avatar asked Jul 04 '12 14:07

Sam


People also ask

What is a chain in R?

chain: Chain together multiple operations. These functions providing an alternative way of calling dplyr (and other data manipulation) functions that you read can from left to right.

How do you do chaining method?

Method Chaining is the practice of calling different methods in a single line instead of calling other methods with the same object reference separately. Under this procedure, we have to write the object reference once and then call the methods by separating them with a (dot.).

What is method chaining Why is it bad?

The drawback to self-referential method chaining is that you communicate that multiple method calls are required to do something, and that each call builds off the last. If this is not true, then method chaining could be communicating the wrong thing to other programmers.

What is method chaining in pandas?

Pandas Chaining: Method chaining, in which methods are called on an object sequentially, one after the another. It has always been a programming style that's been possible with pandas, and over the past few releases, many methods have been introduced that allow even more chaining.


3 Answers

Try the functional package:

library(functional)
squared <- function(x)x*x
Compose(sum, squared)(m)
## [1] 44100
squared(sum(m))
## [1] 44100

EDIT:

Regarding the question in the comments of another response about arguments here is an example of composing with arguments. Curry is also from the functional package:

addn <- function(n, x) x + n
Compose(Curry(addn, 1), squared)(10)
## [1] 121
squared(addn(1, 10))
## [1] 121

EDIT 2:

Regarding question about debugging, debug works if the function is curried. If its not already curried then wrap it in Curry :

# this works since addn is curried
debug(addn)
Compose(Curry(addn, 1), squared)(10)

# to debug squared put it in a Curry -- so this works:
debug(squared)
Compose(Curry(addn, 1), Curry(squared))(10)
like image 174
G. Grothendieck Avatar answered Nov 11 '22 10:11

G. Grothendieck


I would use the magrittr package. It has a "pipe" operator that takes the result of one function and passes it in as an argument to the next:

m <- matrix(c(1:10, 11:20), nrow = 10, ncol = 2)

m %>% mean %>% sum

Ceci n'est pas un pipe!

like image 22
sdgfsdh Avatar answered Nov 11 '22 12:11

sdgfsdh


Sort of, but I think it's un-idiomatic and maybe fragile/not a good idea. (This is implied, I think, by @RichieCotton's comment above.)

From http://cran.r-project.org/doc/manuals/R-lang.html :

10.3.4 Special operators

R allows user-defined infix operators. These have the form of a string of characters delimited by the ‘%’ character. The string can contain any printable character except ‘%’. The escape sequences for strings do not apply here.

Note that the following operators are predefined

 %% %*% %/% %in% %o% %x%
"%@%" <- function(x,f) {
    f(x)
}

sqr <- function(x) x^2
x <- 1:4

x %@% mean  ## 2.5
x %@% mean %@% sqr  ## 6.25
x %@% (mean %@% sqr)  ## fails

Given m as defined above -- maybe what you had in mind?

 m %@% colMeans %@% sum  ## 21

Notes:

  • your example is a bit funny, because mean(x) always returns a scalar (i.e. a length-1 vector), so sum(mean(x)) is always going to be the same as mean(x)
  • the infix operators have to be surrounded by %, so you can't have anything as compact as a single symbol (and %% is taken already).
  • this sort of chaining is non-associative, which worries me -- it seems that the examples above work, so R is (apparently) evaluating left-to-right, but I don't know that that's guaranteed ...

edit: the question now asks how additional arguments can be incorporated. I don't think the syntax suggested (x %@% fun1(arg1) %@% fun2(arg2)) will work without some serious magic. This is the closest I can get at the moment -- creating a wrapper function that creates a modified version of the original function.

F <- function(f,...) {
    function(x) {
        f(x,...)
    }
}

Testing:

pow <- function(x,b=2) { x^b }
sqr <- function(x) x^2
x <- 1:4

x %@% F(mean,na.rm=TRUE)  ## 2.5
x %@% F(mean,na.rm=TRUE) %@% F(pow,3)  ## 16.25

(Note that I have used F as a function here, which may be dicey in some situations because it overwrites the F==FALSE shortcut)

like image 40
Ben Bolker Avatar answered Nov 11 '22 11:11

Ben Bolker