【问题标题】:Finding elements from multiple columns of one dataframe that are not in multiple columns of another从一个数据框的多列中查找不在另一个数据框的多列中的元素
【发布时间】:2018-04-19 09:16:17
【问题描述】:
library(tidyverse) 

我有两个名为 Df1 和 Df2 的数据帧(参见底部的示例代码)。我想在 Df1 中(从所有列中)查找不在 Df2 中的任何电话号码列中的电话号码。

首先,我重组 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 和 Phone 3)中所有不在 Df2(Ph1 和 Ph2)中的号码。下面是一些关于代码的想法。我尝试了这个想法的许多变体,但找不到实现我想要的方法。最终产品应该只是一个表格,其中包含不在任何 Df2 列中的任何 Df1 列中的电话号码以及关联的 Id。我还想知道是否还有其他联接或设置操作可以更有效地实现这一目标?

 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)

【问题讨论】:

  • 在基础 R 中,您可以使用类似 setdiff(unlist(Df1[("Ph1", "Ph2")]), unlist(Df2[c("Phone1", "Phone2")])) 的方式来获取 Df1 中不在 Df1 中的所有电话号码。 setdiff 删除 NA。

标签: r tidyverse


【解决方案1】:

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

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

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

tidyr_dplyr cheatsheet

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

【讨论】:

  • 这将在Df1 中找到不在Df2 中的所有Ids。我认为 OP 想要比较电话号码,而不仅仅是 Ids。
【解决方案2】:

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

  1. df1 中的每个 ID 号都有一组电话号码。
  2. df2 中的每个 ID 号都有一组电话号码。
  3. 您希望在每个 ID 中找到位于 df1df2 之间的 set difference

您可以通过将基本 R 函数 setdiff() 映射到您加入的数据框来做到这一点。为此,您需要将数据框转换为列表列格式,其中每个 ID 的所有电话号码都作为列表显示在数据框的“单元格”中。这很容易通过组合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

【讨论】:

  • 这是一个很好的解决方案。在我的真实数据上进行了测试,它确实有效。很高兴这允许任意数量的电话号码列。
猜你喜欢
  • 1970-01-01
  • 2022-08-10
  • 2021-09-09
  • 2020-09-28
  • 1970-01-01
  • 2018-01-10
  • 2022-07-18
  • 2018-03-15
  • 2023-02-13
相关资源
最近更新 更多