How to iterate over a list of lists in R? - r

I am new to R and not able to find the counter code in R for the following Python code.
Please help
list1 = [10, 20] # or a tuple
list2 = [30, 40] # or a tuple
mylist = [list1, list2] # can be tuple of tuples also
for _list in mylist:
a = _list[0]
b = _list[1]
# usage of a and b
I wrote the following R script:
list1 <- list(10, 20)
list2 <- list(30, 40)
mylist <- list(list1, list2)
for( j in 1:length(mylist))
{
print(j)
list1=mylist[[j]]
print(list1)
# Works perfect till here
# Error in below lines
a=list1[[0]]
b=list1[[1]]
# usage of a and b
}

In R, indexing starts from 1 and not 0 - difference between Python and R. So, if we change it to 1 and 2, it works. In addition, 1:length may be replaced with less buggy seq_along
for( j in seq_along(mylist)){
print(j)
list1 = mylist[[j]]
print(list1)
a=list1[[1]]
b=list1[[2]]
# usage of a and b
}
[1] 1
[[1]]
[1] 10
[[2]]
[1] 20
[1] 2
[[1]]
[1] 30
[[2]]
[1] 40
NOTE: list1, a, b are objects created within the loop and this gets updated in each iteration. It is not clear about the final outcome

A translation of your python code might be something like below
> for (lst in mylist) {
+ a <- lst[[1]]
+ b <- lst[[2]]
+ print(c(a, b))
+ }
[1] 10 20
[1] 30 40

Related

concatenation sublists of two different lists [duplicate]

