在左连接后比较多对 x/y 列,如果不同,则在 R 中使用 y

Compare multiple pairs of x/y columns after left join and if different use y in R

我有一个data.framedf1。一些选定的行已经过手动审查和更新,创建了第二个 data.frame df1updated,它具有所有相同的列,其中一些数据已经更改,加上额外的列。

我想将更新后的版本加入到原来的数据中,数据有变化的地方,替换原来的,没有变化的地方,保留原来的,数据没有审核的地方(即不是在df1updated) 保留原来的

我在这个小例子中是这样做的,如下所示:

library(lubridate)
library(dplyr)
library(tidyr)
df1 =  data.frame(id = c(1,2,3,4,5),
                  date = dmy(c("15/01/2020", "03/12/2020", "20/08/2019" , "01/01/2021", "01/02/2021")),
                  type = c("type_A","type_A", "type_B", "type_C", "type_B"))


df1_update = data.frame(id = c(1,2,3),
                 date = dmy(c("25/01/2020", "03/12/2020", "20/08/2019")),
                 type = c("type_A","type_B", "type_B"),
                 new_info = c("note", "nil","note"))

df3 = left_join(df1, df1_update, by = "id")%>%
  mutate(date = case_when(is.na(date.y) ~ date.x, 
                          date.x == date.y ~ date.x,
                          date.x != date.y ~ date.y),
         type = case_when(is.na(type.y) ~type.x,
                          type.x == type.y ~ type.x,
                          type.x != type.y ~ type.y))%>%
  select(-contains(c(".x", ".y"))) 

df3

> df3
  id new_info       date   type
1  1     note 2020-01-25 type_A
2  2      nil 2020-12-03 type_B
3  3     note 2019-08-20 type_B
4  4     <NA> 2021-01-01 type_C
5  5     <NA> 2021-02-01 type_B

在我的真实数据集中,我有大约 16 列已经过审查和更新。是否可以比较所有以 .x 和 .y 结尾的列对,而不必像上面那样命名每一对?我猜这可能是通过编写一个函数来实现的。

以下是真实列名的示例,在 left_join:

之后

"Access_ID" "First_use_Date_After_Creation.x" "Last_use_Date"
[13]“StatusOnAccessDay.x”
[43]“Access_Type.y”“Access_Site.y”“StatusOnAccessDay.y”
[46]“Date_Construction.y”“Date_Of_First_Use.y”“First_use_Date_After_Creation.y”
[49]“Date_Of_failure.y”“Date_Of_removal.y”
[52] "Problem_item" "Problem_item.1"

coalesce可能会更简单(如果条件不多也可以用case_when)。另外,假设对应的.x列总是有.y列,循环across.x列,替换.x列名的子串(cur_column()) 使用 .yget 值,应用 case_when,更新 .name 中的列名称并使用 [=删除 unused 列=25=]

library(dplyr)
library(stringr)
left_join(df1, df1_update, by = "id") %>% 
   mutate(across(ends_with('.x'), 
   ~ {
      xdat <- as.character(.x)
      ydat <- as.character(get(str_replace(cur_column(), '\.x', '.y')))
      case_when(is.na(ydat) ~ xdat, 
              xdat == ydat ~ xdat,
               xdat != ydat ~ ydat)
     }, 
      .names = "{str_remove(.col, '.x')}"), .keep = 'unused') %>%
   type.convert(as.is = TRUE)

-输出

 id new_info       date   type
1  1     note 2020-01-25 type_A
2  2      nil 2020-12-03 type_B
3  3     note 2019-08-20 type_B
4  4     <NA> 2021-01-01 type_C
5  5     <NA> 2021-02-01 type_B

另一种函数方式:

library(dplyr)
library(purrr)

coalesce_from_base <- function(df, base) {
  
  .x <- paste0(base, ".x")
  .y <- paste0(base, ".y")
  
  df %>% 
    mutate(!!ensym(base) := case_when(is.na(.data[[.y]]) ~ .data[[.x]], 
                                      .data[[.x]] == .data[[.y]] ~ .data[[.x]],
                                      .data[[.x]] != .data[[.y]] ~ .data[[.y]])) 
  
}

# join together
df3 <- left_join(df1, df1_update, by = "id")

# create a vector a fields to iterate over
col_base <- c("date", "type")
# col_base <- stringr::str_subset(names(df3), ".x$") # create this by code

# use reduce to cumulative iterate over each base value
reduce(col_base, coalesce_from_base, .init = df3) %>%
  select(-ends_with(c(".x", ".y")))