在左连接后比较多对 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()
) 使用 .y
、get
值,应用 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")))
我有一个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()
) 使用 .y
、get
值,应用 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")))