加入并覆盖一个表中的数据,使用另一个表中的数据。

3
如何合并和覆盖数据似乎是一个常见的需求,但我还没有找到适用于整个数据集的优雅解决方案。
(注意:为了简化数据,我只使用值为1和NA以及少量列,但实际上我有数百列具有不同的值)。
我有一个数据表(d1),其中某些列和行中有NA值。
library(data.table)
d1 = fread(
"r id v1 v2 v3
1  A  1  1  1
2  B  1  1  1
3  C  1 NA NA
4  D  1  1 NA
5  E  1 NA  1")[, r := NULL]

我有另一个数据表(d2),它包含额外的列以及在d1中现有列中缺失的数据点。

d2 = fread(
"r id v2 v3 v4 v5
1  C  1  1  1  1
2  D  1  1  1  1
3  E  1  1  1  1")[, r := NULL ]

我希望把d1与d2的所有数据进行基本连接和覆盖,当然需要通过id匹配行和通过名称匹配列,如下所示。
> d12
  id v1 v2 v3 v4 v5
1  A  1  1  1 NA NA
2  B  1  1  1 NA NA
3  C  1  1  1  1  1
4  D  1  1  1  1  1
5  E  1  1  1  1  1

附加场景:如果你只想更新d1中的NA值,即确保现有的非NA值不被覆盖,我也想知道如何完成这个任务。(为了更容易地可视化,我包含了新表格,其中包含1和0)。

例如,如果我们有d3:

d3 = fread(
"r id v1 v2 v3
1  A  1  1  1
2  B  1  1  1
3  C  1  0 NA
4  D  1  1  0
5  E  1 NA  1")[, r := NULL ]

我们希望将d2加入并仅覆盖NA值,以获得:

> d32
  id v1 v2 v3 v4 v5
1  A  1  1  1 NA NA
2  B  1  1  1 NA NA
3  C  1  0  1  1  1
4  D  1  1  0  1  1
5  E  1  1  1  1  1

以下是一些解决此问题的帖子,但只适用于一个或两个列。我正在寻找的解决方案应允许另一个表中的许多甚至所有列覆盖一个表中的数据。

合并数据框并覆盖值

合并两个数据框并在R中替换NA值

基于data.table的解决方案将更受欢迎,但其他方案也可以。

3个回答

3

我认为最简单的方法是采用长格式:

md1 = melt(d2, id="id")
md2 = melt(d2, id="id")

然后您可以将它们堆叠并获取最新值:

res1 = unique(rbind(md1, md2), by=c("id", "variable"), fromLast=TRUE)

我还想知道如果您只想更新 [d3] 中的 NA 值,即确保现有的非 NA 值不被覆盖,该怎么做。
您可以从更新表 md2 中排除行,如果它们出现在 md3 中。
md3 = melt(d3, id="id")

res3 = unique(rbind(md3, md2[!md3, on=.(id, variable)]), 
  by=c("id", "variable"), fromLast=TRUE)   

dcast 可以用于必要时返回宽格式,例如 dcast(res3, id ~ ...)


1
有趣的方法,而且它运行良好。然而,融合会强制所有数据转换为一种类型,因此如果您同时拥有字符和整数数据,则会创建问题(我承认我的示例过于简化)。我建议在每个步骤的末尾添加解除融合,因为这是最终数据应该看起来的样子(即宽格式是原始格式)。 - AlexR

2

以下是来自评论区@Frank的解决方案。(注意:d1和d2需要先定义为data.table)。

library(data.table)
cols = setdiff(intersect(names(d1), names(d2)), "id") 
d1[d2, on=.(id), (cols) := mget(paste0("i.", cols))]

如他所指出的那样,通常来说,我下面提供的原始解决方案是一个糟糕的想法。如果ID出现多次或顺序不同,它将执行错误的操作。

2
一般来说,这是一个不好的主意。如果id出现多次或顺序不同,它将执行错误的操作。相反,应该使用连接操作,我认为:cols = setdiff(intersect(names(d1), names(d2)), "id"); d1[d2, on=.(id), (cols) := mget(paste0("i.", cols))]。由于本质上与您的答案相同,所以我将其作为评论发布,您可以根据需要进行编辑。 - Frank
Frank,我欢迎您(或任何其他人)尝试另一个场景,即您想覆盖数据的情况,除非有可能超越非NA值的NA。我在链接的帖子中提供了一些data.table解决方案,希望可以应用于多个列(即所有要更新的列)。 - AlexR
我不确定我理解你所描述的情况。粗略地不覆盖NA的方法可能会使用replaceifelse,我猜测。你可以发布一个新问题。 - Frank
抱歉,我表述不够清晰。在修订后的 OP 中,我描述了第二种情况,即您不希望覆盖现有的非 NA 数据。我认为这篇文章是解决两种情况的好地方,一种是需要覆盖所有数据的情况,另一种是仅需要覆盖 NA 数据的情况。 - AlexR

0
library("dplyr")

d12 <- anti_join(d1, d2, by = "id") %>%
         bind_rows(d2)

此解决方案从d1中获取不在d2中的行,然后将d2的行添加到它们上面。

对于“附加场景”,这种方法可能无法奏效,因为其解决起来更加混乱,或许应该单独提出一个问题。


1
它是 by = "id" 吗? - kangaroo_cliff
1
这样不行,因为行绑定会导致数据不完整。 - AlexR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接