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

时间:2016-04-01 19:40:00

标签: r join merge data.table

摘要:我希望将共享id密钥的两个表合并为all=true(完全外部联接),而不是将具有相同名称的列设置为{ {1}} var1.x等,它们合并为一个列,其中左表中的缺失(NA)值由右表中的值填充(除了合并的标准行为,即,附加具有不同ID的行和具有不同名称的列)。

详细信息:

我想基于共享的var2.y键列将table1table2合并,以便:

1)如果idtable1具有相同名称的列(table2除外),id中的值将保留,如果它存在并替换为如果table1中的值为NA,则table2中的值。

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

3)如果table1的{​​{1}}与table1中的id不匹配,则table2中不同名称列的值为NA

4)如果table2的{​​{1}}与table2中的id不匹配,则会将其添加为新行,并将table1中的不同列名称的值添加为table1是NA。

3& 4与标准merge一样all=TRUE

我担心我已经推翻了这个问题,因为我找不到一个直接的方法来执行mergejoin并不涉及创建{{1}检查每一列。真实数据有大约1000列,因此对每个列进行ifelse查找都是非常长的解决方案。

可重复的简化示例:

ifelse

所需输出的说明:

  1. 对于列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 var1包含所有值,因此不会被忽略table1NA的{​​{1}}被忽略(请注意,这并不包括下面描述的不同ID的行合并)。

  2. 对于专栏id3table2缺少var2索引的值,因此会从table更新(请注意,这不是{\ n}}包括下面描述的不同ID的行合并。

  3. 对于列id3table2中没有匹配列,因此保持原样。

  4. 对于列var3table2中没有列var4,因此var4通过table1键变量合并。< / p>

  5. 对于table2id的行,id6中没有匹配的table1,因此列id6的值仅为table2 var4table2的行desired的{​​{1}}输出为NA。

  6. 对于id6id8的行,table2中没有匹配的id8,因此列table1中的值只有var3table1的{​​{1}}输出中的desired为NA。

  7. 肯定有一种直接的方法可以使用id8执行此操作吗?考虑到实际数据的大小,特别欢迎高效的解决方案。 data.table软件包显然用于完成此操作,但它不再适用于CRAN,我无法通过zip从R3.2.3开始工作。是否有另一个包加紧完成这项任务?还有许多其他线程专注于为一个或几个具有已知名称的列解决此问题,但对于大量列,它们看起来并不实用。

3 个答案:

答案 0 :(得分:10)

以这种方式:

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

创建和使用所有这些列名矢量非常麻烦。

关于原始问题......

以其他方式(不从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]

这样,所有步骤都可以通过引用修改table1

答案 1 :(得分: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))]

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

答案 2 :(得分:3)

编辑:快速调整

首先执行此操作以获取所有必需的行:

table1 <- table1[.(id = union(id, table2$id)), on = "id"]

我不喜欢mget,所以我提出以下建议:

in_common <- parse(text=setdiff(intersect(names(table1), names(table2)), "id"))

for (ii in in_common)
  table1[is.na(eval(ii)), 
         as.character(ii) :=
           table2[.SD, eval(ii), on = "id"]]

新栏目很简单:

new_cols <- setdiff(names(table2), names(table1))

for (jj in new_cols)
  table1[table2, (jj) := eval(parse(text = jj)), on = "id"]

我认为做这样的事情会更快:

in_common_c <- setdiff(intersect(names(table1), names(table2)), "id"))
in_common_q <- parse(text=in_common_c)

for (ii in seq_along(in_common_q))
  table1[is.na(eval(in_common_q[ii])), 
         in_common_c[ii] :=
           table2[.SD, eval(in_common_q[ii]), on = "id"]]

但我希望差异很小。