1

There are many posts on converting list of data frames to single data frame (ex. here). However, I need to do that while preserving information from which list resulting rows are, i.e. I need to preserve an index of original list. If the list is unnamed, I need just an index number, while if the list is named I need to save the name of original list element. How can I do that?

With the data like below:

foo <- list(data.frame(x=c('a', 'b', 'c'),y = c(1,2,3)), 
            data.frame(x=c('d', 'e', 'f'),y = c(4,5,6)))

I need an output like this:

  index x y
1     1 a 1
2     1 b 2
3     1 c 3
4     2 d 4
5     2 e 5
6     2 f 6

while with named elements of a list:

foo <- list(df1 = data.frame(x=c('a', 'b', 'c'),y = c(1,2,3)), 
            df2 = data.frame(x=c('d', 'e', 'f'),y = c(4,5,6)))

the output would be:

  index x y
1   df1 a 1
2   df1 b 2
3   df1 c 3
4   df2 d 4
5   df2 e 5
6   df2 f 6
jakes
  • 1,964
  • 3
  • 18
  • 50
  • Seems related: [Combine (rbind) data frames and create column with name of original data frames](https://stackoverflow.com/questions/15162197/combine-rbind-data-frames-and-create-column-with-name-of-original-data-frames) – Henrik Jan 12 '19 at 09:49

2 Answers2

1

You could name the list if it is unnamed and then loop over them using lapply to create list of dataframes and rbind them together as one dataframe.

names(foo) <- if (is.null(names(foo))) seq_len(length(foo)) else names(foo)

do.call("rbind", lapply(names(foo), function(x) cbind(index = x, foo[[x]])))


#  index x y
#1     1 a 1
#2     1 b 2
#3     1 c 3
#4     2 d 4
#5     2 e 5
#6     2 f 6

For named list

do.call("rbind", lapply(names(foo), function(x) cbind(index = x, foo[[x]])))

#  index x y
#1   df1 a 1
#2   df1 b 2
#3   df1 c 3
#4   df2 d 4
#5   df2 e 5
#6   df2 f 6
Ronak Shah
  • 377,200
  • 20
  • 156
  • 213
1

We can use imap to get the index

library(purrr)
imap_dfr(foo, ~ .x %>% 
              mutate(index = .y))

Or with map

map_dfr(foo, .f = cbind, .id = 'index')
#  index x y
#1     1 a 1
#2     1 b 2
#3     1 c 3
#4     2 d 4
#5     2 e 5
#6     2 f 6

Or use Map from base R, where we loop through the elements of 'foo' and a corresponding sequence of 'foo', cbind to create a new column and then rbind the list elements

do.call(rbind, Map(cbind, index = seq_along(foo), foo))
#  index x y
#1     1 a 1
#2     1 b 2
#3     1 c 3
#4     2 d 4
#5     2 e 5
#6     2 f 6
akrun
  • 874,273
  • 37
  • 540
  • 662