R语言dplyr如何无需虚拟ID筛选ID-Date组合的新增记录
问题
需要从new_df中筛选出ID和Date组合未在my_df中出现过的新增记录,不希望通过拼接字段生成虚拟ID的方式实现。
示例原始数据如下:
- 已有数据框
my_df存储ID/Date对应的Value值:
set.seed(42) library(lubridate) library(dplyr) my_df <- data.frame(ID = c('A', 'B', 'C', 'A', 'B'), Date = seq(date('2022-01-01'), date('2022-01-05'), by = 1), Value = rnorm(5))
> my_df ID Date Value 1 A 2022-01-01 1.3709584 2 B 2022-01-02 -0.5646982 3 C 2022-01-03 0.3631284 4 A 2022-01-04 0.6328626 5 B 2022-01-05 0.4042683
- 待过滤数据框
new_df同时包含已存在和新增的ID/Date组合:
new_df <- data.frame(ID = c('A', 'B', 'C', 'A', 'B'), Date = seq(date('2022-01-01'), date('2022-01-05'), by = 1)) |> bind_rows(data.frame(ID = c('A', 'B', 'D', 'D'), Date = c(date('2022-01-02'), date('2022-01-01'), date('2022-01-01'), date('2022-01-07'))))
> new_df ID Date 1 A 2022-01-01 2 B 2022-01-02 3 C 2022-01-03 4 A 2022-01-04 5 B 2022-01-05 6 A 2022-01-02 7 B 2022-01-01 8 D 2022-01-01 9 D 2022-01-07
最优实现
直接使用dplyr内置的anti_join()即可,这是专门为「保留左表中无法在右表匹配到的行」场景设计的函数,只需要在by参数中指定用来匹配的字段,不需要生成任何中间列,语义清晰可读性强:
result <- new_df |> anti_join(my_df, by = c("ID", "Date"))
运行结果和虚拟ID方案完全一致,无多余字段:
> result ID Date 1 A 2022-01-02 2 B 2022-01-01 3 D 2022-01-01 4 D 2022-01-07
其他可选方案
- 如果不想使用join类函数,可以直接在
filter中临时做匹配,不需要把虚拟ID存为列:
new_df |> filter(!paste(ID, Date) %in% paste(my_df$ID, my_df$Date))
注意:这种写法存在极端场景下字符串拼接碰撞的风险(比如不同ID、Date值拼接后得到相同字符串),稳定性不如anti_join。
- 如果要使用base R实现,可以用
interaction()生成多列组合的唯一标识,避免拼接碰撞问题:
new_df[!interaction(new_df[c("ID", "Date")]) %in% interaction(my_df[c("ID", "Date")]), ]
内容的提问来源于stack exchange,提问作者KidLu
相关产品推荐
相关产品推荐