I have two lists
first = list(a = 1, b = 2, c = 3)
second = list(a = 2, b = 3, c = 4)
I want to merge these two lists so the final product is
$a
[1] 1 2
$b
[1] 2 3
$c
[1] 3 4
Is there a simple function to do this?
If lists always have the same structure, as in the example, then a simpler solution is
mapply(c, first, second, SIMPLIFY=FALSE)
This is a very simple adaptation of the modifyList function by Sarkar. Because it is recursive, it will handle more complex situations than mapply would, and it will handle mismatched name situations by ignoring the items in 'second' that are not in 'first'.
appendList <- function (x, val)
{
stopifnot(is.list(x), is.list(val))
xnames <- names(x)
for (v in names(val)) {
x[[v]] <- if (v %in% xnames && is.list(x[[v]]) && is.list(val[[v]]))
appendList(x[[v]], val[[v]])
else c(x[[v]], val[[v]])
}
x
}
> appendList(first,second)
$a
[1] 1 2
$b
[1] 2 3
$c
[1] 3 4
Here are two options, the first:
both <- list(first, second)
n <- unique(unlist(lapply(both, names)))
names(n) <- n
lapply(n, function(ni) unlist(lapply(both, `[[`, ni)))
and the second, which works only if they have the same structure:
apply(cbind(first, second),1,function(x) unname(unlist(x)))
Both give the desired result.
Here's some code that I ended up writing, based upon #Andrei's answer but without the elegancy/simplicity. The advantage is that it allows a more complex recursive merge and also differs between elements that should be connected with rbind and those that are just connected with c:
# Decided to move this outside the mapply, not sure this is
# that important for speed but I imagine redefining the function
# might be somewhat time-consuming
mergeLists_internal <- function(o_element, n_element){
if (is.list(n_element)){
# Fill in non-existant element with NA elements
if (length(n_element) != length(o_element)){
n_unique <- names(n_element)[! names(n_element) %in% names(o_element)]
if (length(n_unique) > 0){
for (n in n_unique){
if (is.matrix(n_element[[n]])){
o_element[[n]] <- matrix(NA,
nrow=nrow(n_element[[n]]),
ncol=ncol(n_element[[n]]))
}else{
o_element[[n]] <- rep(NA,
times=length(n_element[[n]]))
}
}
}
o_unique <- names(o_element)[! names(o_element) %in% names(n_element)]
if (length(o_unique) > 0){
for (n in o_unique){
if (is.matrix(n_element[[n]])){
n_element[[n]] <- matrix(NA,
nrow=nrow(o_element[[n]]),
ncol=ncol(o_element[[n]]))
}else{
n_element[[n]] <- rep(NA,
times=length(o_element[[n]]))
}
}
}
}
# Now merge the two lists
return(mergeLists(o_element,
n_element))
}
if(length(n_element)>1){
new_cols <- ifelse(is.matrix(n_element), ncol(n_element), length(n_element))
old_cols <- ifelse(is.matrix(o_element), ncol(o_element), length(o_element))
if (new_cols != old_cols)
stop("Your length doesn't match on the elements,",
" new element (", new_cols , ") !=",
" old element (", old_cols , ")")
}
return(rbind(o_element,
n_element,
deparse.level=0))
return(c(o_element,
n_element))
}
mergeLists <- function(old, new){
if (is.null(old))
return (new)
m <- mapply(mergeLists_internal, old, new, SIMPLIFY=FALSE)
return(m)
}
Here's my example:
v1 <- list("a"=c(1,2), b="test 1", sublist=list(one=20:21, two=21:22))
v2 <- list("a"=c(3,4), b="test 2", sublist=list(one=10:11, two=11:12, three=1:2))
mergeLists(v1, v2)
This results in:
$a
[,1] [,2]
[1,] 1 2
[2,] 3 4
$b
[1] "test 1" "test 2"
$sublist
$sublist$one
[,1] [,2]
[1,] 20 21
[2,] 10 11
$sublist$two
[,1] [,2]
[1,] 21 22
[2,] 11 12
$sublist$three
[,1] [,2]
[1,] NA NA
[2,] 1 2
Yeah, I know - perhaps not the most logical merge but I have a complex parallel loop that I had to generate a more customized .combine function for, and therefore I wrote this monster :-)
merged = map(names(first), ~c(first[[.x]], second[[.x]])
merged = set_names(merged, names(first))
Using purrr. Also solves the problem of your lists not being in order.
In general one could,
merge_list <- function(...) by(v<-unlist(c(...)),names(v),base::c)
Note that the by() solution returns an attributed list, so it will print differently, but will still be a list. But you can get rid of the attributes with attr(x,"_attribute.name_")<-NULL. You can probably also use aggregate().
We can do a lapply with c(), and use setNames to assign the original name to the output.
setNames(lapply(1:length(first), function(x) c(first[[x]], second[[x]])), names(first))
$a
[1] 1 2
$b
[1] 2 3
$c
[1] 3 4
Following #Aaron left Stack Overflow and #Theo answer, the merged list's elements are in form of vector c.
But if you want to bind rows and columns use rbind and cbind.
merged = map(names(first), ~rbind(first[[.x]], second[[.x]])
merged = set_names(merged, names(first))
Using dplyr, I found that this line works for named lists using the same names:
as.list(bind_rows(first, second))

How to make variable size 2d arrays in R?

I would like to create a 2d array to store a known number of elements with unknown lengths of each element. However, I am not experienced with R and all the things I tried either require you to state the dimensions beforehand or don't let you append to the elements afterward. Here is something I tried:
b <- list(rep(c(), 5))
for (i in 1:5) {
for (j in 1:i) {
b[i] <- append(b[i], i)
}
print(b[i])
}
This gives this warning Warning message in b[i] <- append(b[i], i): “number of items to replace is not a multiple of replacement length” and does nothing
Here is what I wanted to achieve, but in python:
b = [[] for _ in range(5)]
for i in range(1,6):
for _ in range(i):
b[i-1].append(i)
I don't mind different data types, something like a dictionary will be ok, but I struggle with getting an idea of how this works in this particular case. The examples above are quite dumb, but, in short, what I want to achieve is: [ [1,2,3], [1,2,3,4], [1] ] or { 'a': [1,2], 'b': [1,2,3] }
What is the cleanest way to do that in R?
You dont need to append list you can simply add it to preexisting list.
b <- list()
for(i in 1:5){
b[length(b)+1] <- list(seq(1:i))
}
If you want to add to an existing list you can simply use c()
b[[3]] <- c(b[3][[1]], 1)
Based on what you say, that you want the equivalent in R of the following code in Python,
b = [[] for _ in range(5)]
for i in range(1,6):
for _ in range(i):
b[i-1].append(i)
Which returns the following output:
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5, 5]]
I have 2 ways to do so in R.
num_elements = 5
1st method: You need to load rlist library. Check here
install.packages("rlist")
library(rlist)
list1 <- list()
for (i in 1:num_elements) {
list1 <- list.append(list1, rep(i,i))
}
list1
2nd method: Predefined the initial list with a fixed size.
list2 <- list(rep(NULL,num_elements))
for (i in 1:num_elements) {
list2[i] <- list(rep(i,i))
}
list2
In both ways, the output would be the following:
[[1]]
[1] 1
[[2]]
[1] 2 2
[[3]]
[1] 3 3 3
[[4]]
[1] 4 4 4 4
[[5]]
[1] 5 5 5 5 5
To get items as a list of the sequence of numbers, then you only have to change rep by seq, as follows:
list1 <- list()
for (i in 1:num_elements) {
list1 <- list.append(list1, seq(1,i))
}
In this case, the output is:
[[1]]
[1] 1
[[2]]
[1] 1 2
[[3]]
[1] 1 2 3
[[4]]
[1] 1 2 3 4
[[5]]
[1] 1 2 3 4 5

