【问题标题】:Join and overwrite data in one table with data from another table用另一个表中的数据连接和覆盖一个表中的数据
【发布时间】:2018-03-27 10:17:19
【问题描述】:

如何加入和覆盖数据似乎是一个常见的请求,但我还没有找到适用于整个数据集的优雅解决方案。

(注意:为了简化数据,我将仅使用 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 ]

我想基本上用 d2 中的所有数据加入 + 覆盖 d1,当然要确保按 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

仅供参考,以下是解决此问题的其他一些帖子,但仅针对一两列。我正在寻找的解决方案应该允许一个表中的数据被另一个表中的许多(如果不是全部)列覆盖。

Merge data frames and overwrite values

Merge two data frame and replace the NA value in R

基于 data.table 的解决方案将是首选,但也欢迎使用其他解决方案。

【问题讨论】:

    标签: r data.table overwrite


    【解决方案1】:

    我认为使用长格式最容易:

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

    然后你可以把它们堆叠起来,取最新的值:

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

    如果您只想更新 [d3] 中的 NA 值,我还想知道如何做到这一点,即确保不覆盖现有的非 NA 值。

    如果行出现在md3 中,您可以从更新表md2 中排除它们:

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

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

    【讨论】:

    • 有趣的方法,而且效果很好。然而,熔化将所有数据强制转换为一种类型,因此如果您同时拥有字符数据和整数数据,则会产生问题(我承认我的示例过于简单)。我会在每个步骤的末尾添加 decast,因为这就是最终数据的样子(即宽格式是原始格式)。
    【解决方案2】:

    这是来自 cmets 的 @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 出现多次或以不同的顺序出现,它会做错事。

    d1[d1$id %in% d2$id, 名称(d2):=d2]

    【讨论】:

    • 一般来说这是个坏主意。如果 id 出现多次或以不同的顺序出现,它会做错事。相反,我认为加入应该可以工作:cols = setdiff(intersect(names(d1), names(d2)), "id"); d1[d2, on=.(id), (cols) := mget(paste0("i.", cols))]。我将其作为评论发布,因为它与您的答案基本相同,您可以根据需要对其进行编辑。
    • 弗兰克,我欢迎您(或其他任何人)也对其他场景进行破解,您想要覆盖数据,除非存在可能取代非 NA 值的 NA。我链接的帖子中有一些 data.table 解决方案,我希望它们可以应用于多个列(即所有正在更新的列)。
    • 我不确定我是否理解您描述的场景。我猜,不覆盖 NA 的粗略方法是使用 replaceifelse。您可以发布一个新问题。
    • 对不起,我不够清楚。在现在编辑的 OP 中,我描述了第二种情况,您不想覆盖现有的非 NA 数据。我觉得这篇文章是解决这两种情况的好地方,一种是所有数据都应该被覆盖,另一种是只应该覆盖 NA 数据。
    【解决方案3】:
    library("dplyr")
    
    d12 <- anti_join(d1, d2, by = "id") %>%
             bind_rows(d2)
    

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

    这不适用于“附加场景”,它看起来要解决起来要麻烦得多,也许应该是一个单独的问题。

    【讨论】:

    • by = "id"
    • 这不会因为行绑定导致数据不完整。
    猜你喜欢
    • 2012-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-02
    • 1970-01-01
    • 2020-09-28
    • 1970-01-01
    相关资源
    最近更新 更多