R:使用第二个表合并+更新表的有效方法,其中来自相同列名的值填充NA

Mek*_*lay 13 merge join r data.table

简介:我想通过共享id密钥将两个表合并为all=true(完全外部联接),而不是将具有相同名称的列设置为var1.x var2.y等,它们将合并为单个列,其中左表中缺少(NA)值由右表中的值填充(除了合并的标准行为,即附加具有不同ID的行和具有不同名称的列).

细节:

我想基于共享密钥列合并+更新table1,以便:table2id

1)如果table1并且table2具有相同名称(除了id)之外的列,则如果值table1存在,则保留为单独的值,table2如果值为table1NA ,则将值替换为值in .

2)如果table2具有table1没有(不同名称)的列,则它们被合并(通过id).

3)如果table1具有id不匹配的table2,则不同名称列的值为table2NA

4)如果table2有一个id不匹配的table1,则将其添加为新行,并且不同列名的值为table1NA.

3和4是与标准mergeall=TRUE.

我担心我已经推翻了这个问题,因为我无法找到一个简单的方法来执行此操作,merge或者join不涉及ifelse在每个列上创建检查.真实数据有大约1000列,因此ifelse对每个列进行查找都是非常长的解决方案.

可重复的简化示例:

table1  <- data.table(id  =c("id1", "id2", "id3", "id4", "id5", "id6"),
                      var1=c(1,2,3,4,5, 6),
                      var2=c("a", "b", NA, "d", NA, "f"),
                      var3=c(NA, 12, 13, 14, 15, 16));

table2  <- data.table(id  =c("id1", "id2", "id3", "id4", "id5", "id8"),
                      var1=c(1,2,NA,4,5, 8),
                      var2=c(NA, "b", "c", "d", "e", "h"),
                      var4=c("foo", "bar", "oof", "rab", NA, "sna"));

desired <- data.table(id=c("id1", "id2", "id3", "id4", "id5", "id6", "id8"),
                      var1=c(1,2,3,4,5, 6, 8),
                      var2=c("a", "b", "c", "d", "e", "f", "h"),
                      var3=c(NA, 12, 13, 14, 15, 16, NA),
                      var4=c("foo", "bar", "oof", "rab", NA, NA, "sna"));

table1;
    id var1 var2 var3
1: id1    1    a   NA
2: id2    2    b   12
3: id3    3   NA   13
4: id4    4    d   14
5: id5    5    e   15
6: id6    6    f   16

table2;
    id var1 var2 var4
1: id1    1    a  foo
2: id2    2    b  bar
3: id3   NA    c  oof
4: id4    4    d  rab
5: id5    5    e   NA
6: id8    8    h  sna

desired
    id var1 var2 var3 var4
1: id1    1    a   NA  foo
2: id2    2    b   12  bar
3: id3    3    c   13  oof
4: id4    4    d   14  rab
5: id5    5    e   15   NA
6: id6    6    f   16   NA
7: id8    8    h   NA  sna
Run Code Online (Sandbox Code Playgroud)

期望输出的说明:

  1. 对于列var1,table1具有所有值,因此它被单独保留并且NAfor id3in table2被忽略(请注意,这不包括下面描述的不同ID的行合并).

  2. 对于列var2,table缺少索引的值id3,因此更新自table2(注意,这不包括下面描述的不同ID的行合并).

  3. 对于列var3,没有匹配的列table2,因此保持原样.

  4. 对于列var4,没有列var4table1,所以它是从合并table2id关键变量.

  5. 对于带有id6in的行,table1没有匹配id6in table2,因此var4仅在输出中的列的table2值为NA .desiredid6

  6. 对于具有id8in的行,table2没有匹配id8in table1,因此var3仅在列table1desired输出中的列的值为NA id8.

当然有一种直接的方法来做到这一点data.table?考虑到实际数据的大小,特别欢迎高效的解决方案.该datamerge程序包显然已经习惯了这一点,但它不再在CRAN上了,我无法让它在zip上运行R3.2.3.是否有另一个包加紧完成这项任务?还有许多其他线程专注于为具有已知名称的一个或几个列解决此问题,但对于大量列,它们似乎不实用.

Fra*_*ank 11

这是一种方式:

com.cols    = setdiff(intersect(names(table1), names(table2)), "id")
com.cols.x  = paste0(com.cols, ".x")
com.cols.y  = paste0(com.cols, ".y")

# create combined table
DT = setkey(merge(table1, table2, by="id", all=TRUE), NULL)

# edit common columns where NAs are present
for (j in seq_along(com.cols)) 
  DT[is.na(get(com.cols.x[j])), (com.cols.x[j]) := get(com.cols.y[j])]

# remove unneeded columns
DT[, (com.cols.y) := NULL]

# rename kept columns
setnames(DT, com.cols.x, com.cols)

identical(DT, desired) # TRUE
Run Code Online (Sandbox Code Playgroud)

创建和使用所有这些列名矢量相当混乱.


关于原来的问题......

这是另一种方式(没有从table2原始帖子中导入新行):

com.cols    = setdiff(intersect(names(table1), names(table2)), "id")
i.com.cols  = paste0("i.", com.cols)
new.cols    = c(i.com.cols, setdiff(names(table2), c("id", com.cols)))

# grab columns from table2
table1[table2, (new.cols) := mget(new.cols), on="id"]

# edit common columns where NAs are present
for (j in seq_along(com.cols)) 
  table1[is.na(get(com.cols[j])), (com.cols[j]) := get(i.com.cols[j])]

# remove unneeded columns
table1[, (i.com.cols) := NULL]
Run Code Online (Sandbox Code Playgroud)

这样,所有步骤都是table1通过引用的修改.

  • @JoshO'Brien谢谢.这样,我就不必创建新表了; 我只是将列指针添加到现有表中. (4认同)
  • 非常好.有没有理由你做`table1 [table2,(new.cols):= mget(new.cols),on ="id"]`而不是`table1 [table2,on ="id"]`? (2认同)

edd*_*ddi 5

这是另一个避免将i.列显式添加到原始表的选项:

com.cols    = setdiff(intersect(names(table1), names(table2)), "id")
i.com.cols  = paste0("i.", com.cols)
# I'm using the same var names as Frank, but new.cols is strictly the new ones here
new.cols    = setdiff(names(table2), names(table1))

# this is easy - the previously absent cols
table1[table2, (new.cols) := mget(new.cols), on = 'id']

# now for the ones that need updating
table1[table2, on = 'id',
       (com.cols) := Map(function(col, i.col) pmin(col, i.col, na.rm = T),
                         mget(com.cols), mget(i.com.cols))]
Run Code Online (Sandbox Code Playgroud)

我不知道哪个选项更快 - OP可以检查.