I'd like to unlist a nested list with has some items as vectors. The problem is that unlist also splits up these vectors. How can I keep them as single items?
a) one level up (unlist parameter: recursive = F)
b) all levels (unlist parameter: recursive = T)
Here's the example:
list0 <- list(c(1,2),
list(3,
c(4,5)
)
)
> list0
[[1]]
[1] 1 2
[[2]]
[[2]][[1]]
[1] 3
[[2]][[2]]
[1] 4 5
If we unlist one level:
list1 <- unlist(list0, recursive = F)
we get:
> list1
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4 5
but, as I'd like to keep vectors as they are, I'd like to get:
[[1]]
[1] 1 2
[[2]]
[1] 3
[[3]]
[1] 4 5
Maybe one way is with a for loop, but I guess that would be slow if the number of lists is high.
Could anyone give me some hints, please?
Thanks in advance
For your example, the code below gives the expected result.
f <- function(x){
if(is.atomic(x)){
list(x)
}else{
x
}
}
unlist(lapply(list0, f), recursive=FALSE)
But perhaps you need something which works with more nested levels, like:
f <- function(x){
if(is.atomic(x)){
list(x)
}else{
x
}
}
g <- function(L){
out <- unlist(lapply(L, f), recursive=FALSE)
while(any(sapply(out, is.list))){
out <- g(out)
}
out
}
list1 <- list(c(1,2),
list(3, c(4,5)),
list(6, list(c(7,8)))
)
list1_flattened <- g(list1)
which gives:
> list1
[[1]]
[1] 1 2
[[2]]
[[2]][[1]]
[1] 3
[[2]][[2]]
[1] 4 5
[[3]]
[[3]][[1]]
[1] 6
[[3]][[2]]
[[3]][[2]][[1]]
[1] 7 8
> list1_flattened
[[1]]
[1] 1 2
[[2]]
[1] 3
[[3]]
[1] 4 5
[[4]]
[1] 6
[[5]]
[1] 7 8
Related
I have a list of vectors in R:
test_list <- list()
test_list[[1]] <- c(1,2,3,4,5,6,7)
test_list[[2]] <- c(1,2,3)
test_list[[3]] <- c(6,7)
test_list[[4]] <- c(9,10,11)
And I want to check the intersection of each vector with all the other vectors. A nested loop approach would look like this:
for(i in test_list) {
for(j in test_list) {
intersect(i, j)
}
}
And the results would look like this:
[1] 1 2 3 4 5 6 7
[1] 1 2 3
[1] 6 7
numeric(0)
[1] 1 2 3
[1] 1 2 3
numeric(0)
numeric(0)
[1] 6 7
numeric(0)
[1] 6 7
numeric(0)
numeric(0)
numeric(0)
numeric(0)
[1] 9 10 11
I have seen that I can remove one of the foor loops using map or apply:
get_overlap_cells <- function(x) {
for(i in test_list) {
overlaping_cells <- intersect(i, x)
}
}
r <- map(test_list, get_overlap_cells)
However, I would like to remove both loops, any ideas on how to achieve this?
Thank you!
combins <- expand.grid(seq_along(test_list), seq_along(test_list))
mapply( function(x,y) intersect(test_list[[x]],test_list[[y]]),
combins[,1], combins[,2])
[[1]]
[1] 1 2 3 4 5 6 7
[[2]]
[1] 1 2 3
[[3]]
[1] 6 7
[[4]]
numeric(0)
[[5]]
[1] 1 2 3
[[6]]
[1] 1 2 3
[[7]]
numeric(0)
[[8]]
numeric(0)
[[9]]
[1] 6 7
[[10]]
numeric(0)
[[11]]
[1] 6 7
[[12]]
numeric(0)
[[13]]
numeric(0)
[[14]]
numeric(0)
[[15]]
numeric(0)
[[16]]
[1] 9 10 11
I am noticing this behavior of foreach and print in R. foreach somehow repeats the elements, but assigning result to variable "rectify" it.
> for (i in 1:3) {print(i+1)}
[1] 2
[1] 3
[1] 4
> foreach (i=1:3) %do% {print(i+1)}
[1] 2
[1] 3
[1] 4
[[1]]
[1] 2
[[2]]
[1] 3
[[3]]
[1] 4
> x<-foreach (i=1:3) %do% {print(i+1)}
[1] 2
[1] 3
[1] 4
> x
[[1]]
[1] 2
[[2]]
[1] 3
[[3]]
[1] 4
> foreach (i=1:3, .combine=c) %do% {print(i+1)}
[1] 2
[1] 3
[1] 4
[1] 2 3 4
> foreach (i=1:3) %do% {(i+1)}
[[1]]
[1] 2
[[2]]
[1] 3
[[3]]
[1] 4
> x <-foreach (i=1:3) %do% {(i+1)}
> x
[[1]]
[1] 2
[[2]]
[1] 3
[[3]]
[1] 4
> x<-for (i in 1:3) {(i+1)}
> x
NULL
Moreover, that brings me to the last test with simple for. for loop does not output results while foreach does.
If I would want to pick up results from for clause, is there another dedicated way rather than
> x <- NULL
> for (i in 1:3) {x <- cbind(x,(i+1))}
> x
[,1] [,2] [,3]
[1,] 2 3 4
From the help page, ?foreach (emphasis added):
The foreach and %do%/%dopar% operators provide a looping construct that can be viewed as a hybrid of the standard for loop and lapply function. It looks similar to the for loop, and it evaluates an expression, rather than a function (as in lapply), but it's purpose is to return a value (a list, by default), rather than to cause side-effects.
Regarding, the link to lapply, which also returns a list, observe the analog to the above code:
lapply(1:3, print)
[1] 1
[1] 2
[1] 3
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
So the lapply loop prints each value in an iteration and then returns the input as a list in the same manner as foreach.
I have list l of the form say :
R> l
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4
Now, I want to merge to member lists of this list. For instance, when I say I want to merge l[[1]] and l[[2]], I want the result to be :
R> l
[[1]]
[1] 1 2
[[2]]
[1] 3
[[3]]
[1] 4
While merging two list, I don't need dupilcate elements in the result. Hence, I am using union over two member lists.
R> union(l[[1]], l,[[2]])
[1] 1 2
How can I do this merge operation? Can I do this using some predefined function?
Thanks
You can write you own merge as follows:
l <- as.list(1:4)
mymerge <- function(mylist, element1, element2){
mylist[[element1]] <- union(mylist[[element1]], mylist[[element2]])
mylist[[element2]] <- NULL
return(mylist)
}
l <- mymerge(l,1,2)
Result:
> l
[[1]]
[1] 1 2
[[2]]
[1] 3
[[3]]
[1] 4
Comment: As akrun pointed out, you can also use c instead of union
This question has a nice solution of flattening lists while preserving their data types (which unlist does not):
flatten = function(x, unlist.vectors=F) {
while(any(vapply(x, is.list, logical(1)))) {
if (! unlist.vectors)
x = lapply(x, function(x) if(is.list(x)) x else list(x))
x = unlist(x, recursive=F)
}
x
}
If I give it the following list, it behaves as expected:
> a = list(c(1,2,3), list(52, 561), "a")
> flatten(a)
[[1]]
[1] 1 2 3
[[2]]
[1] 52
[[3]]
[1] 561
[[4]]
[1] "a"
Now I'd like to restructure the flat list like a. relist fails miserably:
> relist(flatten(a), skeleton=a)
[[1]]
[[1]][[1]]
[1] 1 2 3
[[1]][[2]]
[1] 52
[[1]][[3]]
[1] 561
[[2]]
[[2]][[1]]
[[2]][[1]][[1]]
[1] "a"
[[2]][[2]]
[[2]][[2]][[1]]
NULL
[[3]]
[[3]][[1]]
NULL
Now, I could of course do relist(unlist(b), a) but that loses data types again. What is a good way to restructure a flat list?
Bonus points if it handles the analogous attribute to unlist.vectors correctly.
One way to do it is:
relist2 = function(x, like, relist.vectors=F) {
if (! relist.vectors)
like = rapply(a, function(f) NA, how='replace')
lapply(relist(x, skeleton=like), function(e) unlist(e, recursive=F))
}
This retains the classes and distinguishes between lists and vectors:
> relist2(flatten(a), like=a)
[[1]]
[1] 1 2 3
[[2]]
[[2]][[1]]
[1] 52
[[2]][[2]]
[1] 561
[[3]]
[1] "a"
> relist2(flatten(a, unlist.vectors=T), like=a, relist.vectors=T)
[[1]]
[1] 1 2 3
[[2]]
[[2]][[1]]
[1] 52
[[2]][[2]]
[1] 561
[[3]]
[1] "a"
I have a list
> (mylist <- list(list(a=1),list(a=2),list(a=3)))
[[1]]
[[1]]$a
[1] 1
[[2]]
[[2]]$a
[1] 2
[[3]]
[[3]]$a
[1] 3
and I want to add field b to each sublist from 11:13 to get something like
> (mylist <- list(list(a=1,b=11),list(a=2,b=12),list(a=3,b=13)))
[[1]]
[[1]]$a
[1] 1
[[1]]$b
[1] 11
[[2]]
[[2]]$a
[1] 2
[[2]]$b
[1] 12
[[3]]
[[3]]$a
[1] 3
[[3]]$b
[1] 13
How do I do this?
(note that I have a large number of such relatively small lists, so this will be called in apply and has to be reasonably fast).
mylist <- list(list(a=1),list(a=2),list(a=3))
b.vals <- 11:13
mylist <- lapply(
1:length(mylist),
function(x) {
mylist[[x]]$b <- b.vals[[x]]
mylist[[x]]
} )