45

I've got a list of lists, call it listHolder, which has length 5.

Every element in listHolder is a list of numeric data, with 160 or so elements.

I need to turn this list of lists into a data.frame of length 5, with each element being a numeric vector with 160 or so elements.

But everything I've tried, from iterating through the list of lists and turning each element with as.numeric(unlist(listHolder[[i]])), to

data.frame(matrix(unlist(listHolder), nrow = length(totalKeywords), byrow = T))

ends up creating a data frame of length 160 or so, with each element being a numeric vector with 5 or so elements.

How do I do what I want?

Attempting data.frame(matrix(unlist(totalKeywords), nrow=132, byrow=T)) yields the opposite of what I want - 160 small items each 5 elements long.

Léo Léopold Hertz 준영
  • 126,923
  • 172
  • 430
  • 675
Bacter
  • 573
  • 1
  • 4
  • 6
  • 14
    Try `do.call(rbind, listHolder)`. – dimitris_ps Apr 16 '15 at 12:26
  • 1
    possible duplicate of [R list to data frame](http://stackoverflow.com/questions/4227223/r-list-to-data-frame) –  Apr 16 '15 at 12:28
  • I think he wants to turn each listHolder item in a column, so it would be `cbind` instead of `rbind`. – Molx Apr 16 '15 at 12:28
  • 2
    Why not `as.data.frame(listHolder)` – shadow Apr 16 '15 at 12:35
  • Could you provide sample data for `listHolder`? – Hack-R Apr 16 '15 at 12:47
  • Sure: print(listHolder) = [[1]] [1] 0 0 0 0 1 1 0 1 0 1... (on for 160 or so), [[2]] [1] 4 0 2 4 23 0 3 2 4... (on for 160 or so), [[3]] [1] 2 3 1 3 3.... and so on. I tried the solution in "R list to data frame", and ended up with 160 objects called "X1" "X2" "X3" and so on, each of them with five members. doing do.call(cbind, listHolder) gives me what looks like a matrix - the columns labeled with [,1] [,2] [,3] and the rows with [1,], [2,], [3,] and so on – Bacter Apr 16 '15 at 14:51

6 Answers6

46

AS @dimitris_ps mentioned earlier, the answer could be:

do.call(rbind, listHolder)

Since do.call naturally "strips" 1 level of the "list of list", obtaining a list, not a list of lists.

After that, rbind can handle the elements on the list and create a matrix.

Camilo Abboud
  • 768
  • 6
  • 7
  • 4
    This extremely useful when the "rows" were named lists, as it keeps the names of the list (assuming the names are consistent) – tmrlvi Nov 02 '18 at 12:07
  • ...and it does not change data types like 'unlist' above. This is the Answer! – ned Feb 25 '21 at 23:59
  • Super useful! However, is there a way to incude the number/name of each list into the final dataframe? – vog Nov 16 '21 at 18:40
32

The value of nrow needs to be fixed. I fixed your code as follows:

dd  <-  as.data.frame(matrix(unlist(listHolder), nrow=length(unlist(listHolder[1]))))
Eray Balkanli
  • 7,306
  • 10
  • 44
  • 74
Noha Elprince
  • 1,914
  • 1
  • 15
  • 10
  • 6
    Is there anything I could do to preserve types? Following this approach, if one column was `POSIXct`, it converts it to `chr`. – Rafael Jul 12 '17 at 14:05
  • Types can be preserved by using `Reduce` (as in my answer). – luco00 Jul 13 '21 at 10:21
15

This is the easiest solution I've found.

library(jsonlite)
library(purrr)
library(data.table)

dt_list <- map(list_of_lists, as.data.table)
dt <- rbindlist(dt_list, fill = TRUE, idcol = T)

dt
David
  • 382
  • 2
  • 9
  • The only way that worked for me. Still, does not preserve `POSIXct` as was asked earlier. – Marco Sep 13 '21 at 09:21
11

This achieves a similar result, but is more intuitive (to me at least)

#Generate fake data 
listoflists=list(c(1,'a',3,4),c(5,'b',6,7))

#Convert to a dataframe, transpose, and convert the resulting matrix back to a dataframe
df= as.data.frame(t(as.data.frame(listoflists)))

#Strip out the rownames if desired
rownames(df)<-NULL

#Show result
df
sean
  • 500
  • 6
  • 17
7

I keep coming to this question and usually end up adapting the current answers to my need.

Current answers either mess up with types of variables, or do not handle well proper list of lists (note the plural).

tl;dr: use the following:

This works when each element in listHolder contains a column of the dataframe

df <- data.frame(lapply(listHolder, function(x) Reduce(c, x)))

This works when each element in listHolder contains a row of the dataframe

df <- do.call(rbind, lapply(listHolder, data.frame))

Minimal Working Example (list elements are columns)

The following code provides a MWE and reviews other answers. The #General Approach is what I would recommend to use.

listHolder <- list(
  A = rep(list(1, 2), 80),
  B = rep(c(3, 4), 80),
  C = rep(c("a", "b"), 80),
  D = rep(list("c", "d"), 80),
  E = rep(as.POSIXct(10485849600, origin = "1582-10-14", tz = "GMT"), 160)
)


# @Noha's Answer
data1  <-  as.data.frame(matrix(unlist(listHolder), nrow=length(unlist(listHolder[1]))))
# Try this (mess up with types)
str(data1)

# @Camilo's Answer
data2 <- data.frame(do.call(cbind, listHolder))
# Try this (each column becomes a list)
str(data2)

# General Approach
data3 <- data.frame(lapply(listHolder, function(x) Reduce(c, x)))
str(data3)

Minimal Working Example (list elements are rows)

This code should be used when each element in the list is supposed to hold a row in the dataframe

listHolder <- list(
  row1 = list(name = "foo", surname = "bar", age = 90),
  row2 = list(name = "foo", surname = "foo", age = 29),
  row3 = list(name = "bar", surname = "foo", age = 45),
  row4 = list(name = "bar", surname = "bar", age = 10)
)

# A simple rbind won't work (each column is still a list)
data1 <- do.call(rbind, listHolder)
str(data1)

# General Approach (now it's better)
data2 <- do.call(rbind, lapply(listHolder, data.frame))
str(data2)
luco00
  • 361
  • 3
  • 7
-1

I think this is easier than the previous solutions:

mydf = data.frame(x1 = c('a', 'b', 'c'))
mylist = list(c(4, 5), c(4, 5), c(4, 5))
mydf$x2 = mylist
print(mydf)
  x1   x2
1  a 4, 5
2  b 4, 5
3  c 4, 5
Zhaochen He
  • 500
  • 3
  • 10