Change list items in for loop in R - r

I used to work in C++ and I think I am misunderstanding how for-loops (or iterations) work in R. I want to change list items in a for loop, but the for loop seems to make a temporary copy and only change that? How can I prevent this? This seems like a trivial beginners question, but I was unable to find a tutorial / question on stackoverflow about why this happens.
Code:
myList <- list(a=1, b=1, c=1, d=1)
for(item in myList){item <- 3}
myList
# Expected output: 3,3,3,3 - Real output: 1,1,1,1
# Additionally, I now have a variable "item" with value 3.

for(item in myList) creates a new object called item
If you want to refer to the items from the list, it would be better to do it by calling either their position with myList[1], or their name with myList[["a"]].
You can for-loop through the list by using the index (as one of the comments suggested).
myList <- list(a=1, b=2, c=4, d=5)
for(i in 1:length(myList)){
myList[i] <- 3
}
myList
But I would recomment a vector approach. Check this out:
myList <- list(a=1, b=2, c=1, d=5)
myList=='1'
myList[myList=='1']=3
myList
myList[names(myList)=='a']=9
myList
Now you do not have any redundant variables.
This is actually the recommended approach in R. For-loops are too computationally expensive.

As stated by #nicola, lapply should be a good option. Here is an example based on your question.
myList <- list(a = 1, b = 1, c = 1, d = 1) # output: 1,1,1,1
lapply(myList, function(x) 3) # output: 3,3,3,3
# lapply iterates over every list item

Related

R - Refactor list of lists [duplicate]

I have a list which contains list entries, and I need to transpose the structure.
The original structure is rectangular, but the names in the sub-lists do not match.
Here is an example:
ax <- data.frame(a=1,x=2)
ay <- data.frame(a=3,y=4)
bw <- data.frame(b=5,w=6)
bz <- data.frame(b=7,z=8)
before <- list( a=list(x=ax, y=ay), b=list(w=bw, z=bz))
What I want:
after <- list(w.x=list(a=ax, b=bw), y.z=list(a=ay, b=bz))
I do not care about the names of the resultant list (at any level).
Clearly this can be done explicitly:
after <- list(x.w=list(a=before$a$x, b=before$b$w), y.z=list(a=before$a$y, b=before$b$z))
but this is ugly and only works for a 2x2 structure. What's the idiomatic way of doing this?
The following piece of code will create a list with i-th element of every list in before:
lapply(before, "[[", i)
Now you just have to do
n <- length(before[[1]]) # assuming all lists in before have the same length
lapply(1:n, function(i) lapply(before, "[[", i))
and it should give you what you want. It's not very efficient (travels every list many times), and you can probably make it more efficient by keeping pointers to current list elements, so please decide whether this is good enough for you.
The purrr package now makes this process really easy:
library(purrr)
before %>% transpose()
## $x
## $x$a
## a x
## 1 1 2
##
## $x$b
## b w
## 1 5 6
##
##
## $y
## $y$a
## a y
## 1 3 4
##
## $y$b
## b z
## 1 7 8
Here's a different idea - use the fact that data.table can store data.frame's (in fact, given your question, maybe you don't even need to work with lists of lists and could just work with data.table's):
library(data.table)
dt = as.data.table(before)
after = as.list(data.table(t(dt)))
While this is an old question, i found it while searching for the same problem, and the second hit on google had a much more elegant solution in my opinion:
list_of_lists <- list(a=list(x="ax", y="ay"), b=list(w="bw", z="bz"))
new <- do.call(rbind, list_of_lists)
new is now a rectangular structure, a strange object: A list with a dimension attribute. It works with as many elements as you wish, as long as every sublist has the same length. To change it into a more common R-Object, one could for example create a matrix like this:
new.dims <- dim(new)
matrix(new,nrow = new.dims[1])
new.dims needed to be saved, as the matrix() function deletes the attribute of the list. Another way:
new <- do.call(c, new)
dim(new) <- new.dims
You can now for example convert it into a data.frame with as.data.frame() and split it into columns or do column wise operations. Before you do that, you could also change the dim attribute of the matrix, if it fits your needs better.
I found myself with this problem but I needed a solution that kept the names of each element. The solution I came up with should also work when the sub lists are not all the same length.
invertList = function(l){
elemnames = NULL
for (i in seq_along(l)){
elemnames = c(elemnames, names(l[[i]]))
}
elemnames = unique(elemnames)
res = list()
for (i in seq_along(elemnames)){
res[[elemnames[i]]] = list()
for (j in seq_along(l)){
if(exists(elemnames[i], l[[j]], inherits = F)){
res[[i]][[names(l)[j]]] = l[[names(l)[j]]][[elemnames[i]]]
}
}
}
res
}

how to add value to existing variable from inside a loop?

