difference in behaviour between assign vs <<- in lapply - r

Why do <<- and assign behave differently when used in an lapply loop?
my_function <- function(x)
{
last_res <- ifelse(length(results) == 0, 0, results[[length(results)]])
print(last_res)
results[[x]] <<- 2 * x + last_res
}
results <- list()
results <- lapply(1:5, my_function)
[1] 0
[1] 2
[1] 6
[1] 12
[1] 20
my_function <- function(x)
{
last_res <- ifelse(length(results) == 0, 0, results[[length(results)]])
print(last_res)
assign(results[[x]], 2 * x + last_res, envir=.GlobalEnv)
}
results <- list()
results <- lapply(1:5, my_function)
Error in results[[x]] : subscript out of bounds
Note: I know that global assignment is to be avoided, just asking out of curiosity.

Because you try to assign to what is stored in results[[x]]. Since results is an empty list, there is nothing to assign to. The name must be given as a character and subsetting operators are actually functions in R and certainly not part of a name.
You can assign the return value of [[<-:
assign('results', `[[<-`(results, x, 2 * x + last_res), envir=.GlobalEnv)
But as you write, this is all not recommended practice.
PS: I recommend reading the section about subset-assignment in the R Language Definition.

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

A problem with wrapping array algorithm into a function

I just started programming in R. I'm working at code which execute operations on arrays. It works when I put there a variable but if i wrap it into a function something is wrong. When I try to recall list_matrices[i] i got NULL.
F <- function(x){
list_matrices=c()
for(i in 1:dim(x)[1]){
list_matrices[[i]] <- t(rbind(x[i,1:dim(x)[2],1:dim(x)[3]]))}
}
It has already been pointed out in the comments that the problem is that the function does not return list_matrices so here we will point out that there is some question of whether you really need to do this in the first place. If the reason to create the list is to later iterate over it with some function g, it would be possible to do that directly over 'x' using apply. These two are the same:
# test inputs
x <- array(1:24, 2:4)
g <- function(x) sum(x*x)
# 1
f <- function(x) {
list_matrices <- c()
for(i in 1:dim(x)[1]) {
list_matrices[[i]] <- t(rbind(x[i,1:dim(x)[2],1:dim(x)[3]]))
}
list_matrices
}
L <- f(x)
sapply(L, g)
## [1] 2300 2600
# 2
apply(x, 1, g)
## [1] 2300 2600
Also note that F is used for FALSE in R and could result in subtle errors if used as the name of an object so above we have renamed the function f.

Scoping problem when assigning functions in a loop

I would like to create a bunch of functions with a particular structure in the variable name as a crude workaround for what should be one function with multiple arguments (this I cannot do directly). Let's consider the following analogous example:
for(i in 1:3){
for(j in 1:2){
temp_fun <- function(x){
(x+i)^j
}
assign(paste0("fun", paste0("_plus_", i, "_pow_", j)), temp_fun)
}
}
This loop creates 6 functions that have x as dependent variable only
fun_plus_1_pow_1
fun_plus_1_pow_2
fun_plus_2_pow_1
fun_plus_2_pow_2
fun_plus_3_pow_1
fun_plus_3_pow_2
For instance fun_plus_2_pow_1(2) should return (2+2)^1 = 4, however it returns 25. I know what happens here, the values for i and j get updated while the loop is running and eventually i=3 and j=2are taken resulting in (2+3)^2 = 25.
But how can I make them local?
Here is one option. I also changed that assign stuff (creating a bunch of systematically named objects in the global environment is a clear sign to use a list instead).
funs <- matrix(list(), 3, 2, dimnames = list(paste0("plus", 1:3),
paste0("pow", 1:2)))
for(i in 1:3){
for(j in 1:2){
create_fun <- function(i, j){
#force evaluation so that the values are stored in the closure
force(i); force(j)
function(x) (x+i)^j
}
funs[i, j][[1]] <- create_fun(i, j)
}
}
funs["plus2", "pow1"][[1]](2)
#[1] 4
Why do you need to do this? Would it be sufficient to just define one function fun(x, i, j) and then use partial application:
library(pryr)
fun <- function(x, i, j) (x + i)^j
partial(fun, i = 2, j = 1)(2)
## [1] 4
# an example of passing partial(...) as a function to another function, i.e. to sapply
sapply(1:10, partial(fun, i = 2, j = 1))
## [1] 3 4 5 6 7 8 9 10 11 12
Note that partial(fun, i = i, j = j) for particular values of i and j is a function of x alone.

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

R: Using For Loop Variable in Function Declaration

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)

Resources