Benni
Benni

Reputation: 785

data.table rbindlist column of lists

I want to transform a nested list to a data.table but I get this error:

library(data.table)
res = list(list(a=1, b=2, c=list(1,2,3,4,5)), list(a=2, b=3, c=list(1,2,3,4,5)))
rbindlist(res)
Error in rbindlist(res) : 
  Column 3 of item 1 is length 5, inconsistent with first column of that item which is length 1. rbind/rbindlist doesn't recycle as it already expects each item to be a uniform list, data.frame or data.table

My result should look like this:

data.table(a=c(1,2), b=c(2,3), c=list(c(1,2,3,4,5), c(1,2,3,4,5)))
   a b         c
1: 1 2 1,2,3,4,5
2: 2 3 1,2,3,4,5

I there a way to transform this list? It should work without knowing the column names beforehand.

Upvotes: 1

Views: 4579

Answers (3)

Plamen Petrov
Plamen Petrov

Reputation: 327

I think you need to first process every sublist and convert it to achieve your example output, like so

library(data.table)

res = list(list(a=1, b=2, c=list(1,2,3,4,5)), list(a=2, b=3, c=list(1,2,3,4,5)))

DTlist <- lapply(res, function(row_){
    lapply(row_, function(col_){
        if(class(col_) == 'list'){ 
          list(unlist(col_))
        }else{
          col_
        }
      })
})

rbindlist(DTlist)

The result would be

   a b         c
1: 1 2 1,2,3,4,5
2: 2 3 1,2,3,4,5

Sorry post is edited, bc I didnt recognize what the OP is trying to do initially. This also works, if the OP doesnt know which the sublist column is.

Upvotes: 2

JRR
JRR

Reputation: 3223

What you want to do is easy. But your input must be formatted as the following:

library(data.table)
res = list(list(a=1, b=2, c=list(c(1,2,3,4,5))), list(a=2, b=3, c=list(c(1,2,3,4,5))))
rbindlist(res)

You can transform your input with the following code

res = lapply(res, function(x) 
{ 
  x[[3]] <- list(unlist(x[[3]]))
  return(x)
})

Upvotes: 3

Emil Bode
Emil Bode

Reputation: 1830

Generally, R wants a column of a data.frame or data.table to be like a vector, meaning all single values. And rbindlist expects a list of data.frames, or things that can be treated as/converted to data.frames. So the reason your code fails is because it first tries to transform your input into 2 data.frames, for which the third column seems longer then the first and second.

But it is possible, you need to force R to construct 2 data.frames with each just one row. So we need the value of c to be just length-one, which we can do by making it a list with one element: a vector of length 5. And then we need to tell R it needs to really treat it "AsIs", and not convert it to something of length 5. For that, we have the function I which does nothing more then mark its input as "AsIs"

res <- list(data.frame(a=1, b=2, c=I(list(c(1,2,3,4,5)))),
            data.frame(a=2, b=3, c=I(list(c(1,2,3,4,5)))))
res2 <- rbindlist(res)

The data.frame calls are not even necessary, it also works with list. But generally, I think not relying on hoe other functions must first convert your input works best.

Upvotes: 3

Related Questions