Additional function name but with fewer arguments [duplicate] - r

This question already has answers here:
How to curry a ... argument by position in R?
(2 answers)
Closed 9 years ago.
I need to create additional name for my_function(i,x) (where i can be an integer from 1 to 25). I'd like it to work like this
my_function1(x) sames as my_function(1,x)
my_function2(x) sames as my_function(2,x)
my_function3(x) sames as my_function(3,x)
...
my_function25(x) sames as my_function(25,x)
One way to do achieve this would be:
my_function1 <- function (x) my_function(1, x)
my_function2 <- function (x) my_function(2, x)
my_function3 <- function (x) my_function(3, x)
...
But since there are 25 of them it would be reasonable to make it in a loop. For this I've tried:
for(i in 1:25){
assign(paste("my_function",i,sep=""),function(x) my_function(i,x))
}
but it doesn't work since i is passed by reference and in the end the result was
my_function1(x) sames as my_function(25,x)
my_function2(x) sames as my_function(25,x)
my_function3(x) sames as my_function(25,x)
...
How can I pass "i" by value? Or perhaps there is some other way...
Why would I want to do this? I'm improving someones else R package in terms of efficiency but at the same time I need it to be compatible with old version.

This is called currying, and is a part of functional programming.
library(functional)
myf <- function(a,x) cat(a,x,"\n")
myf1 <- Curry(myf, a=1)
myf1(5)
for(i in seq(25)) assign(paste0("myf",i), Curry(myf,a=i) )
> myf15(5)
15 5
I guess there's an important question here as to why you'd want to do this. This seems like exactly the kind of thing you'd want arguments not many related functions for.

Well, you can achieve the same result, using base functions too.
The trick is to force (force) the evaluation of i at each iteration and assign your function in the .Globalenv (or the environment you like)
my_function <- function(a, b) a + b
lapply(1:10, function(i) {
force(i)
assign(paste0("my_function", i), function(x) my_function(i, x), envir = .GlobalEnv)
}
)
my_function1(10)
## [1] 11
my_function9(10)
## [1] 19

I think bquote will help here:
for(i in 1:2){
assign(paste("my_function",i,sep=""), bquote( function(x) my_function( i = .(i) , x ) ) )
}
>my_function2
# function(x) my_function(i = 2L, x)
But the point still stands - why would you want to do this?

Related

how to extract multiple function output automatially in R

I have built my own function, where it returns many values. I really need to extract several values at once. For example, suppose the following is my function
myfunc <- function(x,y){
res <- x+y
res2 <- x^2
res3 <- x*2
out <- list()
out$add <- res
out$squ <- res2
out$or <- res3
out$ADD <- res+res2+res3
out$fi <- res^2+res2+res3
return(out)
}
Then,
> myres
$add
[1] 7
$squ
[1] 9
$or
[1] 6
$ADD
[1] 22
$fi
[1] 64
suppose I want to extract two values at a time, for example,
myres$add, and myres$ADD
is there a way to find them automatically in R instead of repeating it. My original function is very complicated and this will help a lot.
Perhaps, you can try something like this -
res <- myfunc(6, 4)
extract_values <- c('add', 'ADD')
res[extract_values]
#$add
#[1] 10
#$ADD
#[1] 58
You could concatenate them or join in a list:
c(myres$add, myres$squ)
list(myres$add, myres$squ)
If you only want one call to myres you could also index like this:
myres[c(1, 2)]
What you want is known as destructuring, and unfortunately R does not natively support it. There are multiple packages which support this. The one with the (IMHO) nicest syntax is my own package ‘unpack’, which allows you to write positional unpacking as follows:
c[add, ., ., ADD, .] = myfunc(3, 4)
After this, the variables add and ADD are directly available to the caller.
A similar solution (more powerful but with a less nice syntax) is provided by the ‘zeallot’ package.

How do I create a list of functions in R? [duplicate]

This question already has answers here:
Enclosing variables within for loop
(3 answers)
Closed 4 years ago.
I am trying to create a list of functions where each function is slightly different. The following code works fine:
fun1 <- function(n) {
fun2 <- function(x) {
x^n
}
return(fun2)
}
powerfuns <- vector("list", 3)
powerfuns[[2]] <- fun1(2)
powerfuns[[3]] <- fun1(3)
powerfuns[[2]](4)
# [1] 16
powerfuns[[3]](4)
# [1] 64
The second element of the list is a function that squares its argument while the third is a function that cubes its argument. But it doesn't seem to work if I create the list using a for-loop instead:
powerfuns <- vector("list", 3)
for (i in 1:3) {
powerfuns[[i]] <- fun1(i)
}
powerfuns[[2]](4)
# [1] 64
powerfuns[[3]](4)
# [1] 64
Now both functions cube their arguments. There seems to be something about the for-loop that causes the environments for the functions to be identical. get("n", environment(powerfuns[[2]])) returns 3 in the second example but 2 in the first example. Does anyone have a suggestion about how I can get the result I want using a loop or something similar? Thanks!
This has to do with lazy evaulation. Since fun1 doesn't use the value n you pass in right away, it doesn't capture the value in the way you expect. You can fix this with
fun1 <- function(n) {
force(n)
fun2 <- function(x) {
x^n
}
return(fun2)
}
Then you can run the same code
powerfuns <- vector("list", 3)
for (i in 1:3) {
powerfuns[[i]] <- fun1(i)
}
powerfuns[[2]](4)
# [1] 16
powerfuns[[3]](4)
# [1] 64

