0

I have two data frames:

> df1 <- data.frame(a=3:4, b=1:2)
> df2 <- data.frame(a=5:6, b=7:8)
>
> df1
  a b
1 3 1
2 4 2
>
> df2
  a b
1 5 7
2 6 8

How can I combine (or zip or interleave) them by rows so the result will be following:

> df_final <- some_magic_function(df1, df2)
> df_final
1 3 1
2 5 7
3 4 2
4 6 8

I've found similar approaches here and there, but it is for vectors:

> a <- c(1, 2, 3)
> b <- c(4, 5, 6)
>
> a
[1] 1 2 3
> b
[1] 4 5 6
>
> as.vector(rbind(a,b))
[1] 1 4 2 5 3 6
>
> z1 <- setNames(c(a,b),rep(seq_along(a),2))
> z1 <- as.vector(z1[order(names(z1))]) 
> z1
[1] 1 4 2 5 3 6
>
> c( matrix(c(a,b), nrow=2, byrow=TRUE) ) 
[1] 1 4 2 5 3 6
>
> c(a,b)[ order( c(seq_along(a), seq_along(b)))] 

How to do the similar for data frames? Please note I want to interleave rows, not to concatenate data frames and then somehow rearrange it's rows.

Wakan Tanka
  • 7,542
  • 16
  • 69
  • 122

3 Answers3

2

Try this:

as.data.frame.matrix(mapply(function(x,y){rbind(x,y)},df1,df2))

#  a b
#1 3 1
#2 5 7
#3 4 2
#4 6 8

or

Reduce(function(x,y){rbind(x,y)},(sapply(1:nrow(df1),
 functio‌​n(i,x,y){rbind(x[i,]‌​,y[i,])},df1,df2,sim‌​plify = FALSE)))
tushaR
  • 3,083
  • 1
  • 20
  • 33
  • while the OP example is all numeric, preserving column types is likely desirable. – hrbrmstr Oct 17 '17 at 11:17
  • @hrbrmstr Sorry, but I did not get the point you are trying to make. Could you please explain in detail the issues my approach would lead to? – tushaR Oct 17 '17 at 11:22
  • 1
    your version turns it into a matrix along the way. by default that means column types get converted. if the OP's real data frame has a mix of types, this will coerce them into the same type. – hrbrmstr Oct 17 '17 at 11:27
  • @hrbrmstr Got it. Thanks a lot. – tushaR Oct 17 '17 at 11:28
  • 3
    For data frames with cols of the same type, this is definitely a clever solution! – hrbrmstr Oct 17 '17 at 11:29
  • 1
    @hrbrmstr: How about this approach: `Reduce(function(x,y){rbind(x,y)},(sapply(1:nrow(df1),function(i,x,y){rbind(x[i,],y[i,])},df1,df2,simplify = F)))` ? – tushaR Oct 17 '17 at 12:08
2

These preserves column types but 2 of them rely on an internal ggplot2 function.

The easy way:

gdata::interleave(df1, df2)

The hard way(s):

Base:

do.call(
  cbind.data.frame, 
  setNames(
    lapply(
      colnames(df1), 
        function(x) { 
          ggplot2:::interleave(df1[,x], df2[,x])
        }
    ), colnames(df1)
  )
)

Tidyverse:

library(tidyverse)

map(colnames(df1), ~ggplot2:::interleave(df1[,.x], df2[,.x])) %>% 
  set_names(colnames(df1)) %>% 
  bind_cols()
hrbrmstr
  • 77,368
  • 11
  • 139
  • 205
1

A slightly over-fitted, Vectorized solution could be,

full_d <- rbind(df1, df2)
rbind(full_d[c(TRUE, FALSE),], full_d[c(FALSE, TRUE),])
Sotos
  • 51,121
  • 6
  • 32
  • 66