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'
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.
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.).
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.
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.
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)
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!
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:
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)
%
, so you can't have anything as compact as a single symbol (and %%
is taken already).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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With