R: Using For Loop Variable in Function Declaration - r

I would like to create a list of functions in R where values from a for loop are stored in the function definition. Here is an example:
init <- function(){
mod <- list()
for(i in 1:3){
mod[[length(mod) + 1]] <- function(x) sum(i + x)
}
return(mod)
}
mod <- init()
mod[[1]](2) # 5 - but I want 3
mod[[2]](2) # 5 - but I want 4
In the above example, regardless of which function I call, i is always the last value in the for loop sequence, I understand this is the correct behavior.
I'm looking for something that achieves this:
mod[[1]] <- function(x) sum(1 + x)
mod[[2]] <- function(x) sum(2 + x)
mod[[3]] <- function(x) sum(3 + x)

You can explicitly ensure i is evaluated at it's current value in the for loop by using force.
init <- function(){
mod <- list()
f_gen = function(i) {
force(i)
return(function(x) sum(i + x))
}
for(i in 1:3){
mod[[i]] <- f_gen(i)
}
return(mod)
}
mod <- init()
mod[[1]](2)
# [1] 3
mod[[2]](2)
# [1] 4
More details are in the Functions/Lazy Evaluation subsection of Advanced R. Also see ?force, of course. Your example is fairly similar to the examples given in ?force.
Using a single-function generator function (f_gen in my code above) seems to make more sense than a list-of-functions generator function. Using my f_gen your code code be simplified:
f_gen = function(i) {
force(i)
return(function(x) sum(i + x))
}
mod2 <- lapply(1:3, f_gen)
mod2[[1]](2)
# [1] 3
mod2[[2]](2)
# [1] 4
## or alternately
mod3 = list()
for (i in 1:3) mod3[[i]] <- f_gen(i)
mod3[[1]](2)
mod3[[2]](2)

Related

How to create a list of functions from a list of parameters?

I want to create several functions using parameters and the function names contained inside a dataframe.
The for loop did not return what I was expecting, i.e each fuction to contain the parameters of intercept and slope from their line in the dataframe
data <- data.frame(name = c("A","B","C"), intercepts = c(1,0.5,4), slopes = c(0.1, -2,4))
> data
names intercepts slopes
1 A 1.0 0.1
2 B 0.5 -2.0
3 C 4.0 4.0
for(i in data$name){
assign(i, function(x){force(i);
data[data$name==i,]$intercepts + data[data$name==i,]$slopes*x}
)
}
I know the problem has something to do with the scope, but I could not fix it using "force" as recommended by some users.
> A(1)
[1] 8
> B(1)
[1] 8
> C(1)
[1] 8
I messed around with it a little bit and I do not think you can get what you want because of R's weird scope rules. Maybe try a function factory instead?
data <- data.frame(name = c("A","B","C"), intercepts = c(1,0.5,4), slopes = c(0.1, -2,4))
factory <- function(data, i) {
function(x) {
data[i,]$intercepts + data[i,]$slopes*x
}
}
factory(data, 1)(1) #> 1.1
A <- factory(data, 1)
A(1) #> 1.1
Or you could write code that just takes in data, i, and x and calculates the value outright. To be honest, what you're asking for seems weirdly unidiomatic.
One way to do it with substitute:
for(i in data$name){
local({
fn <- function(x) 1
body(fn) <- substitute(data[data$name==X,]$intercepts + data[data$name==X,]$slopes*x, list(X=i))
assign(i, fn, .GlobalEnv)
}
)
}
This does not rely on lexical scoping (which kkeey called "weird scoping rules"); "A" becomes hard-coded within A() etc:
print(A)
# function (x)
# data[data$name == "A", ]$intercepts + data[data$name == "A",
# ]$slopes * x
Using local above is not really necessary but I did it to avoid polluting the global namespace with unnecessary objects (fn in this case).
A simpler version without local:
for(i in data$name){
fn <- function(x) 1
body(fn) <- substitute(data[data$name==X,]$intercepts + data[data$name==X,]$slopes*x, list(X=i))
assign(i, fn)
}
Finally, a version for creating functions that become independent of your data:
for(i in data$name){
fn <- function(x) 1
int <- data[data$name==i,]$intercepts
slp <- data[data$name==i,]$slopes
body(fn) <- substitute(a + b*x, list(a=int, b=slp))
assign(i, fn)
}
> A
function (x)
1 + 0.1 * x
> B
function (x)
0.5 + -2 * x
> C
function (x)
4 + 4 * x

How to indicate the level of the for loop in R

Suppose I have a function including a for loop part. This for loop will work for, say, 10 iteration. How can I know from the result that the function is working now at level (iteration) number, say, 5.
That is, I would like my function to let me know the current iteration number.
For example,
I would like the result to be such this:
Iteration 1 starts
some result
iteration 1 ends
iteration 2 starts
some result
iteration 2 ends
...
...
Please note this is not my original function. In my original function I use optim function over a list of models, and I really need to know what is the current model.
Here is a general example:
Myfun <- function(x,y){
v <- list()
for(i in 1:100){
v[[i]] <- sum(x[[i]], y[[i]])
cat(v, "\n")
}
v
}
x <- rnorm(100)
y <- rnorm(100)
Myfun(x=x, y=y)
Method 1
Output the current iteration step inside the for loop.
Myfun <- function(x,y) {
v <- list()
for (i in 1:100) {
v[[i]] <- sum(x[[i]], y[[i]])
cat(sprintf("Step %i / 100 done\n", i))
}
v
}
Method 2
Use a progress bar (see ?txtProgressBar for details).
Myfun <- function(x,y) {
v <- list()
pb <- txtProgressBar(min = 0, max = 100, style = 3)
for (i in 1:100) {
v[[i]] <- sum(x[[i]], y[[i]])
setTxtProgressBar(pb, i)
}
close(pb)
v
}
Note that the line cat(v, "\n") from your original Myfun will give an error.