For Loop in R not printing what I need

In R, I would like elements in list1 to be added to list2
list1 = c(1,2,3,4)
list2 = c(2,4,6,8)
for(i in list1){
for(j in list2){
print(i + j)
}
}
I am looking for the loop to return
3
6
9
12
but it returns
3
5
7
4
6
8
5
7
9
how can I get it to return the first former case?
if you are using for loop we need to mention jth interation equals to i.
for(i in list1){
for(j in list2[list1==i]){
print(i + j)
}
}
[1] 3
[1] 6
[1] 9
[1] 12
This is a classic question and duplicate of many other questions.
Each for loop iterates over the iterator. The comments answer you question but for understanding below is an example that will show you 'why' this is happening:
list1 = c(1,2,3,4)
list2 = c(2,4,6,8)
for(i in seq_along(list1)){
for(j in seq_along(list2)){
cat("list1[[", i,"]] + list2[[", j,"]] =",list1[[i]],"+",list2[[j]],"=", list1[[i]] + list2[[j]],"\n")
}
}
This should illustrate how the for loop works.
library(tidyverse)
list1 = c(1,2,3,4)
list2 = c(2,4,6,8)
purrr::walk2(list1, list2, ~print(.x + .y))
[1] 3
[1] 6
[1] 9
[1] 12
list1 is the .x and list2 is the .y

How to use multiple result values from a function in R?

I have the following code in R:
a <- 2
evaluate <- function(x){
b <- 2*x
c <- 3*x
d <- 4*x
out <- list("b" = b, "c" = c, "d" = d)
return(out)
}
evaluate(a)
I obtain something like
$b
[1] 4
$c
[1] 6
$d
[1] 8
How can I compute something like b + c + d ?
so many options
# with
with(evaluate(a), b + c + d)
[1] 18
# unlist the unnamed output object
sum(unlist(evaluate(a)))
[1] 18
# subset a named output object
result <- evaluate(a)
result$b + result$c + result$d
[1] 18
# subset an unnamed output object
evaluate(a)$b + evaluate(a)$c + evaluate(a)$d
[1] 18
# custom function with fancy arguments
f <- function(...) {
args <- unlist(...)
sum(args)
}
f(evaluate(a))
[1] 18
Also, +1 from: #Gregor (double-bracket list subsetting)
result[["b"]] + result[["c"]] + result[["d"]]
[1] 18
In R you can access list members using $ operator, followed by member name so, in your code, for example:
result = evaluate(a)
result$b + result$c + result$d
Your function returns a list. You could return a vector and then use the sum() function to compute the sum of the elements in the vector. If you must use a list, the 'Reduce()` function can work.
l <- list(2, 3, 4)
v <- c(2,3,4)
sum(v) # returns 9
Reduce("+", l) # returns 9

How to assign to a list in R without creating a bunch of new elements?

If I say l = list() and then I do l[[27]] = 100, it will create 26 other entries with the value NULL in them. Is there a way to avoid this?
For example if I run: l <- list(); for (i in c(4,7,1)) { l[[i]] <- i^1 }
It will creat a list with entries ranging from 1 to 7, and NULL values for all the ones I did not assign. How can I avoid these spurious entries?
Use character values for the indices:
l <- list(); for (i in c(4,7,1)) { l[[as.character(i)]] <- i^1 }
> l
$`4`
[1] 4
$`7`
[1] 7
$`1`
[1] 1
I would avoid a for loop and use lapply:
> x <- c(4, 7, 1)
> setNames(lapply(x, `^`, 1), x)
$`4`
[1] 4
$`7`
[1] 7
$`1`
[1] 1
You could append to the list:
l <- list(); for (i in c(4,7,1)) { l <- append(l, i^1) }

Resources