combine elements of list of lists with the same name - r

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

Related

How to organize the output of the list of list in R

Suppose this is my list of list (I would like to organize the result as my data contains more than 40 results and it is difficult for me to organize them manually).
s <- c(1,2,3)
ss <- c(4,5,6)
S <- list(s,ss)
h <- c(4,8,7)
hh <- c(0,3,4)
H <- list(h,hh)
HH <- list(S,H)
names1 <- c("First","Second")
lapply(setNames(HH, paste0(names1, '_Model')), function(x)
setNames(x, paste0('Res_', seq_along(x))))
#$First_Model
#$First_Model$Res_1
#[1] 1 2 3
#$First_Model$Res_2
#[1] 4 5 6
#$Second_Model
#$Second_Model$Res_1
#[1] 4 8 7
#$Second_Model$Res_2
#[1] 0 3 4
I would like to have the result similar to the following:
#$First_Model
#$First_Model$Res_1
#[1] 1 2 3
#$Second_Model
#$Second_Model$Res_1
#[1] 4 8 7
#$First_Model$Res_2
#[1] 4 5 6
#$Second_Model$Res_2
#[1] 0 3 4
The problem in question is how to rearrange the nested list from "Model No. > Results No." to "Results No. > Model No."
I was going for something similar to Wimpel's answer.
Res_no <- seq_along(HH[[1]]) # results elements
lapply(setNames(Res_no, paste0("Res_", Res_no)), function(x)
lapply(setNames(HH, paste0(names1, '_Model')), `[[`, x)
)
Output
#$Res_1
#$Res_1$First_Model
#[1] 1 2 3
#
#$Res_1$Second_Model
#[1] 4 8 7
#
#
#$Res_2
#$Res_2$First_Model
#[1] 4 5 6
#
#$Res_2$Second_Model
#[1] 0 3 4
The base of this solution is to extract the x-th element of the nested list (seen in the inner lapply() function of the code). You can do this with lapply or purrr:map, as described here.
The outer lapply() function lets you repeat it for all the "Results No."
Something like this perhaps?
# From your code, create a list L
L <- lapply(setNames(HH, paste0(names1, '_Model')), function(x)
setNames(x, paste0('Res_', seq_along(x))))
# get all x-th elements from the list, and add them to new list L2
L2 <- lapply( 1:length(L[[1]]), function(x) {
lapply(L, "[[", x)
})
# set names of L2
names(L2) <- names(L[[1]])
output
# $Res_1
# $Res_1$First_Model
# [1] 1 2 3
#
# $Res_1$Second_Model
# [1] 4 8 7
#
#
# $Res_2
# $Res_2$First_Model
# [1] 4 5 6
#
# $Res_2$Second_Model
# [1] 0 3 4

How to use elements from a second list within lapply()

I have two lists, for example:
L1 <- list(A=1:3, B=4:6)
L2 <- list(A=2, B=3)
and I want the elements of the first list to be repeated by the numers in the second list, resulting in:
>L3
$A
[1] 1 2 3 1 2 3
$B
[1] 4 5 6 4 5 6 4 5 6
I tried with lapply() but I cannot figure out the right times argument:
L3 <- lapply(L1, function(x) rep(x, L2))
Certainly quite easy to solve, but I don't get it right now.
Here is one solution:
lapply(names(L1), function(x) rep(L1[[x]], L2[[x]]))

How to merge lists with identical column names to get their union

Say I have N lists that all have the same column names. I want to merge these such that I get a resulting list with same columns, but now containing entries from all N list. Here is a MWE showing what I want:
ls<-list()
ls[[1]]<-list("a"=1,
"b"=2)
ls[[2]]<-list("a"=3,
"b"=4)
#how to write a one-liner that produces lsTotal, which is the union of ls[[1]] and ls[[2]]?
lsTotal<-list("a"=c(1,3),
"b"=c(2,4))
I found this thread, from which I can use Map(c, ls[[1]], ls[[2]]). However, writing it out is tedious if ls is very long. Is there a shortcut?
One option is tidyverse
library(purrr)
library(dplyr)
transpose(ls) %>%
map(unlist)
Or use Map with do.call
do.call(Map, c(f=c, ls))
#$a
#[1] 1 3
#$b
#[1] 2 4
Here is a simple two-liner with unlist and split.
# get a named vector
tmp <- unlist(ls)
# split on the names
split(unname(tmp), names(tmp))
$a
[1] 1 3
$b
[1] 2 4
I know this question already has a few answers but another option is to use Reduce with your Map to apply the Map to each of the elements successively in the list:
Reduce(function(x,y) Map(c,x,y), ls)
#$a
#[1] 1 3
#$b
#[1] 2 4
Or for a more complicated example, the results are:
ls <- list(list(a=1, b=2), list(a=3, b=4), list(a=2,b=4), list(a=5,b=2), list(a=3,b=2))
#$a
#[1] 1 3 2 5 3
#$b
#[1] 2 4 4 2 2