How to add two functions to a new function in R

I met a problem adding two functions together to a new function in R. For example, fun_1<-function(w)... fun_2<-function(w)... now I need to get a function fun(w)=fun_1(w)+fun_2(w) how could I do it?
Do you mean this ?
fun_1 <- function(x){
x ^ 2
}
fun_2 <- function(x){
x + 1
}
fun_3 <- function(x){
fun_1(x) + fun_2(x)
}
fun_3(1)
returns 3
k <- NA
fun <- function(w){
for (i in 1:100){
k[i] <- (-i/100)^2 + exp(w)
}
sum(k)
}
fun(1)
returns 305.6632
You can use get with envir = parent.frame() and just use paste to specify the function name.
# define functions
for(i in 1:100) assign(paste0('fun',i), function(w) (-i/100)^2+exp(w) )
# sum them
sum.fun <- function(x){
out <- 0
for(i in 1:100){
fun <- get(paste0('fun',i), envir = parent.frame())
out <- out + fun(x)
}
out
}
sum.fun(2)

Incrementing i when assigning functions?

I'm trying to create functions containing i in a loop, but i isn't been evaluated.
For example, the loop:
func <- list(0)
for (i in 1:3) {
func[[i]] <- function(x) i*x
}
produces:
> func[[1]]
function(x) i * x
<bytecode: 0x0000000011316b08>
when I actually need 1 * x, 2 * x, 3 * x
Write a function that returns a function. Be sure to use force() to force the evaluation of the lazy parameter.
func <- list(0)
makefun <- function(i) {
force(i)
function(x) i*x
}
func <- Map(makefun, 1:3)
func[[1]](5)
# [1] 5
func[[2]](5)
# [1] 10
func[[3]](5)
# [1] 15
You could do this in a for loop with the help of local().
func <- list(0)
for (i in 1:3) {
func[[i]] <- local({i<-i; function(x) i*x})
}
In both cases the definition still looks like "function(x) i*x" but the environment where the i value is coming from is different.
The issue is that your function refers to i, but there's only one i.MrFlick's answer is one way to force a local environment to be created to hold different copies of i with different values; another is to use local(), e.g.
func <- list()
for (i in 1:3) {
func[[i]] <- local(
{
j <- i # make a local copy of the current value
function(x) j*x
} )
}
func[[1]](5)
# [1] 5
func[[2]](5)
# [1] 10
func[[3]](5)
# [1] 15

how to append an element to a list without keeping track of the index?

I am looking for the r equivalent of this simple code in python
mylist = []
for this in that:
df = 1
mylist.append(df)
basically just creating an empty list, and then adding the objects created within the loop to it.
I only saw R solutions where one has to specify the index of the new element (say mylist[[i]] <- df), thus requiring to create an index i in the loop.
Is there any simpler way than that to just append after the last element.
There is a function called append:
ans <- list()
for (i in 1992:1994){
n <- 1 #whatever the function is
ans <- append(ans, n)
}
ans
## [[1]]
## [1] 1
##
## [[2]]
## [1] 1
##
## [[3]]
## [1] 1
##
Note: Using apply functions instead of a for loop is better (not necessarily faster) but it depends on the actual purpose of your loop.
Answering OP's comment: About using ggplot2 and saving plots to a list, something like this would be more efficient:
plotlist <- lapply(seq(2,4), function(i) {
require(ggplot2)
dat <- mtcars[mtcars$cyl == 2 * i,]
ggplot() + geom_point(data = dat ,aes(x=cyl,y=mpg))
})
Thanks to #Wen for sharing Comparison of c() and append() functions:
Concatenation (c) is pretty fast, but append is even faster and therefor preferable when concatenating just two vectors.
There is: mylist <- c(mylist, df) but that's usually not the recommended way in R. Depending on what you're trying to achieve, lapply() is often a better option.
mylist <- list()
for (i in 1:100){
n <- 1
mylist[[(length(mylist) +1)]] <- n
}
This seems to me the faster solution.
x <- 1:1000
aa <- microbenchmark({xx <- list(); for(i in x) {xx <- append(xx, values = i)} })
bb <- microbenchmark({xx <- list(); for(i in x) {xx <- c(xx, i)} } )
cc <- microbenchmark({xx <- list(); for(i in x) {xx[(length(xx) + 1)] <- i} } )
sapply(list(aa, bb, cc), (function(i){ median(i[["time"]]) / 10e5 }))
#{append}=4.466634 #{c}=3.185096 #{this.one}=2.925718
mylist <- list()
for (i in 1:100) {
df <- 1
mylist <- c(mylist, df)
}
Use
first_list = list(a=0,b=1)
newlist = c(first_list,list(c=2,d=3))
print(newlist)
$a
[1] 0
$b
[1] 1
$c
[1] 2
$d
[1] 3
Here's an example:
glmnet_params = list(family="binomial", alpha = 1,
type.measure = "auc",nfolds = 3, thresh = 1e-4, maxit = 1e3)
Now:
glmnet_classifier = do.call("cv.glmnet",
c(list(x = dtm_train, y = train$target), glmnet_params))

Resources