通过不同的ID合并数据帧列表

时间:2017-07-21 12:19:54

标签: r merge

我有数据帧的可变长度列表。我想使用指定的列名称或索引将每个列表中的dfs合并为单个df,该列名称或索引因df而异。这是一个3 dfs的例子

my.list <- list(
data.frame(a = 1:10, b = letters[1:10], c = 101:110),
data.frame(d = 6:15, e = letters[1:10], f = 1:10),
data.frame(l = 2:11, m = letters[11:20], o = 1:10))

我希望通过ids

中提到的每个df的特定列进行合并
ids <- c('a', 'f', 'l')

获得类似

的内容
id  b   c   d   e   m   o
1   a   101 6   a   NA  NA
2   b   102 7   b   k   1
3   c   103 8   c   l   2
4   d   104 9   d   m   3
5   e   105 10  e   n   4
6   f   106 11  f   o   5
7   g   107 12  g   p   6
8   h   108 13  h   q   7
9   i   109 14  i   r   8
10  j   110 15  j   s   9
11  NA  NA  NA  NA  t   10

我尝试使用merge和/或Reduce执行此操作,但未能传递ID

5 个答案:

答案 0 :(得分:6)

我们可以通过更改与&#39; ids&#39;对应的列名来更改所有names元素的唯一list。与&#39; id&#39;然后使用Reduce

执行merge
lst <- Map(function(x, y) {names(x)[match(y, names(x))] <- 'id'; x}, my.list, ids)
Reduce(function(...) merge(..., by = 'id', all = TRUE), lst)
#   id    b   c  d    e    m  o
#1   1    a 101  6    a <NA> NA
#2   2    b 102  7    b    k  1
#3   3    c 103  8    c    l  2
#4   4    d 104  9    d    m  3
#5   5    e 105 10    e    n  4
#6   6    f 106 11    f    o  5
#7   7    g 107 12    g    p  6
#8   8    h 108 13    h    q  7
#9   9    i 109 14    i    r  8
#10 10    j 110 15    j    s  9
#11 11 <NA>  NA NA <NA>    t 10

答案 1 :(得分:6)

这是一个data.table答案,与@ akrun的回答类似。

但是,我们将其设置为键,而不是重命名列。然后我们可以通过键合并而不是按名称合并。这会保留列名。

library(data.table)

funky <- function(x) {
  setDT(my.list[[x]])
  setkeyv(my.list[[x]], ids[x])
  return(NULL)
}

所以这个函数将传递一个索引x。首先,它会将data.frame的{​​{1}}位置xth设置为my.list。然后,它将根据data.table中相同位置指定的列名称设置此新data.table的键。最后,由于这一切都已完成,因此请返回ids以防止无用的打印输出。

现在将该函数应用于列表中的所有对象。

NULL

解压缩a <- lapply(seq_along(ids), funky) Reduce(function(x, y) merge(x, y, by.x = key(x), by.y = key(y), all = TRUE), my.list) ,我们可以使用Reducekey(x)指定要合并的列。这是允许我们避免修改列名称的步骤。

key(y)

答案 2 :(得分:5)

一个想法可能是将感兴趣的列转换为rownames,然后合并到rownames,即

l1 <- Map(function(x, y) {rownames(x) <- x[[y]]; x}, my.list, ids)
Reduce(function(x, y)merge(x, y, all = TRUE), lapply(l1, function(x) 
                                                 data.frame(x, id = rownames(x))))

#   id  a    b   c  d    e  f  l    m  o
#1   1  1    a 101  6    a  1 NA <NA> NA
#2  10 10    j 110 15    j 10 10    s  9
#3   2  2    b 102  7    b  2  2    k  1
#4   3  3    c 103  8    c  3  3    l  2
#5   4  4    d 104  9    d  4  4    m  3
#6   5  5    e 105 10    e  5  5    n  4
#7   6  6    f 106 11    f  6  6    o  5
#8   7  7    g 107 12    g  7  7    p  6
#9   8  8    h 108 13    h  8  8    q  7
#10  9  9    i 109 14    i  9  9    r  8
#11 11 NA <NA>  NA NA <NA> NA 11    t 10

答案 3 :(得分:3)

@Frank做了一个评论,让我想到一个简单,直接的循环:

# initialise result
result <- my.list[[1L]]
# add/merge remaining data.frames from list using the given column in ids to merge on
for (i in tail(seq_along(my.list), -1L)) {
  result <- merge(result, my.list[[i]], by.x = ids[1L], by.y = ids[i], all = TRUE)
}
result
    a    b   c  d    e    m  o
1   1    a 101  6    a <NA> NA
2   2    b 102  7    b    k  1
3   3    c 103  8    c    l  2
4   4    d 104  9    d    m  3
5   5    e 105 10    e    n  4
6   6    f 106 11    f    o  5
7   7    g 107 12    g    p  6
8   8    h 108 13    h    q  7
9   9    i 109 14    i    r  8
10 10    j 110 15    j    s  9
11 11 <NA>  NA NA <NA>    t 10

在合并之前,此方法不需要重命名列表中的任何data.frames的单个列。但是,为了与OP的预期结果保持一致,之后可以重命名id列:

tmp <- colnames(result)
colnames(result) <- replace(tmp, tmp == ids[1L], "id")
result
   id    b   c  d    e    m  o
1   1    a 101  6    a <NA> NA
2   2    b 102  7    b    k  1
3   3    c 103  8    c    l  2
4   4    d 104  9    d    m  3
5   5    e 105 10    e    n  4
6   6    f 106 11    f    o  5
7   7    g 107 12    g    p  6
8   8    h 108 13    h    q  7
9   9    i 109 14    i    r  8
10 10    j 110 15    j    s  9
11 11 <NA>  NA NA <NA>    t 10

注意OP多次指出ids向量包含要为每个 data.frames合并的列的名称:

  

I want to merge by a specific column of each df mentioned in ids,和   Essentially I know the by variables (ids) but they differ among the dfs

因此,我担心使用match()的答案可能是错误的。

答案 4 :(得分:-2)

合并我可以建议你使用sqldf包中的sqldf命令,你可以这样做:

A = data.frame(a = 1:10, b = letters[1:10], c = 101:110)
B = data.frame(d = 6:15, e = letters[1:10], f = 1:10)
C = data.frame(l = 2:11, m = letters[11:20], o = 1:10)
joined_df <- sqldf('select A.*,B.*,C.* from A left join B on A.a=B.f left join C on A.a=C.l')