Get the name of a function contained in variable - r

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

Related

Apply a function to objects in my global environment R

This code chunk creates a 10 objects based of length of alpha.
alpha <- seq(.1,1,by=.1)
for (i in 1:length(alpha)){
assign(paste0("list_ts_ses_tune", i),NULL)
}
How do I put each function into the new list_ts_ses_tune1 ... null objects I've created? Each function puts in a list, and works if I set list_ts_ses_tune1 <- lapply ...
for (i in 1:length(alpha))
{
list_ts_ses_tune[i] <- lapply(list_ts, function(x)
forecast::forecast(ses(x,h=24,alpha=alpha[i])))
list_ts_ses_tune[i] <- lapply(list_ts_ses_tune[i], "[", c("mean"))
}
Maybe this is a better way to do this? I need each individual output in a list of values.
Edit:
for (i in 1:length(alpha))
{
list_ts_ses_tune[[i]] <- lapply(list_ts[1:(length(list_ts)/2)],
function(x)
forecast::forecast(ses(x,h=24,alpha=alpha[i])))
list_ts_ses_tune[[i]] <- lapply(list_ts_ses_tune[[i]], "[", c("mean"))
}
We can use mget to return all the objects into a list
mget(ls(pattern = '^list_ts_ses_tune\\d+'))
Also, the NULL list can be created more easily instead of 10 objects in the global environment
list_ts_ses_tune <- vector('list', length(alpha))
Now, we can just use the OP's code
for (i in 1:length(alpha))
{
list_ts_ses_tune[[i]] <- lapply(list_ts, function(x)
forecast::forecast(ses(x,h=24,alpha=alpha[i])))
}
If we want to create a single data.frame
for(i in seq_along(alpha)) {
list_ts_ses_tune[[i]] <- data.frame(Mean = do.call(rbind, lapply(list_ts, function(x)
forecast::forecast(ses(x,h=24,alpha=alpha[i]))$mean)))
}
You could simply accomplish everything by doing:
library(forecast)
list_ts_ses_tune <- Map(function(x)
lapply(alpha, function(y)forecast(ses(x,h=24,alpha=y))['mean']), list_ts)

lapply with optional function argument and optional vector arguments

I have a function in R, say
f1 <- function(x,y,vec, func0,...){
...
...
...
return(out)
}
The arguments func0 and vec in this function f1 are function object and some vector object respectively. Now I want to repeat this function 'reps' times (everything else being the same). I have stored the arguments of this function in a list as there are a lot of arguments and I keep changing them to do the replications again.
list1 <- list(x,y,vec, func0, other arguments)
Then I want to do,
f1_reps <- lapply(1:reps, f1, list1)
I get an error when I do this as the function arguments func0 and vec are not found.
Any help in this direction would be helpful. Here is a mock example of the situation.
Here is an example,
a <- function(n){
sqrt(n)
}
N = 100
out <- rep(NA,N)
# simple function with multiple arguments
foo <- function(a=a, b= c(1:3), c= 1000){
for(n in 1:N){
out[n] <- b%*%b+ a(n)*c
}
return(out)
}
candidates <- list(a=a, b = c(1:3), c=1000)
lapply(1:4, foo(a=candidates$a,b=candidates$b,c=candidates$c)) ## Doesn't work
lapply(1:4, foo, a=candidates$a, b=candidates$b, c=candidates$c) ## Doesn't work
candidates2 <- c(a=a, b = c(1:3), c=1000) # A vector of arguments
lapply(1:4, foo, a=candidates2$a, b = c(candidates2$b1,candidates2$b2,candidates2$b3), c=candidates2$c) #Doesn't work either
This uses the dots aka the ... argument:
foo2 <- function(...) {
#I just returns the identity
l <- lapply(..., I)
a <- l[[1]]
b <- l[[2]]
c <- l[[3]]
for(n in 1:N){
out[n] <- b%*%b+ a(n)*c
}
return(out)
}
candidates <- list(a=a, b = c(1:3), c=1000)
foo2(candidates)
# or to simplify. Same output as previous.
c(crossprod(1:3)) + sqrt(seq_len(100)) * 1000
[1] 1014.000 1428.214 1746.051 2014.000 2250.068 2463.490 2659.751 2842.427 3014.000 3176.278 3330.625 3478.102 3619.551 3755.657
[15] 3886.983 4014.000 4137.106 4256.641 4372.899 4486.136 4596.576 4704.416 4809.832 4912.979 5014.000 5113.020 5210.152 5305.503
[29] 5399.165 5491.226 5581.764 5670.854 5758.563 5844.952 5930.080 6014.000 6096.763 6178.414 6258.998 6338.555 6417.124 6494.741
[43] 6571.439 6647.250 6722.204 6796.330 6869.655 6942.203 7014.000 7085.068 7155.428 7225.103 7294.110 7362.469 7430.198 7497.315
[57] 7563.834 7629.773 7695.146 7759.967 7824.250 7888.008 7951.254 8014.000 8076.258 8138.038 8199.353 8260.211 8320.624 8380.600
[71] 8440.150 8499.281 8558.004 8616.325 8674.254 8731.798 8788.964 8845.761 8902.194 8958.272 9014.000 9069.385 9124.434 9179.151
[85] 9233.544 9287.618 9341.379 9394.832 9447.981 9500.833 9553.392 9605.663 9657.651 9709.360 9760.794 9811.959 9862.858 9913.495
[99] 9963.874 10014.000
The main issue is your function f1 takes an input of variables, not a list of the variables. This is one way you could approach it, with a simple eg, if I've understood correctly how your inputs are stored
# simple function with multiple arguments
foo <- function(a=1, b=2, c=3){
return(a+b+c)
}
# works
foo(a=1, b=2, c=3)
# doesn't work as not required format
foo(list(a=1, b=2, c=3))
# formatted list such that each element has 5 elements
candidates <- list(
a=1:5,
b=2:6,
c=3:7
)
# you need to apply the variables one by one with this setup
N <- 5
out <- lapply(1:N, function(i){
foo(a=candidates$a[i]
,b=candidates$b[i]
,c=candidates$c[i])
})
out

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)

