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
tibble
s 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
Warning When Defining Factor: Duplicated Levels in Factors Are Deprecated
Ggplot2 Error "No Layers in Plot"
Subset Data Based on Partial Match of Column Names
How to Increase Stack Space Overflow for Pandoc in R
How Does One Merge Dataframes by Row Name Without Adding a "Row.Names" Column
R: Data.Table Count !Na Per Row
Passing a 'Data.Table' to C++ Functions Using 'Rcpp' And/Or 'Rcpparmadillo'
Can Lapply Not Modify Variables in a Higher Scope
Scoping and Functions in R 2.11.1:What's Going Wrong
How to Jitter Two Ggplot Geoms in the Same Way
Tm: Read in Data Frame, Keep Text Id'S, Construct Dtm and Join to Other Dataset
Match.Call with Default Arguments
Aggregating Multiple Columns in Data.Table
How to Reset All Options() Arguments to Their Default Values
Adding Regression Line Equation and R2 on Separate Lines Graph
Check If Character String Is a Valid Color Representation
Setting Hex Bins in Ggplot2 to Same Size
Ggplot2 Time Series Plotting: How to Omit Periods When There Is No Data Points