I want to add a computed value to an existing vector from within a loop in which the wanted vector is called from within the loop . that is im looking for some function that is similar to assign() function but that will enable me to add values to an existing variables and not creating new variables.
example:
say I have 3 variabels :
sp=3
for(i in 1:sp){
name<-paste("sp",i,sep="")
assign(name,rnorm(5))
}
and now I want to access the last value in each of the variabels, double it and add the resault to the vector:
for(i in 1:sp){
name<-paste("sp",i,sep="")
name[6]<-name[5]*2
}
the problem here is that "name" is a string, how can R identify it as a veriable name and access it?
What you are asking for is something like this:
get(name)
In your code it would like this:
v <- 1:10
var <- "v"
for (i in v){
tmp <- get(var)
tmp[6] <- tmp[5]*2
assign(var, tmp)
}
# [1] 1 2 3 4 5 10 7 8 9 10
Does that help you in any way?
However, I agree with the other answer, that lists and the lapply/sapply-functions are better suited!
This is how you can do this with a list:
sp=3
mylist <- vector(mode = "list", length = sp) #initialize a list
names(mylist) <- paste0("sp",seq_len(sp)) #set the names
for(i in 1:sp){
mylist[[i]] <- rnorm(5)
}
for(i in 1:sp){
mylist[[i]] <- c(mylist[[i]], mylist[[i]][5] * 2)
}
mylist
#$sp1
#[1] 0.6974563 0.7714190 1.1980534 0.6011610 -1.5884306 -3.1768611
#
#$sp2
#[1] -0.2276942 0.2982770 0.5504381 -0.2096708 -1.9199551 -3.8399102
#
#$sp3
#[1] 0.235280995 0.276813498 0.002567075 -0.774551774 0.766898045 1.533796089
You can then access the list elements as described in help("["), i.e., mylist$sp1, mylist[["sp1"]], etc.
Of course, this is still very inefficient code and it could be improved a lot. E.g., since all three variables are of same type and length, they really should be combined into a matrix, which could be filled with one call to rnorm and which would also allow doing the second operation with vectorized operations.
#Roland is absolutely right and you absolutely should use a list for this type of problem. It's cleaner and easier to work with. Here's another way of working with what you have (It can be easily generalised):
sp <- replicate(3, rnorm(5), simplify=FALSE)
names(sp) <- paste0("sp", 1:3)
sp
#$sp1
#[1] -0.3723205 1.2199743 0.1226524 0.7287469 -0.8670466
#
#$sp2
#[1] -0.5458811 -0.3276503 -1.3031100 1.3064743 -0.7533023
#
#$sp3
#[1] 1.2683564 0.9419726 -0.5925012 -1.2034788 -0.6613149
newsp <- lapply(sp, function(x){x[6] <- x[5]*2; x})
newsp
#$sp1
#[1] -0.3723205 1.2199743 0.1226524 0.7287469 -0.8670466 -1.7340933
#
#$sp2
#[1] -0.5458811 -0.3276503 -1.3031100 1.3064743 -0.7533023 -1.5066046
#
#$sp3
#[1] 1.2683564 0.9419726 -0.5925012 -1.2034788 -0.6613149 -1.3226297
EDIT: If you are truly, sincerely dedicated to doing this despite being recommended otherwise, you can do it this way:
for(i in 1:sp){
name<-paste("sp",i,sep="")
assign(name, `[<-`(get(name), 6, `[`(get(name), 5) * 2))
}

Apply mapping to a list in R

How can I apply a mapping (stored in a list) to a vector?
Consider I have a mapping defined in this way:
m <- list(foo='bar', a='b', 1=2)
I can get a single list element by just writing something like m[['foo']]. What I want to do is to get a list values for multiple keys at once. An obvious solution would be just iterating through the vector:
a <- c('foo', 'a')
b <- c()
for (it in a) {b <- c(b, m[[it]])}
But looks like this is not an R-style. Can I do it with a one-liner? I also tried using lapply() and mapply() with get() function, but didn't succeed in that.
Please note: I'm new to R, so I may use some terms improperly.
m <- list(foo='bar', a='b', c = 'baz')
a <- c("foo", "a")
unlist(m[a])
## foo a
## "bar" "b"

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)))

Accessing same named list elements of the list of lists in R

Frequently I encounter situations where I need to create a lot of similar models for different variables. Usually I dump them into the list. Here is the example of dummy code:
modlist <- lapply(1:10,function(l) {
data <- data.frame(Y=rnorm(10),X=rnorm(10))
lm(Y~.,data=data)
})
Now getting the fit for example is very easy:
lapply(modlist,predict)
What I want to do sometimes is to extract one element from the list. The obvious way is
sapply(modlist,function(l)l$rank)
This does what I want, but I wonder if there is a shorter way to get the same result?
probably these are a little bit simple:
> z <- list(list(a=1, b=2), list(a=3, b=4))
> sapply(z, `[[`, "b")
[1] 2 4
> sapply(z, get, x="b")
[1] 2 4
and you can define a function like:
> `%c%` <- function(x, n)sapply(x, `[[`, n)
> z %c% "b"
[1] 2 4
and also this looks like an extension of $:
> `%$%` <- function(x, n) sapply(x, `[[`, as.character(as.list(match.call())$n))
> z%$%b
[1] 2 4
I usually use kohske way, but here is another trick:
sapply(modlist, with, rank)
It is more useful when you need more elements, e.g.:
sapply(modlist, with, c(rank, df.residual))
As I remember I stole it from hadley (from plyr documentation I think).
Main difference between [[ and with solutions is in case missing elements. [[ returns NULL when element is missing. with throw an error unless there exist an object in global workspace having same name as searched element. So e.g.:
dah <- 1
lapply(modlist, with, dah)
returns list of ones when modlist don't have any dah element.
With Hadley's new lowliner package you can supply map() with a numeric index or an element name to elegantly pluck components out of a list. map() is the equivalent of lapply() with some extra tricks.
library("lowliner")
l <- list(
list(a = 1, b = 2),
list(a = 3, b = 4)
)
map(l, "b")
map(l, 2)
There is also a version that simplifies the result to a vector
map_v(l, "a")
map_v(l, 1)

Resources