do.call specify environment inside function

I'm using the following construct in a package,
## two functions in the global environment
funa <- function(x) x^2
funb <- function(x) x^3
## called within a function, fine
fun_wrap <- function(){
lapply(c('funa', 'funb'), do.call, list(x=3))
}
fun_wrap()
[[1]]
[1] 9
[[2]]
[1] 27
but I've just been bitten by the fact that it won't work if the functions are in a different (local) frame,
## same construct, but the functions are local
fun_wrap1 <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
lapply(c('funa1', 'funb1'), do.call, list(x=3))
}
## now it fails
fun_wrap1()
##Error in FUN(c("funa1", "funb1")[[1L]], ...) :
## could not find function "funa1"
I've tried passing envir=parent.frame(2) to do.call() (doesn't work); frankly the help page of ?parent.frame goes way over my head. Any hint for a more robust use of do.call?
Note that the list of functions comes as a character vector, passed from another piece of code; I prefer not to pass the functions directly.
Edit: one more twist... I thought I'd illustrated the right problem with my toy example, but the actual code I'm using is slightly different, in the sense that I'm calling fun_wrap1 within a separate function. The proposed solutions fail in this context.
fun_wrap1 <- function(funs){
lapply(funs, do.call, args=list(x=3), envir=environment())
}
foo <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
fun_wrap1(c('funa1', 'funb1'))
}
foo()
##Error in FUN(c("funa1", "funb1")[[1L]], ...) :
## could not find function "funa1"
(and the same happens with the match.fun approach)
I can get it to work by passing an optional environment to fun_wrap1,
fun_wrap1 <- function(funs, e=parent.frame()){
lapply(funs, do.call, args=list(x=3), envir=e)
}
foo <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
fun_wrap1(c('funa1', 'funb1'))
}
foo()
and that's hopefully it.
This seems to work, but i'm not sure if it has other implications I'm not considering:
fun_wrap1 <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
lapply(c('funa1', 'funb1'), do.call, args=list(x=3), envir=environment())
}
fun_wrap1()
#[[1]]
#[1] 9
#
#[[2]]
#[1] 27
So this is essentially equivalent to having the lapply statement as:
lapply(
c('funa1', 'funb1'),
function(f) do.call(f, args=list(x=3), envir=environment() )
)
Evidently if we evaluate the functions in fun_wrap2 it works. The problem with the approach in the question is that the character strings get converted to functions inside one of the processing functions which changes the lookup path.
fun_wrap2 <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
nms <- c("funa1", "funb1")
funs <- lapply(nms, match.fun)
lapply(funs, do.call, list(x=3))
}
fun_wrap2()
A slightly simpler version of #g-grothendieck's answer. Rather than using the function names, we just put the functions themselves into the list that is fed to lapply.
fun_wrap1 <- function(){
funa1 <- function(x) x^2
funb1 <- function(x) x^3
lapply(list(funa1, funb1), do.call, list(x=3))
}
fun_wrap1()

How to simplify sub functions' arguments in 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)

Resources