Unlisting a list of list while keeping second list names - r

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

Related

How to Effectively Join Two Lists Elementwise by Element Name

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)

How to automatically transform columns into objects in R?

I need that each column of my dataset become an object named with that column's name and containing its values as the object value.
I know how to do the process manually (see "Process question" heading below), but I need to automatize and generalize the process with as few rows as possible.
Example data
library(tibble)
df <- tibble(a = 1, b = 2, c = 3, d = 4)
Input
> df
# A tibble: 1 x 4
a b c d
<dbl> <dbl> <dbl> <dbl>
1 1 2 3 4
Process Question
How to automatize this part?
a <- df$a
b <- df$b
c <- df$c
d <- df$d
Output
> a;b;c;d
[1] 1
[1] 2
[1] 3
[1] 4
tibble/data.frame are list. So, we can use list2env (or use attach)
list2env(df, .GlobalEnv)
-checking
> a
[1] 1
> b
[1] 2

Unlist nested list by name

I import a nested list of unknown length (here 2) and unknown names (here iter1 and iter2) and get the names of the list:
iter1 <- list(1, 2, 3, 4)
iter2 <- list(1, 2, 3, 4)
nested_list <- list(iter1 = iter1, iter2 = iter2)
names <- names(nested_list)
The next thing I want to do is actually this:
unlist <- data.frame(x=unlist(nested_list$iter1))
But due to the fact I don't know the names beforehand I want to do something like this:
unlist <- data.frame(x=unlist(nested_list$names[1]))
Which is certainly not working. There is no error, but the created list is empty.
In the end I want to do something like this:
for(i in 1:length(nested_list)) {
unlist <- data.frame(x=unlist(nested_list$names[i]))
print(unlist)
}
Using Map, avoiding the names vector.
data.frame(Map(unlist, nested_list)[1])
# iter1
# 1 1
# 2 2
# 3 3
# 4 4
Or, in order to give column names with mapply:
data.frame(x=mapply(unlist, nested_list)[,1])
# x
# 1 1
# 2 2
# 3 3
# 4 4
The 1 in brackets indicates first list name, use 2 for the second name accordingly.
Data
nested_list <- list(iter1 = list(1, 2, 3, 4), iter2 = list(1, 2, 3, 4))
Maybe you can try the code below
unlist <- data.frame(x=unlist(nested_list[names[1]]))
such that
x
iter11 1
iter12 2
iter13 3
iter14 4
I am not sure I get what you intended as result, could you precise it if needed ?
iter1 <- list(1, 2, 3, 4)
iter2 <- list(1, 2, 3, 4)
nested_list <- list(iter1 = iter1, iter2 = iter2)
names <- names(nested_list)
cbind.data.frame(lapply(nested_list, unlist))
#> iter1 iter2
#> 1 1 1
#> 2 2 2
#> 3 3 3
#> 4 4 4

Save the results of combn() into a dataframe

Using the combn() in console we can see a list of results.
> combn(3, 2, simplify = FALSE)
[[1]]
[1] 1 2
[[2]]
[1] 1 3
[[3]]
[1] 2 3
How is it possible to save the result to a dataframe with one column which will have the results?
Example of the new dataframe:
1 2
1 3
2 3
Probably not the most elegant, but this should work :
data.frame(t(data.frame(combn(3, 2, simplify = FALSE))),row.names = NULL)

R: change data frame structure using values from one variable as new variable

df1 <- data.frame(
name = c("a", "b", "b", "c"),
score = c(1, 1, 2, 1)
)
How can I get a new data frame with variables/columns from df$name and with each 'corresponding' df$score. I figure that its actually a two-step problem:
First I would need to make a list of (in this example) unequal length vectors like this:
$a
[1] 1
$b
[1] 1 2
$c
[1] 1
Second, NAs need to be padded so one get vectors of equal length before making the desired data frame
that would be like:
a b c
1 1 1 1
2 NA 2 NA
I cannot find any simple means to do this - Im sure there must be!
If the solution can be delivered using dplyr it would be fantastic! Thanks!
To split the data:
(s <- split(df1$score, df1$name))
# $a
# [1] 1
#
# $b
# [1] 1 2
#
# $c
# [1] 1
To create the new data frame:
as.data.frame(sapply(s, `length<-`, max(vapply(s, length, 1L))))
# a b c
# 1 1 1 1
# 2 NA 2 NA
Slightly more efficient would be to use vapply in place of sapply
len <- max(vapply(s, length, 1L))
as.data.frame(vapply(s, `length<-`, double(len), len))
# a b c
# 1 1 1 1
# 2 NA 2 NA

Resources