I wonder if there is an effective (without loops) method of joining two lists by names of their elements.
I checked here but this did not help me as well as tried to access names from sapply() as on the answer from here.
Say I have the following lists:
mylist1 <- list(region1 = 1:3, region2 = 5:7)
> mylist1
$region1
[1] 1 2 3
$region2
[1] 5 6 7
and
mylist2 <- list(region1 = "#E8C506", region2 = "#F3B508")
$region1
[1] "#E8C506"
$region2
[1] "#F3B508"
How can I get joined list by element names:
mylist3 <- list(region1 = list(1:3, "#E8C506"), region2 = list(5:7, "#F3B508"))
> mylist3
$region1
$region1[[1]]
[1] 1 2 3
$region1[[2]]
[1] "#E8C506"
$region2
$region2[[1]]
[1] 5 6 7
$region2[[2]]
[1] "#F3B508"
Relying heavily on the answers from #Ajar and #Flodel to Combine/merge lists by elements names, this maps into list's instead of a vectors (c) to achieve your end:
Using base:
keys <- unique(c(names(mylist1), names(mylist2)))
setNames(mapply(list, mylist1[keys], mylist2[keys], SIMPLIFY = FALSE), keys)
Using purrr:
library(purrr)
cat_lists <- function(list1, list2) {
keys <- unique(c(names(list1), names(list2)))
map2(list1[keys], list2[keys], list) |>
set_names(keys)
}
reduce(list(mylist1, mylist2), cat_lists)
Output:
$region1
$region1[[1]]
[1] 1 2 3
$region1[[2]]
[1] "#E8C506"
$region2
$region2[[1]]
[1] 5 6 7
$region2[[2]]
[1] "#F3B508"
If the elements are identical in both lists you can use purrr::transpose():
library(purrr)
transpose(list(mylist1, mylist2))
$region1
$region1[[1]]
[1] 1 2 3
$region1[[2]]
[1] "#E8C506"
$region2
$region2[[1]]
[1] 5 6 7
$region2[[2]]
[1] "#F3B508"
Note tranpose() uses the first list as a template and matches subsequent list elements by name, so if the elements are different across lists, you need to ensure all names are first present in the first list, e.g.:
mylist3 <- list(region1 = 1:3, region2 = 5:7, region3 = 5:7)
mylist4 <- list(region1 = "#E8C506", region2 = "#F3B508", region4 = "#F00008")
l <- list(mylist3, mylist4)
l_names <- unique(unlist(lapply(l, names)))
mylist3[l_names] <- mylist3[l_names]
transpose(list(mylist3, mylist4))
$region1
$region1[[1]]
[1] 1 2 3
$region1[[2]]
[1] "#E8C506"
$region2
$region2[[1]]
[1] 5 6 7
$region2[[2]]
[1] "#F3B508"
$region3
$region3[[1]]
[1] 5 6 7
$region3[[2]]
NULL
$region4
$region4[[1]]
NULL
$region4[[2]]
[1] "#F00008"
I hope this works.
list12 <- mapply(list, mylist1 , mylist2 , SIMPLIFY=FALSE)
Related
I want to substract one from a particular element of a nested list.
How should I do that?
This is my nested list
I want to one from positive values of bar vector (check first is it is positive)
x <- list(a = list(foo = 1:8, bar = 3:4), b = list(baz = 5:6))
I was thinking about map function.
Thank you!
You could write a recursive function:
mod_at <- function(x, name, FUN){
if(name %in% names(x)){
x[[name]] <- FUN(x[[name]])
x
}
else if(is.list(x))lapply(x, mod_at, name, FUN)
else x
}
Now use that to modify the list:
mod_at(x, "bar", function(x)ifelse(x>0, x-1, x))
$a
$a$foo
[1] 1 2 3 4 5 6 7 8
$a$bar
[1] 2 3
$b
$b$baz
[1] 5 6
You could use the function the way you want:
mod_at(x, "baz", sqrt)
$a
$a$foo
[1] 1 2 3 4 5 6 7 8
$a$bar
[1] 3 4
$b
$b$baz
[1] 2.236068 2.449490
Suppose we have list l which is a list of lists.
a <- list(c(1,2,3))
b <- list(c(4,5,6), c(7,8,9))
c <- list(c(10,11,12), c(13,14,15), c(16,17,18))
l <- list(a, b, c)
So l is a list of lists, where each of those lists itself contains at least one list.
Question
How can I make a function which can extract all the lowest level lists into a single list of lists?
# Goal
list(c(1,2,3), c(4,5,6), c(7,8,9), c(10,11,12), c(13,14,15), c(16,17,18))
Notes:
The example is a minimal reproducible example for which it would be possible to hard-code a solution, but it is important that the solution generalise, since the real problem has tens-of-thousands of lists each containing unknown numbers of lists - so a hard-coded solution definitely won't scale!
I hope to find a solution in base R, if possible.
A few things I've tried so far
Some unsuccessful attempts, mostly using sapply() and unlist():
sapply(l, function(x) { unlist(x) })
# [[1]]
# [1] 1 2 3
#
# [[2]]
# [1] 4 5 6 7 8 9
#
# [[3]]
# [1] 10 11 12 13 14 15 16 17 18
unlist(a)
# [1] 1 2 3
# unlist() seems to combine the elements of multiple lists into one list (not desired here)..
unlist(b)
# [1] 4 5 6 7 8 9
I thought the recursive = FALSE argument looked promising, but unfortunately I couldn't get it to do what I wanted either:
unlist(b, recursive = FALSE)
# [1] 4 5 6 7 8 9
Use unlist, non-recursive on your initial list.
unlist(l, recursive=FALSE)
# [[1]]
# [1] 1 2 3
#
# [[2]]
# [1] 4 5 6
#
# [[3]]
# [1] 7 8 9
#
# [[4]]
# [1] 10 11 12
#
# [[5]]
# [1] 13 14 15
#
# [[6]]
# [1] 16 17 18
Just use unlist():
a <- list(c(1,2,3))
b <- list(c(4,5,6), c(7,8,9))
c <- list(c(10,11,12), c(13,14,15), c(16,17,18))
l <- list(a, b, c)
l.want <- list(c(1,2,3), c(4,5,6), c(7,8,9), c(10,11,12), c(13,14,15), c(16,17,18))
#Use unlist
l.func <- unlist(l, recursive = F)
all.equal(l.want, l.func)
# TRUE
I would like to unlist a list of list while keeping the names of the second list.
For example if have a list like this:
$`listA`
$`listA_a`
[1] 1 2
$`listA_g`
[1] 1 2
$`listB`
$`listB_b`
[1] 1 2
I would like to obtain this list:
$`listA_a`
[1] 1 2
$`listA_g`
[1] 1 2
$`listB_b`
[1] 1 2
I know there is an argument in unlist to keep names (use.names = T, which is true by default)
however it keeps the names of the first list and add a number if there is several elements ("listA1", "listA2", "listB").
(This is an example but in my code the elements of the list are plots so I cannot use a data.frame or anything... I cannot easily reconstruct the names as they contain informations about the data used for the plots).
Thank you very much for your help!
Pernille
Try this approach. You can use unlist() with recursive=F to keep the desired structure and then format the names. Here the code:
#Data
List <- list(listA = list(listA_a = c(1, 2), listA_g = c(1, 2)), listB = list(
listB_b = c(1, 2)))
#Code
L <- unlist(List,recursive = F)
names(L) <- gsub(".*\\.","", names(L) )
L
Output:
L
$listA_a
[1] 1 2
$listA_g
[1] 1 2
$listB_b
[1] 1 2
Or the more simplified version without regex (Many thanks and credits to #markus):
#Code 2
L <- unlist(unname(List),recursive = F)
Output:
L
$listA_a
[1] 1 2
$listA_g
[1] 1 2
$listB_b
[1] 1 2
We could use rrapply from rrapply
library(rrapply)
rrapply(List, how = 'flatten')
#$listA_a
#[1] 1 2
#$listA_g
#[1] 1 2
#$listB_b
#[1] 1 2
data
List <- list(listA = list(listA_a = c(1, 2), listA_g = c(1, 2)), listB = list(
listB_b = c(1, 2)))
Another option is using flatten from package purrr
> purrr::flatten(lst)
$listA_a
[1] 1 2
$listA_g
[1] 1 2
$listB_b
[1] 1 2
Another option would be to make use of e.g. Reduce to concatenate the sublists:
list_of_lists <- list(
listA = list(listA_a = c(1, 2), listA_g = c(1, 2)),
listB = list(listB_b = c(1, 2)
))
Reduce(c, list_of_lists)
#> $listA_a
#> [1] 1 2
#>
#> $listA_g
#> [1] 1 2
#>
#> $listB_b
#> [1] 1 2
df1 <- data.frame(id = c(1,2,3,4,5,6,7,8)
df2 <- data.frame(id = c(3,5,9)
How to find the number of values that are not the same between two dataframes
for df1 6 (1,2,4,6,7,8)
for df2 1 (9)
WE can use setdiff
library(dplyr)
setdiff(df1, df2)
setdiff(df2, df1)
Or if we need duplicate values as well
library(vecsets)
vsetdiff(df1$id, df2$id)
vsetdiff(df2$id, df1$id)
Which items in df2are not (!) contained %in% df1?
df2[!df2$id %in% df1$id,]
[1] 9
Which items in df1are not (!) contained %in% df2?
df1[!df1$id %in% df2$id,]
[1] 1 2 4 6 7 8
Here is another base R option
If you want only the length of difference
> lapply(list(df1$id,df2$id), function(x) length(setdiff(x,intersect(df1$id,df2$id))))
[[1]]
[1] 6
[[2]]
[1] 1
If you want to see the difference in set elements
> lapply(list(df1$id,df2$id), function(x) setdiff(x,intersect(df1$id,df2$id)))
[[1]]
[1] 1 2 4 6 7 8
[[2]]
[1] 9
I have a list of 4 lists with the same name:
lst1 <-
list(list(c(1,2,3)),list(c(7,8,9)),list(c(4,5,6)),list(c(10,11,12)))
names(lst1) <- c("a","b","a","b")
I want to combine the sub lists together (first "a" with second "a", first "b" with second "b":
result <- list(list(c(1,2,3,4,5,6)),list(c(7,8,9,10,11,12)))
names(result) <- c("a","b")
I have tried multiple things, but can't figure it out.
Since lst1["a"] isn't going to give us all the elements of lst1 named a, we are going to need to work with names(lst1). One base R approach would be
nm <- names(lst1)
result <- lapply(unique(nm), function(n) unname(unlist(lst1[nm %in% n])))
names(result) <- unique(nm)
result
# $a
# [1] 1 2 3 4 5 6
#
# $b
# [1] 7 8 9 10 11 12
Another option is to use unlist first and then split the resulting vector.
vec <- unlist(lst1)
split(unname(vec), sub("\\d+$", "", names(vec)))
#$a
#[1] 1 2 3 4 5 6
#$b
#[1] 7 8 9 10 11 12
Just group the elements with the same name and unlist them:
tapply(lst1,names(lst1),FUN=function(x) unname(unlist(x)))