34

rbind does not check for column names when binding together vectors:

l = list(row1 = c(10, 20), row2 = c(20, 10))
names(l$row1) = c("A", "B")
names(l$row2) = c("B", "A")
l
$row1
 A  B 
10 20 

$row2
 B  A 
20 10 

rbind(l$row1, l$row2)
      A  B
[1,] 10 20
[2,] 20 10

How can I produce this matrix from a number of list elements, insuring the column names are correctly matched across rows:

      A  B
[1,] 10 20
[2,] 10 20
Matthew Plourde
  • 43,932
  • 7
  • 96
  • 113
Robert Kubrick
  • 8,413
  • 13
  • 59
  • 91

7 Answers7

28

It seems that in current versions of R (I have version 3.3.0), rbind has the capacity to to join two data sets with the same name columns even if they are in different order.

   df1 <- data.frame(a = c(1:5), c = c(LETTERS[1:5]),b=c(11:15))
   df2 <- data.frame(a = c(6:10), b = c(16:20),c=c(LETTERS[6:10]))
   rbind(df1,df2)
    a c  b
1   1 A 11
2   2 B 12
3   3 C 13
4   4 D 14
5   5 E 15
6   6 F 16
7   7 G 17
8   8 H 18
9   9 I 19
10 10 J 20
scs76
  • 393
  • 3
  • 5
  • I find this is only true when combining two elements. When attempting to combine three, the third is bound without matching labels. Can someone confirm this? – Des Grieux Apr 09 '18 at 17:00
  • 1
    It seems to work fine with three elements in R version 3.5.2: `df1 <- data.frame(a = c(1:5), c = c(LETTERS[1:5]),b=c(11:15)) df2 <- data.frame(a = c(6:10), b = c(16:20),c=c(LETTERS[6:10])) df3 <- data.frame(c = c(LETTERS[11:13]), a = c(11:13), b = c(21:23)) rbind(df1, df2, df3)` – user29609 Feb 25 '19 at 21:57
  • It still seems to work with R version 4.1.1 – FrsLry Apr 14 '22 at 13:27
  • Yes, with `rbind`: (1) number of columns must match between the two dataframes (otherwise you get `numbers of columns of arguments do not match`) and column names must match as well (otherwise you get `names do not match previous names`). R 4.3.1. – robertspierre Jul 07 '23 at 10:34
22

smartbind() will match column names and tolerates missing ones:

library(gtools)
do.call(smartbind,l)
      A  B
row1 10 20
row2 10 20
Ari B. Friedman
  • 71,271
  • 35
  • 175
  • 235
15

You can use match:

l <- list(row1 = setNames(1:3, c("A", "B", "C")),
          row2 = setNames(1:3, c("B", "C", "A")),
          row3 = setNames(1:3, c("C", "A", "B")))

do.call(rbind, lapply(l, function(x) x[match(names(l[[1]]), names(x))]))

The result:

     A B C
row1 1 2 3
row2 3 1 2
row3 2 3 1
Sven Hohenstein
  • 80,497
  • 17
  • 145
  • 168
11

rbind will work if you first change each element of l to a data frame:

do.call("rbind", lapply(l, function(x) data.frame(as.list(x))))

      A  B
row1 10 20
row2 10 20
SchaunW
  • 3,561
  • 1
  • 21
  • 21
  • Shoot. Matthew Plourde beat me to the punch. – SchaunW Jun 06 '13 at 13:07
  • 2
    +1! and also notice that what it is interesting here is that the classical equivalent `rbindlist` from `data.table` don't give the same answer as `do.call(rbind,...)` – agstudy Jun 06 '13 at 13:12
  • A nice feature of the `rbindlist` is the ability to fill empty values with NA quite easily. – Brandon Feb 18 '15 at 22:30
7
do.call(rbind, lapply(l, function(row) row[order(names(row))]))
Matthew Plourde
  • 43,932
  • 7
  • 96
  • 113
1

Why not just rbind(l$row1, l$row2[names(l$row1)]). Also works well for data frames. Note that this will discard columns from l$row2 that don't appear in l$row1.

David Roberts
  • 617
  • 1
  • 11
  • 23
0

Reduce is a powerful function, but some how not frequently used ; here's an alternate implementation

This will create a rbind where if there are columns that don't match "NA" will be generated for them.

rbindedFrame = Reduce(custom_rbind,listofDataframes)  

custom_rbind = function(x1,x2){
  c1 = setdiff(colnames(x1),colnames(x2))
  c2 = setdiff(colnames(x2),colnames(x1))
  for(c in c2){##Adding missing columns from 2 in 1
    x1[[c]]=NA
  }
  for(c in c1){##Similiarly ading missing from 1 in 2
    x2[[c]]=NA
  }
  x2 = x2[colnames(x1)]
  rbind(x1,x2)
}
}
Yash
  • 307
  • 3
  • 10