Error message when using lapply to apply a function to multiple dataframes in a list.

My dataset looks like this, and I have a list of data.
Plot_ID Canopy_infection_rate DAI
1 YO01 5 7
2 YO01 8 14
3 YO01 10 21
What I want to do is to apply a function called "audpc_Canopyinfactionrate" to a list of dataframes.
However, when I run lapply, I get an error as below:
Error in FUN(X[[i]], ...) : argument "DAI" is missing, with no default
I've checked my list that my data does not shift a column.
Does anyone know what's wrong with it? Thanks
Here is part of my code:
#Read files in to list
for(i in 1:length(files)) {
lst[[i]] <- read.delim(files[i], header = TRUE, sep=" ")
}
#Apply a function to the list
densities <- list()
densities<- lapply(lst, audpc_Canopyinfactionrate)
#canopy infection rate
audpc_Canopyinfactionrate <- function(Canopy_infection_rate,DAI){
n <- length(DAI)
meanvec <- matrix(-1,(n-1))
intvec <- matrix(-1,(n-1))
for(i in 1:(n-1)){
meanvec[i] <- mean(c(Canopy_infection_rate[i],
Canopy_infection_rate[i+1]))
intvec[i] <- DAI[i+1] - DAI[i]
}
infprod <- meanvec * intvec
sum(infprod)
}
As pointed out in the comments, the problem lies in the way you are using lapply.
This function is built up like this: lapply(X, FUN, ...). FUN is the name of a function used to apply to the elements in a data.frame/list called X. So far so good.
Back to your case: You want to apply a function audpc_Canopyinfactionrate() to all data frames in lst. This function takes two arguments. And I think this is where things got mixed up in your code. Make sure you understand that in the way you are using lapply, you use lst[[1]], lst[[2]], etc. as the only argument in audpc_Canopyinfactionrate(), whereas it actually requires two arguments!
If you reformulate your function a bit, you can use lst[[1]], lst[[2]] as the only argument to your function, because you know that argument contains the columns you need - Canopy_infection_rate and DAI:
audpc_Canopyinfactionrate <- function(df){
n <- nrow(df)
meanvec <- matrix(-1, (n-1))
intvec <- matrix(-1, (n-1))
for(i in 1:(n-1)){
meanvec[i] <- mean(c(df$Canopy_infection_rate[i],
df$Canopy_infection_rate[i+1]))
intvec[i] <- df$DAI[i+1] - df$DAI[i]
}
infprod <- meanvec * intvec
return(sum(infprod))
}
Call lapply in the following way:
lapply(lst, audpc_Canopyinfactionrate)
Note: lapply can also be used with more than 1 argument, by using the ... in lapply(X, FUN, ...). In your case, however, I think this is not the best option.

R make functions with lapply: bug or what? [duplicate]

This question already has answers here:
Explain a lazy evaluation quirk
(2 answers)
Closed 7 years ago.
I am using lapply to make new functions and noticed that that sometimes it returns what is expected, and sometimes it returns only copies of the lastly created function.
Here is an example for the illustration, consider that I want to make the following simple list of functions
listFuncs = lapply( 1:3, function(X){
myfunc = function(y){X+y}
myfunc
})
Unfortunately, a simple evaluation shows that I am not getting what I hoped
listFuncs[[1]](10)
[1] 13
listFuncs[[2]](10)
[1] 13
Indeed, the list only contains the function
myfunc = function(y){3+y}
However, if I output something during the creation of the functions, for example
listFuncs = lapply( 1:3, function(X){
myfunc = function(y){X+y}
print(myfunc(0)) ## NEW LINE HERE !!!
myfunc
})
then my list of functions is "as expected"
[1] 1
[1] 2
[1] 3
> listFuncs[[1]](10)
[1] 11
> listFuncs[[2]](10)
[1] 12
Does anyone understand what is going on ? By advance, thank you.
You can use the force function:
listFuncs = lapply( 1:3,
function(X) {
force(X)
myfunc <- function(y) { X+y }
myfunc
}
)
listFuncs[[1]](10) ## 11

Replacing elements in a list of lists

The apply functions in R are a nice way to simplify for loops to get to an output. Is there an equivalent function that helps one avoid for loops when replacing the values of a vector? This is better understood by example...
# Take this list for example
x = list( list(a=1,b=2), list(a=3,b=4), list(a=5,b=6) )
# To get all of the "a" elements from each list, I can do
vapply(x,"[[",1,"a")
[1] 1 3 5
# If I want to change all of the "a" elements, I cannot do
vapply(x,"[[",1,"a") = 10:12
Error in vapply(x, "[[", 1, "a") = 10:12 :
could not find function "vapply<-"
# (this error was expected)
# Instead I must do something like this...
new.a = 10:12
for(i in seq_along(x)) x[[i]]$a = new.a[i]
Is there a simpler or faster alternative to using a loop?
One option would be to first unlist the list x, then replace the values named "a", and then relist the new list u based on the list structure of x.
u <- unlist(x)
u[names(u) == "a"] <- 10:12
relist(u, x)
vapply is a special case of sapply where you need to pre-specify the return type.
If you a multivariate version of sapply, the function you are looking for is mapply (or Map which is a wrapper with SIMPLIFY=FALSE`)
In general, functions with side-effects are frowned upon in R. The standard approach would be to create a new object when modifying.
You could use modlifyList to perform the modifications
xnew <- Map(modifyList, x, val = lapply(10:12,function(x) list(a = x)))

Resources