R rbind a dataframe of dataframes

Viewed 315

How is it possible to concatenate a dataframe that contains one or more data.frames among its columns. For example:

df <- data.frame(a=1:3)
df$df <- data.frame(a=1:3)  
rbind( df, df)

Error in row.names<-.data.frame(*tmp*, value = value) :
duplicate 'row.names' are not allowed In addition: Warning message: non-unique values when setting 'row.names': ‘1’, ‘2’, ‘3’

library(dplyr)
bind_rows(list(df,df))

Error: Argument 2 can't be a list containing data frames

3 Answers

One option would be to replicate df twice (or more) instead of rbind-ing it; this will automatically create non duplicated row.names. Try this:

df[rep(seq_len(nrow(df)), 2), ]
# output
    a a
1   1 1
2   2 2
3   3 3
1.1 1 1
2.1 2 2
3.1 3 3

The same process using dplyr will give you more interesting row.names:

library(dplyr)
df %>% slice(rep(row_number(), 2))
# output
  a a
1 1 1
2 2 2
3 3 3
4 1 1
5 2 2
6 3 3

The issue here seems to be not another data.frame within a data frame, but the non-unique rownames in the result. If you made sure that rownames are unique after rbind - it should work:

df1 <- data.frame(a=1:3)
df2 <- data.frame(a=1:3)
df1$df <- data.frame(a=1:3, row.names=letters[1:3])
df2$df <- data.frame(a=1:3, row.names=LETTERS[1:3])

> res <- rbind(df1, df2)
> res
  a a
1 1 1
2 2 2
3 3 3
4 1 1
5 2 2
6 3 3

> res$df
  a
a 1
b 2
c 3
A 1
B 2
C 3

The problem seems to be that rbind adjusts the rownames for the two data.frames being merged, but does not adjust the rownames for data.frames within data.frames.

We may list the data frames, then using mapply to handle column types differently: stack for vectors and do.call(rbind) for data.frames.

L <- mget(ls(pattern="df\\."))  # or list(df.1, df.2, df.3)
res <- data.frame(a=stack(mapply(`[`, L, 1))[[1]])
res$df <- do.call(rbind, mapply(`[`, L, 2))
res
#   a a
# 1 1 1
# 2 2 2
# 3 3 3
# 4 4 4
# 5 5 5
# 6 6 6
# 7 7 7
# 8 8 8
# 9 9 9
str(res)
# 'data.frame': 9 obs. of  2 variables:
#   $ a : int  1 2 3 4 5 6 7 8 9
# $ df:'data.frame':    9 obs. of  1 variable:
#   ..$ a: int  1 2 3 4 5 6 7 8 9

Data

df.1 <- structure(list(a = 1:3, df = structure(list(a = 1:3), class = "data.frame", row.names = c(NA, 
-3L))), row.names = c(NA, -3L), class = "data.frame")
df.2 <- structure(list(a = 4:6, df = structure(list(a = 4:6), class = "data.frame", row.names = c(NA, 
-3L))), row.names = c(NA, -3L), class = "data.frame")
df.3 <- structure(list(a = 7:9, df = structure(list(a = 7:9), class = "data.frame", row.names = c(NA, 
-3L))), row.names = c(NA, -3L), class = "data.frame")
Related