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
Related
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))
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
I would like to create multiple object names with a for loop. I have tried the following which fails horribly:
somevar_1 = c(1,2,3)
somevar_2 = c(4,5,6)
somevar_3 = c(7,8,9)
for (n in length(1:3)) {
x <- as.name(paste0("somevar_",[i]))
x[2]
}
The desired result is x being somevar_1, somevar_2, somevar_3 for the respective iterations, and x[2] being 2, 5 and 8 respectively.
How should I do this?
somevar_1 = c(1,2,3)
somevar_2 = c(4,5,6)
somevar_3 = c(7,8,9)
for (n in 1:3) {
x <- get(paste0("somevar_", n))
print(x[2])
}
Result
[1] 2
[1] 5
[1] 8
We can use mget to get all the required objects in a list and use sapply to subset 2nd element from each of them.
sapply(mget(paste0("somevar_", 1:3)), `[`, 2)
#somevar_1 somevar_2 somevar_3
# 2 5 8
I write the following code in r
Candidate <- vector(mode="list", length=3)
names(Candidate)<-c("Survivex","Survivey","Splitx")
Candidate[]<-0
for(i in 1:10)
{
if(i%%2==0)
Candidate["Survivex"]<-i
else if(i%%5==0)
Candidate["Survivey"]<-i
else
Candidate["Splitx"]<-i
}
which gives the following result
Candidate
$Survivex
[1] 10
$Survivey
[1] 5
$Splitx
[1] 9
but my desired result is like
Candidate
$Survivex
[1] 2 4 6 8 10
$Survivey
[1] 5 10
$Splitx
[1] 1 3 7 9
how can I modify my codes to get the required result
Concatenate the new results to the existing ones instead of replacing them, and
use double bracket extraction instead of single bracket subsetting. For example,
Candidate <- vector(mode="list", length=3)
names(Candidate) <- c("Survivex", "Survivey", "Splitx")
for(i in 1:10)
{
if(i %% 2 == 0)
Candidate[["Survivex"]] <- c(Candidate[["Survivex"]], i)
else if(i %% 5 == 0)
Candidate[["Survivey"]] <- c(Candidate[["Survivey"]], i)
else
Candidate[["Splitx"]] <- c(Candidate[["Splitx"]], i)
}
You could work with lapply() and functions to get what you want.
Candidate <- lapply(1:3, function(j) unlist(do.call(cbind, lapply(1:10, function(i) {
if (i %% 2 == 0) Candidate[[1]] <- i
else if (i %% 5 == 0) Candidate[[2]] <- i
else Candidate[[3]] <- i
if (i %% 5 == 0) Candidate[[2]] <- i
return(Candidate)
}))[j, ]))
names(Candidate) <- nms
Result
> Candidate
$`Survivex`
[1] 2 4 6 8 10
$Survivey
[1] 5 10
$Splitx
[1] 1 3 7 9
Data
Candidate <- vector(mode="list", length=3)
nms <- names(Candidate) <- c("Survivex", "Survivey", "Splitx")
So i've written this basic code that sorts a list using the well-known merge-sorting algorithm, i've defined two functions mergelists that compares and merges the elements and mergesort that divides the list into single elements:
mergelists <- function(a,b) {
al <- length(a)
bl <- length(b)
r <- numeric(al+bl)
ai <- 1
bi <- 1
j <- 1
while((ai<=al) && (bi<=bl)) {
if(a[ai]<b[bi]) {
r[j] <- a[ai]
ai <- ai+1
} else {
r[j] <- b[bi]
bi <- bi+1
}
j <- j+1
}
if(ai<=al) r[j:(al+bl)] <- a[ai:al]
else if(bi<=bl) r[j:(al+bl)] <- b[bi:bl]
return(r)
}
mergesort <- function(x) {
l <- length(x)
if(l>1) {
p <- ceiling(l/2)
a <- mergesort(x[1:p])
b <- mergesort(x[(p+1):l])
return(mergelists(a,b))
}
return(x)
}
this seems to work fine for the examples i used so far, for example:
> mergesort(c(11,10,9,15,6,12,17,8,19,7))
[1] 6 7 8 9 10 11 12 15 17 19
now for the sake of some research i'm doing, i want to change this code to work with R-lists and not vectors, the lists are usually defined as following:
> list(number=10,data=c(10,5,8,2))
$number
[1] 10
$data
[1] 10 5 8 2
data represents here the vector and number is the number of comparaisons.
After the change i imagine that the program should give me something like this:
>mergelists(list(number=8,data=c(1,3,5,8,9,10)),list(number=5,data=c(2,4,6,7)))
$number
[1] 20
$data
[1] 1 2 3 4 5 6 7 8 9 10
> mergesort(c(11,10,9,15,6,12,17,8,19,7))
$number
[1] 22
$data
[1] 6 7 8 9 10 11 12 15 17 19
the 20 here is basically 8 + 5 + 7, because 7 comparaisons would be necessary to merge the two sorted lists, but i don't know how to do this because i have a little experience with R-lists. i would appreciate your help. Thanks.
The starting point for any vector vec is list(number = 0, data = vec), where number is 0 because it as taken 0 comparisons to start with an unsorted vector.
You first need to modify mergelists to deal with two lists, simply by adding the indexing and then reforming the list at the end.
mergelists <- function(a,b) {
firstn <- a$number + b$number
a <- a$data
b <- b$data
al <- length(a)
bl <- length(b)
r <- numeric(al+bl)
ai <- 1
bi <- 1
j <- 1
while((ai<=al) && (bi<=bl)) {
if(a[ai]<b[bi]) {
r[j] <- a[ai]
ai <- ai+1
} else {
r[j] <- b[bi]
bi <- bi+1
}
j <- j+1
}
if(ai<=al) r[j:(al+bl)] <- a[ai:al]
else if(bi<=bl) r[j:(al+bl)] <- b[bi:bl]
return(list(number = firstn + j - 1L, data = r))
}
mergelists(list(number=8,data=c(1,3,5,8,9,10)), list(number=5,data=c(2,4,6,7)))
# $number
# [1] 20
# $data
# [1] 1 2 3 4 5 6 7 8 9 10
Now that you have the "base function" defined, you need the calling function to generate the enhanced vector (list) and pass it accordingly. This function can easily be improved for efficiency, but I think its recursive properties are sound.
mergesort <- function(x) {
# this first guarantees that if called with a vector, it is list-ified,
# but if called with a list (i.e., every other time in the recursion),
# the argument is untouched
if (! is.list(x)) x <- list(number = 0, data = x)
l <- length(x$data)
if (l > 1) {
p <- ceiling(l/2)
# the `within(...)` trick is a sneaky trick, can easily be
# handled with pre-assignment/subsetting
a <- mergesort(within(x, { data <- data[1:p]; }))
b <- mergesort(within(x, { data <- data[(p+1):l]; }))
return(mergelists(a,b))
}
return(x)
}
mergesort(c(11,10,9,15,6,12,17,8,19,7))
# $number
# [1] 22
# $data
# [1] 6 7 8 9 10 11 12 15 17 19