Converting Nested List (Unequal Length) to Data Frame

Converting nested list (unequal length) to data frame

We get the length of list element ('indx') by looping with sapply. In the recent version of R, we can use lengths to replace the sapply(.., length) step. We change the length of each element to the max length from the 'indx' (length<-) and thereby pad NA values at the end of the list elements with length less than the max length. We can rbind the list elements, convert to data.frame and change the column names.

 indx <- sapply(lst, length)
#indx <- lengths(lst)
res <- as.data.frame(do.call(rbind,lapply(lst, `length<-`,
max(indx))))

colnames(res) <- names(lst[[which.max(indx)]])
res
# sk ques pval diff imp
#1 10 sfsf 0.05 <NA> <NA>
#2 24 wwww 0.11 0.3 <NA>
#3 24 wwww 0.11 0.3 2

data

 lst <- list(structure(c("10", "sfsf", "0.05"), .Names = c("sk", "ques", 
"pval")), structure(c("24", "wwww", "0.11", "0.3"), .Names = c("sk",
"ques", "pval", "diff")), structure(c("24", "wwww", "0.11", "0.3",
"2"), .Names = c("sk", "ques", "pval", "diff", "imp")))

Convert list with nested lists of different lengths and empty elements into dataframe in R

library(tidyverse)
test %>%
map_df(~.x %>%
map(~if(length(.)) . else NA) %>%
do.call(what = cbind) %>%
as_tibble)

Gives

  V1    V2    V3   
<chr> <chr> <chr>
1 a b NA
2 a c NA
3 a d NA
4 j r NA
5 j s NA
6 h NA i

Create a pandas dataframe from a nested lists of unequal lengths

The zip_longest function from itertools does this:

>>> import itertools, pandas
>>> pandas.DataFrame((_ for _ in itertools.zip_longest(*nest)), columns=['aa', 'bb', 'cc'])
aa bb cc
0 aa1 bb1 cc1
1 aa2 bb2 cc2
2 aa3 bb3 cc3
3 aa4 bb4 None
4 aa5 None None

If you have an older version of pandas you may need to wrap zip_longest in a list constructor. On older Python you may need to call izip_longest instead of zip_longest.

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

Put nested lists of different lengths into a dataframe one bellow another

Like this?

do.call(rbind, lapply(NestedList, function(x)as.data.frame(x, col.names=c("First", "Second", "Third"))))

gives,

    First Second Third
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
5 5 5 5
6 6 6 6
7 7 7 7
8 8 8 8
9 9 9 9
10 10 10 10
11 11 11 11
12 12 12 12
13 13 13 13
14 14 14 14
15 15 15 15
16 16 16 16
17 17 17 17
18 18 18 18
19 19 19 19
20 20 20 20
21 21 21 21
22 22 22 22
23 23 23 23
24 24 24 24
25 25 25 25
26 26 26 26
27 27 27 27
28 28 28 28
29 29 29 29
30 30 30 30
31 31 31 31
32 32 32 32
33 33 33 33
34 34 34 34
35 35 35 35
36 36 36 36
37 37 37 37
38 38 38 38
39 39 39 39
40 40 40 40
41 41 41 41
42 42 42 42
43 43 43 43
44 44 44 44
45 45 45 45
46 46 46 46
47 47 47 47
48 48 48 48
49 49 49 49
50 50 50 50
51 1 1 1
52 2 2 2
53 3 3 3
54 4 4 4
55 5 5 5
56 6 6 6
57 7 7 7
58 8 8 8
59 9 9 9
60 10 10 10
61 11 11 11
62 12 12 12
63 13 13 13
64 14 14 14
65 15 15 15
66 16 16 16
67 17 17 17
68 18 18 18
69 19 19 19
70 20 20 20
71 21 21 21
72 22 22 22
73 23 23 23
74 24 24 24
75 25 25 25
76 26 26 26
77 27 27 27
78 28 28 28
79 29 29 29
80 30 30 30
81 1 1 1
82 2 2 2
83 3 3 3
84 4 4 4
85 5 5 5
86 6 6 6
87 7 7 7
88 8 8 8
89 9 9 9
90 10 10 10
91 11 11 11
92 12 12 12
93 13 13 13
94 14 14 14
95 15 15 15
96 16 16 16
97 17 17 17
98 18 18 18
99 19 19 19
100 20 20 20

So, lapply goes through the list converting each element (i.e., each nested list) to a data frame and giving the columns the same names. do.call then binds these data frames together using rbind.

uneven nested list to dataframe

You could unlist and store it in a matrix and then change to a data frame.

# Helper function
conv=function(x) {
hr=floor(x/3600)
mins1=x%%3600
mins=floor(mins1/60)
secs=mins1%%60
return(paste0(hr,":",mins,":",secs))
}

# Mutation
library(dplyr)
library(lubridate)
n=names(unlist(res))[1:10]
f=matrix(unlist(res), ncol=10, byrow=TRUE)
f=data.frame(f, stringsAsFactors = FALSE)
colnames(f)=n
g=rename(f, b1=beta.b1, b2=beta.b2) %>%
mutate(across(time_iter:b2, as.numeric),
time_iter=time_iter*10000,
time_iter=conv(time_iter),
time_iter=as.difftime(time_iter, "%H:%M:%S", "secs"),
time_iter=time_iter/10000)

output

  modeltype   time_iter seed nobs hyperpar1 hyperpar2 metric1 metric2    b1    b2
1 tree 0.7099 secs 1 75 0.5 0.5 0.4847 0.2576 0.575 0.745
2 tree 0.0580 secs 2 75 0.5 0.5 0.4013 0.2569 0.535 0.775
3 tree 0.0460 secs 1 75 0.8 0.5 0.4755 0.2988 0.541 0.702
4 tree 0.0474 secs 2 75 0.8 0.5 0.2413 0.2147 0.545 0.793
5 tree 0.0502 secs 1 75 0.5 1.0 0.7131 0.5024 0.500 0.722
6 tree 2.9419 secs 2 75 0.5 1.0 0.4254 0.2824 0.555 0.712
7 tree 0.0410 secs 1 75 0.8 1.0 0.6709 0.4092 0.578 0.701
8 tree 0.0396 secs 2 75 0.8 1.0 0.4585 0.4115 0.501 0.777

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

Turning lists of unequal length lists into data frames

Here is an option using tidyverse

library(tidyverse)
out <- mylist %>%
transpose %>%
map(~ if(all(lengths(.x) == 1)) unlist(.x) else
map(.x, as_tibble) %>%
reduce(full_join, by = 'V1') %>%
mutate_all(funs(.[order(!is.na(.))])))

Now, we can extract the list elements

out$price %>%
set_names(c("Day", paste0("Price", LETTERS[1:3])))
# A tibble: 7 x 4
# Day PriceA PriceB PriceC
# <dbl> <dbl> <int> <int>
#1 1.00 NA NA 21
#2 2.00 NA NA 22
#3 3.00 NA 12 23
#4 4.00 NA 13 24
#5 5.00 7.00 14 25
#6 6.00 8.00 15 26
#7 7.00 9.00 16 27


Related Topics



Leave a reply



Submit