Remove elements in a list in R

I want to remove part of the list where it is a complete set of the other part of the list. For example, B intersect A and E intersect C, therefore B and E should be removed.
MyList <- list(A=c(1,2,3,4,5), B=c(3,4,5), C=c(6,7,8,9), E=c(7,8))
MyList
$A
[1] 1 2 3 4 5
$B
[1] 3 4 5
$C
[1] 6 7 8 9
$E
[1] 7 8
MyListUnique <- RemoveSubElements(MyList)
MyListUnique
$A
[1] 1 2 3 4 5
$C
[1] 6 7 8 9
Any ideas ? Any know function to do it ?
As long as your data is not too huge, you can use an approach like the following:
# preparation
MyList <- MyList[order(lengths(MyList))]
idx <- vector("list", length(MyList))
# loop through list and compare with other (longer) list elements
for(i in seq_along(MyList)) {
idx[[i]] <- any(sapply(MyList[-seq_len(i)], function(x) all(MyList[[i]] %in% x)))
}
# subset the list
MyList[!unlist(idx)]
#$C
#[1] 6 7 8 9
#
#$A
#[1] 1 2 3 4 5
Similar to the other answer, but hopefully clearer, using a helper function and 2 sapplys.
#helper function to determine a proper subset - shortcuts to avoid setdiff calculation if they are equal
is.proper.subset <- function(x,y) !setequal(x,y) && length(setdiff(x,y))==0
#double loop over the list to find elements which are proper subsets of other elements
idx <- sapply(MyList, function(x) any(sapply(MyList, function(y) is.proper.subset(x,y))))
#filter out those that are proper subsets
MyList[!idx]
$A
[1] 1 2 3 4 5
$C
[1] 6 7 8 9

Naming list items via loop in R

I want to take a list, create a String vector of the names of the list items, filling in blanks with a generic name and then set the names vector as the names of the list.
My code works fine for list who dont have items with names in it. It however does nothing, when there are items with a name in it.
addNamesToList <- function(myList){
listNames <- vector()
for(i in 1:length(myList)){
if(identical(names(myList[i]),NULL)){
listNames <- c(listNames,paste("item",i,sep=""))
}else{
listNames <- c(listNames,names(myList[i]))
}
}
names(myList) <- listNames
return (myList)
}
result without named items
$item1
[1] 2 3 4
$item2
[1] "hey" "ho"
result with named items
[[1]]
[1] 2 3 4
[[2]]
[1] "hey" "ho"
$hello
[1] 2 3 4
Hope you can help.
It sounds like you want to insert names where there is not currently a name. If that's the case, I would suggest using direct assignment via names(x) <- value, instead of using a loop to fill in the blanks.
In the following example, lst creates a sample list of three elements, the second of which is not named. Notice that even if only one of the list element has a name, its names vector is a character vector the same length as lst.
( lst <- list(item1 = 1:5, 6:10, item3 = 11:15) )
# $item1
# [1] 1 2 3 4 5
#
# [[2]]
# [1] 6 7 8 9 10
#
# $item3
# [1] 11 12 13 14 15
names(lst)
# [1] "item1" "" "item3"
We can insert a name into the empty name element with the following. This will also work with a vector, provided the right side vector is the same length as the left side vector.
names(lst)[2] <- "item2"
lst
# $item1
# [1] 1 2 3 4 5
#
# $item2
# [1] 6 7 8 9 10
#
# $item3
# [1] 11 12 13 14 15
For a longer list containing sporadic empty names, you can use
names(list)[!nzchar(names(list))] <- namesToAdd
nzchar basically means "non-zero character" and returns a logical, TRUE if the element is a non-zero length string.

Resources