从一个数据框的多列中查找不在另一个数据框的多列中的元素

Finding elements from multiple columns of one dataframe that are not in multiple columns of another

library(tidyverse) 

我有两个名为 Df1 和 Df2 的数据帧(请参阅底部的示例代码)。我想在 Df1(来自所有列)中找到不在 Df2 中任何 phone 数字列中的 phone 数字。

首先,我重构了 Df1,使每行只有一个 Id。

Df1<-Df1 %>% 
gather(key, value, -Id) %>%
filter(!is.na(value)) %>% 
select(-key) %>% 
group_by(Id) %>% 
filter(!duplicated(value)) %>% 
mutate(Phone=paste0("Phone_",1:n())) %>% 
spread(Phone, value)

接下来,我重命名 Df2,然后使用联接仅查找 Df1 中存在于 Df2 中的 ID。

Df2<-Df2%>%set_names(c("Id","Ph1","Ph2"))
DfJoin<-left_join(Df2,Df1,by="Id")

这就是我卡住的地方。我想找到 Df1(Phone1 Phone2 和 Phone3)中所有不在 Df2(Ph1 和 Ph2)中的数字。以下是代码的一些想法。我尝试了这个想法的许多变体,但找不到实现我想要的方法。最终产品应该只是 table,任何 Df1 列中的 phone 数字以及相关 ID 不在任何 Df2 列中。我还想知道是否还有其他连接或设置操作可以更有效地实现此目的?

 DfJoin<-DfJoin%>%mutate(New=if_else(! DfJoin[2:3] %in% DfJoin[4:6]),1,0)

 DfJoin<-DfJoin%>%filter(! DfJoin[2:3] %in% DfJoin[2:4])

示例数据:

数据框 1:

Id<-c(199,148,148,145,177,165,144,121,188,188,188,111)
Ph1<-c(6532881717,6572231223,6541132112,6457886543,6548887777,7372222222,6451123425,6783450101,7890986543,6785554444,8764443344,6453348736)
Ph2<-c(NA,NA,NA,NA,NA,7372222222,NA,NA,NA,6785554444,NA,NA)

Df1<-data.frame(Id,Ph1,Ph2)

数据框 2:

Id2<-c(199,148,142,145,177,165,144,121,182,109,188,111)
Phone1<-c(6532881717,6572231223,6541132112,6457886543,6548887777,7372222222,6451123425,6783450101,7890986543,6785554400,8764443344,6453348736)
 Phone2<-c(NA,NA,NA,NA,NA,7372222222,NA,NA,NA,6785554444,NA,NA)

 Df2<-data.frame(Id2,Phone1,Phone2)

你试过了吗anti_join(a, b, by = "x1")

这基本上为您提供了 a 中不在 b 中的所有行

DfJoin <- anti_join(Df1, Df2, by = "Id")

tidyr_dplyr cheatsheet

使用上面的备忘单在 tidyverse 中进行数据操作

思考这个问题的一种方式:

  1. 对于每个身份证号码,您在 df1 中有一组 phone 个号码。
  2. 每个身份证号码在 df2 中有一组 phone 个号码。
  3. 您想在每个 ID 中找到 df1df2 之间的 set difference

您可以通过将基本 R 函数 setdiff() 映射到连接的数据框来实现。为此,您需要将数据框转换为列表列格式,其中每个 ID 的所有 phone 数字都作为列表出现在数据框的 "cell" 中。这很容易通过组合 group_by()summarize()list().

来完成
# create example data
Id <- c(199,148,148,145,177,165,144,121,188,188,188,111)
ph1 <- c(6532881717,6572231223,6541132112,6457886543,6548887777,7372222222,6451123425,6783450101,7890986543,6785554444,8764443344,6453348736)
ph2 <- c(NA,NA,NA,NA,NA,7372222222,NA,NA,NA,6785554444,NA,NA)

df1 <- data.frame(Id, ph1, ph2)

Id2 <- c(199,148,142,145,177,165,144,121,182,109,188,111)
phone1 <- c(6532881717,6572231223,6541132112,6457886543,6548887777,7372222222,6451123425,6783450101,7890986543,6785554400,8764443344,6453348736)
phone2 <- c(NA,NA,NA,NA,NA,7372222222,NA,NA,NA,6785554444,NA,NA)

df2 <- data.frame(Id=Id2, phone1, phone2)


# convert the data to list-column format
df1.listcol <- df1 %>%
    gather(col, phone, -Id) %>%
    na.omit() %>%
    group_by(Id) %>%
    summarize(phone_list1 = list(phone)) 

df2.listcol <- df2 %>%
    gather(col, phone, -Id) %>%
    na.omit() %>%
    group_by(Id) %>%
    summarize(phone_list2 = list(phone)) 

看看这些数据框,确保您了解我们是如何重新格式化它们的。显然,我们可以通过将这个转换过程变成一个函数,然后分别在df1df2上调用该函数来节省几行代码,但我这里没有这样做。

# join the two listcol dfs by Id, then map setdiff on the two columns
result <- 
    df1.listcol %>%
    left_join(df2.listcol, by='Id') %>%
    mutate(only_list_1 = map2(phone_list1, phone_list2, ~setdiff(.x, .y))) %>%
    select(Id, only_list_1) %>%
    unnest()

result

result

Id  only_list_1
148 6541132112
188 7890986543
188 6785554444