左连接两个数据框并覆盖。

16
我想将两个数据框合并,其中df2覆盖任何在df1中的值为NA或已存在的值。 Merge data frames and overwrite values 提供了一个data.table选项,但我想知道是否有一种使用dplyr的方法来实现这一点。 我尝试了所有的_join选项,但似乎没有一种可以实现这一点。 是否有一种使用dplyr的方法来实现这一点?
以下是示例:
df1 <- data.frame(y = c("A", "B", "C", "D"), x1 = c(1,2,NA, 4)) 
df2 <- data.frame(y = c("A", "B", "C"), x1 = c(5, 6, 7))

期望的输出:

  y x1
1 A  5
2 B  6
3 C  7
4 D  4

3
left_join(df1, df2, by="y") %>% transmute(y, x1 = ifelse(is.na(x1.y), x1.x, x1.y))?也许这可以添加到其他答案中,然后关闭此问题作为重复?(另一个问题不是针对data.table的特定问题) - talat
我认为之前的评论可能会从最终输出中删除任何不是y或x1的列,这很可能是不希望的。所以这里有一个小改变,它只更新x1的值,并保持df1其他部分不变: left_join(df1, df2, by="y") %>% mutate(x1 = ifelse(is.na(x1.y), x1.x, x1.y)) %>% select(!c(x1.x, x1.y)) - Max Yari
2个回答

12

我认为你想要的是保留df2的值,并且仅添加在df1中存在而df2中不存在的值,这正是anti_join所做的:

"anti_join返回所有在x中与y中无匹配值的行,仅保留x中的列。"

我的解决方案:

df3 <- anti_join(df1, df2, by = "y") %>% bind_rows(df2)

Warning messages:
1: In anti_join_impl(x, y, by$x, by$y) :
  joining factors with different levels, coercing to character vector
2: In rbind_all(x, .id) : Unequal factor levels: coercing to character

> df3
Source: local data frame [4 x 2]

      y    x1
  (chr) (dbl)
1     D     4
2     A     5
3     B     6
4     C     7

这一行会输出你所期望的结果(顺序可能不同),但是请注意警告信息:当处理数据集时一定要将y作为字符变量读入。


2
谢谢!终于找到了答案!我的背景不是计算机科学,所以我很难找到这个答案。我应该寻找什么?R merge dataframes with priority 或者 R complete dataframe from other dataframe if values not present 或者 R merge without overwrite 或者 R add rows if not present 等等都没有成功... - Lionel Trebuchon

2
这是我现在使用的习语,因为它还可以处理保留不属于更新表的列。我使用了一些与原帖不同的名称,但风格类似。
我做的一件事是为连接中使用的键创建一个变量,因为我在几个地方使用它。但除此之外,它实现了所需的功能。
本身它不能处理例如“如果值为NA,则更新此行”的操作,但您应该在创建连接表时考虑该条件。
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

.keys <- c("key1", "key2")

.base_table <- tribble(
    ~key1, ~key2, ~val1, ~val2,
    "A", "a", 0, 0,
    "A", "b", 0, 1,
    "B", "a", 1, 0,
    "B", "b", 1, 1)

.join_table <- tribble(
    ~key1, ~key2, ~val2,
    "A", "b", 100,
    "B", "a", 111)

# This works
df_result <- .base_table %>%
    # Pull off rows from base table that match the join table
    semi_join(.join_table, .keys) %>%
    # Drop cols from base table that are in join table, except for the key columns
    select(-matches(setdiff(names(.join_table), .keys))) %>%
    # Left join on the join table columns
    left_join(.join_table, .keys) %>%
    # Remove the matching rows from the base table, and bind on the newly joined result from above.
    bind_rows(.base_table %>% anti_join(.join_table, .keys))

df_result %>%
    print()
#> # A tibble: 4 x 4
#>   key1  key2   val1  val2
#>   <chr> <chr> <dbl> <dbl>
#> 1 A     b         0   100
#> 2 B     a         1   111
#> 3 A     a         0     0
#> 4 B     b         1     1

reprex package (v0.3.0) 于2019年12月12日创建


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