How to simplify sub functions' arguments in R? - r

Suppose I have a function with sub functions like this format:
f<-function(f,a,b,c,d,e) {
f1<-function(a,b,c,d,e){
cbind(rnorm(a,mean=b,sd=1),
rnorm(a,mean=b,sd=c),
rbinom(a,d,e))
}
out<-list()
for(i in 1:f) {out[[i]]<-f1(a,b,c,d,e)}
return(out)
}
f(a=10,b=3,c=4,d=3,e=0.5,f=6)
Q1:
How to simplify the arguments for f1?
Q2:
I use list() and for loop for the out, How to rbind() or other better ways to return a single data frame?
Q3:
How to add ... in the f( ) to pass arguments for function mean, rnorm and rbinom?

func1, func2 and func3 can already access the arguments of func directly so it is unnecessary pass the arguments of func to each of them. e.g.
f <- function(x) {
g <- function() x*x
g()
}
f(2)

use as.list(environment()) eg
not.include <- c("a", "f", "vars", "not.include")
vars <- as.list(environment())
vars[! names(vars) %in% not.include]
do.call(func1, vars)

Related

Can ellipsis be iterated in R like *args in Python?

The newly-defined function to sum inputs:
mysum=function(...){
return(sum(...))
invisible(...)
}
> mysum(1,2,3,4)
[1] 10
What if I don't use the sum function?I mean this:
mysum=function(...){
s=0
for(i in ...){
s=s+i
}
return(s)
}
It doesn't work. Can ... be iterated?
In Python,it's simple:
def mysum(*args):
s=0
for i in args:
s+=i
return(s)
use c() on ellipsis before the loop, and assign it inside the function:
mysum=function(...){
vec = c(...)
s=0
for(i in vec){
s=s+i
}
return(s)
}
mysum(1,2,3)
[1] 6
Yes!
The usual route is to stuff it into a list and then iterate over the list:
my_fun <- function(...) {
args <- list(...)
# do stuff with ellipses args.
}
Whether you use lapply, go straight for named arguments or just loop over (for (i in seq_along(args)) {args[[i]]}) is up to you.
If you assume ... only contains vectors, you could do:
args <- unlist(list(...))
sum(args)

Not passing all optional arguments in apply

I am facing some problem with the apply function passing on arguments to a function when not needed. I understand that apply don't know what to do with the optional arguments and just pass them on the function.
But anyhow, here is what I would like to do:
First I want to specify a list of functions that I would like to use.
functions <- list(length, sum)
Then I would like to create a function which apply these specified functions on a data set.
myFunc <- function(data, functions) {
for (i in 1:length(functions)) print(apply(X=data, MARGIN=2, FUN=functions[[i]]))
}
This works fine.
data <- cbind(rnorm(100), rnorm(100))
myFunc(data, functions)
[1] 100 100
[1] -0.5758939 -5.1311173
But I would also like to use additional arguments for some functions, e.g.
power <- function(x, p) x^p
Which don't work as I want to. If I modify myFunc to:
myFunc <- function(data, functions, ...) {
for (i in 1:length(functions)) print(apply(X=data, MARGIN=2, FUN=functions[[i]], ...))
}
functions as
functions <- list(length, sum, power)
and then try my function I get
myFunc(data, functions, p=2)
Error in FUN(newX[, i], ...) :
2 arguments passed to 'length' which requires 1
How may I solve this issue?
Sorry for the wall of text. Thank you!
You can use Curry from functional to fix the parameter you want, put the function in the list of function you want to apply and finally iterate over this list of functions:
library(functional)
power <- function(x, p) x^p
funcs = list(length, sum, Curry(power, p=2), Curry(power, p=3))
lapply(funcs, function(f) apply(data, 2 , f))
With your code you can use:
functions <- list(length, sum, Curry(power, p=2))
myFunc(data, functions)
I'd advocate using Colonel's Curry approach, but if you want to stick to base R you can always:
funcs <- list(length, sum, function(x) power(x, 2))
which is roughly what Curry ends up doing
One option is to pass the parameters in a list with the arguments needed for each function. You can add those parameters to the others needed for apply using c and then use do.call to call the function. Something like this. I also wrap all the output in a list here rather than using print; your usage may vary.
power <- function(x, p) x^p
myFunc <- function(data, functions, parameters) {
lapply(seq_along(functions), function(i) {
p0 <- list(X=data, MARGIN=2, FUN=functions[[i]])
do.call(apply, c(p0, parameters[[i]]))
})
}
d <- matrix(1:6, nrow=2)
functions <- list(length, sum, power)
parameters <- list(NULL, NULL, p=3)
myFunc(d, functions, parameters)
You can use lazyeval package:
library(lazyeval)
my_evaluate <- function(data, expressions, ...) {
lapply(expressions, function(e) {
apply(data, MARGIN=2, FUN=function(x) {
lazy_eval(e, c(list(x=x), list(...)))
})
})
}
And use it like this:
my_expressions <- lazy_dots(sum = sum(x), sumpow = sum(x^p), length_k = length(x)*k )
data <- cbind(rnorm(100), rnorm(100))
my_evaluate(data, my_expressions, p = 2, k = 2)

