Search code examples
rdataframelistmergereduce

list of dataframes back to list after merge


I have a list of dataframes with different numbers of rows but a single id

set.seed(1)
f <- function(){
  m <- matrix(1:40,ncol = 4, dimnames = list(NULL, paste0("x",1:4)))
  m <- cbind.data.frame(id=1:10, m)
  m[-sample(10,sample(9,1)),]
  
}  
li <- lapply(1:5,\(x) f())  ; names(li) <- paste0("Obj ", 1:length(li))
li

$`Obj 1`
  id x1 x2 x3 x4
6  6  6 16 26 36

$`Obj 2`
   id x1 x2 x3 x4
2   2  2 12 22 32
3   3  3 13 23 33
4   4  4 14 24 34
6   6  6 16 26 36
7   7  7 17 27 37
8   8  8 18 28 38
10 10 10 20 30 40

$`Obj 3`
  id x1 x2 x3 x4
2  2  2 12 22 32
3  3  3 13 23 33
4  4  4 14 24 34
8  8  8 18 28 38

$`Obj 4`
  id x1 x2 x3 x4
6  6  6 16 26 36

$`Obj 5`
   id x1 x2 x3 x4
1   1  1 11 21 31
4   4  4 14 24 34
5   5  5 15 25 35
7   7  7 17 27 37
9   9  9 19 29 39
10 10 10 20 30 40

I need to merge them all together

merged_df <- Reduce(\(x,y) merge(x, y, by = "id", all = T), li)

merged_df
 id x1.x x2.x x3.x x4.x x1.y x2.y x3.y x4.y x1.x x2.x x3.x x4.x x1.y x2.y x3.y x4.y x1 x2 x3 x4
1   1   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA  1 11 21 31
2   2   NA   NA   NA   NA    2   12   22   32    2   12   22   32   NA   NA   NA   NA NA NA NA NA
3   3   NA   NA   NA   NA    3   13   23   33    3   13   23   33   NA   NA   NA   NA NA NA NA NA
4   4   NA   NA   NA   NA    4   14   24   34    4   14   24   34   NA   NA   NA   NA  4 14 24 34
5   5   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA  5 15 25 35
6   6    6   16   26   36    6   16   26   36   NA   NA   NA   NA    6   16   26   36 NA NA NA NA
7   7   NA   NA   NA   NA    7   17   27   37   NA   NA   NA   NA   NA   NA   NA   NA  7 17 27 37
8   8   NA   NA   NA   NA    8   18   28   38    8   18   28   38   NA   NA   NA   NA NA NA NA NA
9   9   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA   NA  9 19 29 39
10 10   NA   NA   NA   NA   10   20   30   40   NA   NA   NA   NA   NA   NA   NA   NA 10 20 30 40

And then return it back to the list while preserving the original names of the columns that were originally in the list and preserving the names of each list object that were originally.

How can I do that?

=========UPD========

here is my original list

$`Obj 1`
  id x1 x2
1  1  1  4
3  3  3  6

$`Obj 2`
  id x1 x2
2  2  2  5
3  3  3  6

I want to get this

$Obj1
  id x1 x2
1  1  1  4
2  2 NA NA
3  3  3  6

$Obj2
  id x1 x2
1  1 NA NA
2  2  2  5
3  3  3  6

Solution

  • I don't think this is a reduction, I suspect it's just enforcing all ids in all frames.

    lapply(li, merge,
       data.frame(id = sort(unique(unlist(lapply(li, `[[`, "id"))))),
       by = "id", all = TRUE)
    # $`Obj 1`
    #    id x1 x2 x3 x4
    # 1   1 NA NA NA NA
    # 2   2 NA NA NA NA
    # 3   3 NA NA NA NA
    # 4   4 NA NA NA NA
    # 5   5 NA NA NA NA
    # 6   6  6 16 26 36
    # 7   7 NA NA NA NA
    # 8   8 NA NA NA NA
    # 9   9 NA NA NA NA
    # 10 10 NA NA NA NA
    # $`Obj 2`
    #    id x1 x2 x3 x4
    # 1   1 NA NA NA NA
    # 2   2  2 12 22 32
    # 3   3  3 13 23 33
    # 4   4  4 14 24 34
    # 5   5 NA NA NA NA
    # 6   6  6 16 26 36
    # 7   7  7 17 27 37
    # 8   8  8 18 28 38
    # 9   9 NA NA NA NA
    # 10 10 10 20 30 40
    # $`Obj 3`
    #    id x1 x2 x3 x4
    # 1   1 NA NA NA NA
    # 2   2  2 12 22 32
    # 3   3  3 13 23 33
    # 4   4  4 14 24 34
    # 5   5 NA NA NA NA
    # 6   6 NA NA NA NA
    # 7   7 NA NA NA NA
    # 8   8  8 18 28 38
    # 9   9 NA NA NA NA
    # 10 10 NA NA NA NA
    # $`Obj 4`
    #    id x1 x2 x3 x4
    # 1   1 NA NA NA NA
    # 2   2 NA NA NA NA
    # 3   3 NA NA NA NA
    # 4   4 NA NA NA NA
    # 5   5 NA NA NA NA
    # 6   6  6 16 26 36
    # 7   7 NA NA NA NA
    # 8   8 NA NA NA NA
    # 9   9 NA NA NA NA
    # 10 10 NA NA NA NA
    # $`Obj 5`
    #    id x1 x2 x3 x4
    # 1   1  1 11 21 31
    # 2   2 NA NA NA NA
    # 3   3 NA NA NA NA
    # 4   4  4 14 24 34
    # 5   5  5 15 25 35
    # 6   6 NA NA NA NA
    # 7   7  7 17 27 37
    # 8   8 NA NA NA NA
    # 9   9  9 19 29 39
    # 10 10 10 20 30 40