R:Convert Nested List into a One Level List

R : Convert nested list into a one level list

Try lapply together with rapply:

lapply(rapply(y, enquote, how="unlist"), eval)

#[[1]]
#[1] 12 54 2

#[[2]]
#[1] 2 88 1

#[[3]]
#[1] 4 8

It does work for deeper lists either.

Convert nested list with different names to data.frame filling NA and adding column

A shorter solution in base R would be

make_df <- function(a = NA, b = NA, z = NA) {
data.frame(a = unlist(a), b = unlist(b), z = unlist(z))
}

do.call(rbind, lapply(mylist, function(x) do.call(make_df, x)))
#> a b z
#> 1 1 2 <NA>
#> 2 3 NA <NA>
#> 3 NA 5 <NA>
#> 4 9 NA k

Update

A more general solution using the same method, but which does not require specific names would be:

build_data_frame <- function(obj) {
nms <- unique(unlist(lapply(obj, names)))
frmls <- as.list(setNames(rep(NA, length(nms)), nms))
dflst <- setNames(lapply(nms, function(x) call("unlist", as.symbol(x))), nms)
make_df <- as.function(c(frmls, call("do.call", "data.frame", dflst)))

do.call(rbind, lapply(mylist, function(x) do.call(make_df, x)))
}

This allows

build_data_frame(mylist)
#> a b z
#> 1 1 2 <NA>
#> 2 3 NA <NA>
#> 3 NA 5 <NA>
#> 4 9 NA k

Flatten nested lists in a list

Loop through the list, unlist recursively, then return as a list:

lapply(LIST2, function(i) list(unlist(i, recursive = TRUE)))

Convert a nested list to a list

Take a look at llply function from plyr package

> library(plyr)
> llply(mylist, unlist)
$a
A B
1 5

$b
C D
1 2

$c
E F
1 3

If you want to get rid of the names, then try:

> lapply(llply(mylist, unlist), unname)
$a
[1] 1 5

$b
[1] 1 2

$c
[1] 1 3

Convert nested list elements into data frame and bind the result into one data frame

Borrowing from Spacedman and flodel here, we can define the following pair of recursive functions:

library(tidyverse)  # I use dplyr and purrr here, plus tidyr further down below

depth <- function(this) ifelse(is.list(this), 1L + max(sapply(this, depth)), 0L)

bind_at_any_depth <- function(l) {
if (depth(l) == 2) {
return(bind_rows(l))
} else {
l <- at_depth(l, depth(l) - 2, bind_rows)
bind_at_any_depth(l)
}
}

We can now bind any arbitrary depth list into a single data.frame:

bind_at_any_depth(x)
# A tibble: 2 × 2
a b
<dbl> <dbl>
1 1 2
2 3 4
bind_at_any_depth(x_ext) # From P Lapointe
# A tibble: 5 × 2
a b
<dbl> <dbl>
1 1 2
2 5 6
3 7 8
4 1 2
5 3 4

If you want to keep track of the origin of each row, you can use this version:

bind_at_any_depth2 <- function(l) {
if (depth(l) == 2) {
l <- bind_rows(l, .id = 'source')
l <- unite(l, 'source', contains('source'))
return(l)
} else {
l <- at_depth(l, depth(l) - 2, bind_rows, .id = paste0('source', depth(l)))
bind_at_any_depth(l)
}
}

This will add a source column:

bind_at_any_depth2(x_ext)
# A tibble: 5 × 3
source a b
* <chr> <dbl> <dbl>
1 X_x_1 1 2
2 X_y_z 5 6
3 X_y_zz 7 8
4 Y_x_1 1 2
5 Y_y_1 3 4

Note: At some point you can use purrr::depth, and will need to change at_depth to modify_depth when their new version rolls out to CRAN (thanks @ManuelS).

convert nested list to data frame

This is a bit awkward because of the inconsisteny nesting levels, but we could write a recursive function to extract the lists that have "x" in their name. For example

find_x <- function(x) {
if (is.list(x) && !"x" %in% names(x)) {
return(do.call("rbind", lapply(x, find_x)))
} else if ( !is.list(x)) {
return(NULL)
} else {
return(x)
}
}
find_x(l)
# x y
# [1,] 1 2 3 4
# [2,] 3 4 5 6
# [3,] 1 2 3 4
# [4,] 2 3 4 5

You can change the "x" part to whatever marker you have for your own data of interest

Convert nested list to data.frame

Base R option :

do.call(rbind, lapply(l, function(x) {
data.frame(A = x[[1]], B = unlist(x[[2]]), C = names(x[[2]]))
}))

# A B C
#a1 a a1v a1
#b1 b b1v b1
#b2 b b2v b2
#c11 c c1v1 c1
#c12 c c1v2 c1
#c13 c c1v3 c1

flattern nested list with uneven column numbers into data frame in R

tibbles are a nice format, as they support nested data.frames. I would aim for a tibble with 2 rows, a wide format. In it, each nested list element would be its own data.frame, which we could manipulate later when needed. I would do something like this:

library(tidyverse)
l = unlist(l, recursive = F)
ind_to_nest <- which(map_lgl(l[[1]], is.list))
non_tbl <- map(l, ~ .x[-ind_to_nest])
tbl <- map(l, ~ .x[ind_to_nest])

df <- bind_rows(non_tbl) %>%
mutate(n = 1:n(), .before = 1) %>%
mutate(data = map(tbl, ~ map(.x, ~flatten(.x) %>% bind_cols))) %>%
unnest_wider(data, simplify = F)

Note that this does throw a bunch of warnings. This is because of the name conflicts present within the list.

#> New names:
#> * id -> id...5
#> * id -> id...10

Can be resolved by specifying a naming policy, or by rethinking how the data is read into R to resolve naming conflicts early.

#> Outer names are only allowed for unnamed scalar atomic inputs 

This is a bit tougher to resolve, but this issue is a starting point.

For analysis some cleaning of sub-tibbles can be performed when needed, as different tasks require different shapes.

Unlist the second to last list of a nested list

This can be done by using recursion. A simple recursion will be:

my_fun <- function(x) if(is.list(x[[1]])) lapply(x, my_fun) else lengths(x)

out <- my_fun(have_list)

identical(out, want_list)
[1] TRUE

How to convert a nested lists to dataframe in R?

temp = unique(unlist(lapply(All, names)))
mydf = setNames(object = data.frame(lapply(temp, function(nm)
unlist(lapply(All, function(x) x[[nm]])))), nm = temp)

mydf
# item1 item2
#iter1 1 a
#iter2 1 b

OR

do.call(rbind, lapply(All, data.frame))
# item1 item2
#iter1 1 a
#iter2 1 b

OR

data.table::rbindlist(All, idcol = TRUE)
# .id item1 item2
#1: iter1 1 a
#2: iter2 1 b


Related Topics



Leave a reply



Submit