Get the name of a function contained in variable

Certainly a very basic question but I do not have the answer:
I have a vector of function:
func1 <- function(u) u
func2 <- function(u) NA
func3 <- function(u) 1
funcs = c(func1, func2, func3)
I loop over every function using sapply, and I want to find a function command that retrieves the name of the function:
res=sapply(funcs, function(f){
command(f)
})
So that res is then:
c("func1","func2","func3")
Although there is no way to get the names if funcs is created with c, here is a convenience function for creating funcs that preserves the names:
cn <- function(...)
{
# call c() on parameters supplied, adding names
cnames <- sapply(as.list(substitute(list(...)))[-1L],as.character)
out <- c(...)
names(out) <- cnames
return(out)
}
funcs = cn(func1, func2, func3)
How about this approach:
flist<-ls(patt='func*')
flist[1]
[1] "func1"
do.call(flist[1],list(5))
# 5

Replace and restore the object in `globalenv()` during a function call

I need to call a function, named g, whose behavior depends on the variable under globalenv() several times. For convenience, I try to wrap it into a helper function, named f. However, I hope that after executing f, the globalenv() is invariant.
Here is my implementation so far:
g <- function(name) {
print(globalenv()[[name]])
}
f <- function(w) {
# backup "w" in globalenv if needed
is_existed.w <- !is.null(globalenv()[["w"]])
if (is_existed.w) {
temp.w <- globalenv()[["w"]]
}
w <<- w
g("w")
# restore w if needed
if (is_existed.w) {
w <<- temp.w
}
}
w <- "a"
f("gg")
w
However, this approach is very tedious. I need to copy-paste many times. Is there an more elegant way to implement this?
Why do you need to copy and paste? If it is because you'd want to save different variables, or call different functions, you could pass both of these as arguments to a higher order function, i.e. a function returning a function, like this:
wrap <- function(name, g) {
f <- function(value, ...) {
old <- globalenv()[[name]]
assign(name, value, globalenv())
res <- g(...)
if (!is.null(old))
assign(name, old, globalenv())
return (res)
}
return (f)
}
You could then create your f using wrap("w", g) and call it using f("gg", "w"), the latter "w" being the name of the variable you want to print.

How to expand an ellipsis (...) argument without evaluating it in R

I need a function that accepts an arbitrary number of arguments and stores them in a variable as an expression without evaluating them. I managed to do it with match.call but it seems a little "kludgy".
foo <- function(...) {
expr <- match.call()
expr[[1]] <- expression
expr <- eval(expr)
# do some stuff with expr
return(expr)
}
> bla
Error: object 'bla' not found
> foo(x=bla, y=2)
expression(x = bla, y = 2)
Clarification
To clarify, I'm asking how to write a function that behaves like expression(). I can't use expression() directly for reasons that are too long to explain.
The most idiomatic way is:
f <- function(x, y, ...) {
match.call(expand.dots = FALSE)$`...`
}
Using . from plyr as a prototype
foo <- function (...)
{
as.expression(as.list(match.call()[-1]))
}
The ultimate intended outcome is slightly vague (could you clarify a bit?). However, this may be helpful:
foo2 <- function(...) {
expr <- as.list(substitute(list(...)))[-1L]
class(expr) <- "expression"
expr
}
example:
foo2(x=bla, y=2)
# expression(x = bla, y = 2)

Resources