【问题标题】:Joining two tables by cross checking two by two columns in R tidyverse通过在 R tidyverse 中交叉检查两列两列来连接两个表
【发布时间】:2019-06-25 08:57:44
【问题描述】:

问题:

如何通过使用 R tidyverse 和 dplyr 交叉检查 2x2 列来有效地连接两个表?我对 R 比较陌生,但我在之前的任何问题或讨论中都找不到这个问题。

我有两张表,它们的行数和列数不同。每个表都包含 A 列和 B 列。这些列包含可以相同或唯一的字符串,它们也可能重叠或从一列或另一列中丢失。基本上,我需要对照 A2 和 B2 检查 A1 列,然后对照 A2 和 B2 检查 B1。

概念解释示例:

df1
ID          pISSN       eISSN       Level
437097                  1530-9932   1
489309      2366-004X   2366-0058   1
437103      0025-5858               1
437109      1042-9670   1545-7230   1
449363      1093-1139               0
437127                  0949-1775   1
437124      0361-3682   1873-6289   2
481203      0103-846X   0103-846X   1
479825      2153-2184   2153-2192   0
437136      0734-2071   1557-7333   2


df2
ID          pISSN       eISSN       Format
41120                   2364-9534   E OA S C
12249                   1530-9932   E OF S
261                     2366-0058   E OF S
12188       0025-5858   1865-8784   PE OF S
40596       1042-9670   1545-7230   PE OF S
12129       0895-4852   1936-4709   PE OF
769         0949-1775   1432-0517   PE OF S


result
ID          pISSN       eISSN       Level   Format
437097                  1530-9932   1       E OF S
489309      2366-004X   2366-0058   1       E OF S
437103      0025-5858   1865-8784   1       PE OF S
437109      1042-9670   1545-7230   1       PE OF S
437127                  0949-1775   1       PE OF S

输入示例表:

dput(df1, file = "")
structure(list(ID = c(437097, 489309, 437103, 437109, 449363, 437127, 437124, 481203, 479825, 437136), pISSN = c(NA, "2366-004X", "0025-5858", "1042-9670", "1093-1139", NA, "0361-3682", "0103-846X", "2153-2184", "0734-2071"), eISSN = c("1530-9932", "2366-0058", NA, "1545-7230", NA, "0949-1775", "1873-6289", "0103-846X", "2153-2192", "1557-7333"), Level = c(1, 1, 1, 1, 0, 1, 2, 1, 0, 2)), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"))

dput(df2, file = "")
structure(list(ID = c(41120, 12249, 261, 12188, 40596, 12129, 769), pISSN = c(NA, NA, NA, "0025-5858", "1042-9670", "0895-4852", "0949-1775"), eISSN = c("2364-9534", "1530-9932", "2366-0058", "1865-8784", "1545-7230", "1936-4709", "1432-0517"), Format = c("E OA S C", "E OF S", "E OF S", "PE OF S", "PE OF S", "PE OF", "PE OF S")), row.names = c(NA, -7L), class = c("tbl_df", "tbl", "data.frame"))

【问题讨论】:

  • 有趣。你能提供一个可重现的例子吗? stackoverflow.com/questions/5963269/…
  • 您好,感谢您的回复。我对 R、编码和 Stack Overflow 还是很陌生。所以感谢信息链接,我尝试使用 dput 设置示例。我已将其添加到帖子中。希望这行得通。

标签: r join merge dplyr tidyverse


【解决方案1】:

我想我现在明白你想要达到的目标了。

代码

# Step 1
library(magrittr)
suppressMessages(library(dplyr))
library(fuzzyjoin)

# Step 2
df1 <- structure(list(ID = c(437097, 489309, 437103, 437109, 449363, 437127, 437124, 481203, 479825, 437136), pISSN = c(NA, "2366-004X", "0025-5858", "1042-9670", "1093-1139", NA, "0361-3682", "0103-846X", "2153-2184", "0734-2071"), eISSN = c("1530-9932", "2366-0058", NA, "1545-7230", NA, "0949-1775", "1873-6289", "0103-846X", "2153-2192", "1557-7333"), Level = c(1, 1, 1, 1, 0, 1, 2, 1, 0, 2)), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"))
df2 <- structure(list(ID = c(41120, 12249, 261, 12188, 40596, 12129, 769), pISSN = c(NA, NA, NA, "0025-5858", "1042-9670", "0895-4852", "0949-1775"), eISSN = c("2364-9534", "1530-9932", "2366-0058", "1865-8784", "1545-7230", "1936-4709", "1432-0517"), Format = c("E OA S C", "E OF S", "E OF S", "PE OF S", "PE OF S", "PE OF", "PE OF S")), row.names = c(NA, -7L), class = c("tbl_df", "tbl", "data.frame"))

# Step 3  
my_match <- function(key1, key2) {
  match <- key1 == key2
  match[is.na(match)] <- FALSE
  return(match)
}

# Step 4 
bind_rows(
fuzzy_inner_join(df1, df2, 
                 by = c("pISSN" = "pISSN"), 
                 match_fun = list(my_match)),
fuzzy_inner_join(df1, df2, 
                 by = c("eISSN" = "pISSN"), 
                 match_fun = list(my_match)),
fuzzy_inner_join(df1, df2, 
                 by = c("pISSN" = "eISSN"), 
                 match_fun = list(my_match)),
fuzzy_inner_join(df1, df2, 
                 by = c("eISSN" = "eISSN"), 
                 match_fun = list(my_match))
) %>% # Step 5
  mutate(pISSN = coalesce(pISSN.x, pISSN.y),
         eISSN = coalesce(eISSN.x, eISSN.y)) %>%
  select(-c("pISSN.x", "pISSN.y", "eISSN.x", "eISSN.y")) %>%
  select("ID.x", "ID.y", "pISSN", "eISSN", "Level", "Format") -> result

result
#> # A tibble: 6 x 6
#>     ID.x  ID.y pISSN     eISSN     Level Format 
#>    <dbl> <dbl> <chr>     <chr>     <dbl> <chr>  
#> 1 437103 12188 0025-5858 1865-8784     1 PE OF S
#> 2 437109 40596 1042-9670 1545-7230     1 PE OF S
#> 3 437127   769 0949-1775 0949-1775     1 PE OF S
#> 4 437097 12249 <NA>      1530-9932     1 E OF S 
#> 5 489309   261 2366-004X 2366-0058     1 E OF S 
#> 6 437109 40596 1042-9670 1545-7230     1 PE OF S

各步骤说明

  1. 我加载了一些需要的包,即:magrittr,用于管道运算符%&gt;%dplyr,用于bind_rows(类似于rbind)、mutateselect;还有,fuzzyjoin 代表 fuzzy_inner_join
  2. 然后我创建您的两个示例数据框:df1df2
  3. 我们定义函数my_match。此函数基于相等性进行匹配,但当涉及NAs 时,它返回FALSE(不匹配)而不是NA
  4. 然后我们使用fuzzy_inner_join通过以下键进行四次连接:(i)df1"pISSN"df2"pISSN"; (ii)df1"eISSN"df2"pISSN"; (iii)df1"pISSN"df2"eISSN"; (iv)df1"eISSN"df2"eISSN"。这是我们执行您所说的交叉检查 2x2 列的部分。然后,我们将这四个结果数据框包装在 bind_rows 中,以将所有这些观察结果(行)放在一个数据框中。
  5. 最后,我们进行一些数据整理以使数据框成为您想要的形状:(i) 我们使用 mutate 从列 pISSN.x 中创建两个新列 pISSNeISSN(最初来自 @987654359 @) 和pISSN.y(最初来自df2),以及来自eISSN.x(来自df1)和eISSN.y(来自df2); (ii) 我们使用select 保留/丢弃这些列。

注意:与您的预期 result 不同,我输出了两个 ID 列,一个来自 df1,另一个来自 df2。在您的帖子中,您只保留了数据框df1 中的ID。但是要保留哪一个是模棱两可的,所以我保留了两个。您可以随时使用select(-ID.x)select(-ID.y) 丢弃其中一个。

【讨论】:

  • 感谢您提供此解决方案,并感谢您对每个步骤的解释。它似乎运作良好,因为它捕获了所有可能的组合。然而,我确实得到了重复。我正在调查发生了什么,但它们很容易管理。
【解决方案2】:

我对您的示例代码以及您与 dput 共享的代码有些困惑,因为我不确定它们之间的关系……但这是我对您的问题的看法:

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(stringr)

df1 <- structure(list(ID = c(437097, 489309, 437103, 437109, 449363, 437127, 437124, 481203, 479825, 437136), pISSN = c(NA, "2366-004X", "0025-5858", "1042-9670", "1093-1139", NA, "0361-3682", "0103-846X", "2153-2184", "0734-2071"), eISSN = c("1530-9932", "2366-0058", NA, "1545-7230", NA, "0949-1775", "1873-6289", "0103-846X", "2153-2192", "1557-7333"), Level = c(1, 1, 1, 1, 0, 1, 2, 1, 0, 2)), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"))


df2 <- structure(list(ID = c(41120, 12249, 261, 12188, 40596, 12129, 769), pISSN = c(NA, NA, NA, "0025-5858", "1042-9670", "0895-4852", "0949-1775"), eISSN = c("2364-9534", "1530-9932", "2366-0058", "1865-8784", "1545-7230", "1936-4709", "1432-0517"), Format = c("E OA S C", "E OF S", "E OF S", "PE OF S", "PE OF S", "PE OF", "PE OF S")), row.names = c(NA, -7L), class = c("tbl_df", "tbl", "data.frame"))

surrogate_key <- Vectorize(function(x, y) {
  str_c(sort(c(x, y)), collapse = "")
})

df1 %>% mutate(join_key = surrogate_key(pISSN, eISSN)) -> df3
df2 %>% mutate(join_key = surrogate_key(pISSN, eISSN)) -> df4

result <- full_join(df3, df4, "join_key") %>%
  select(-join_key)
#> Warning: Column `join_key` has different attributes on LHS and RHS of join

result
#> # A tibble: 15 x 8
#>      ID.x pISSN.x   eISSN.x   Level  ID.y pISSN.y   eISSN.y   Format  
#>     <dbl> <chr>     <chr>     <dbl> <dbl> <chr>     <chr>     <chr>   
#>  1 437097 <NA>      1530-9932     1 12249 <NA>      1530-9932 E OF S  
#>  2 489309 2366-004X 2366-0058     1    NA <NA>      <NA>      <NA>    
#>  3 437103 0025-5858 <NA>          1    NA <NA>      <NA>      <NA>    
#>  4 437109 1042-9670 1545-7230     1 40596 1042-9670 1545-7230 PE OF S 
#>  5 449363 1093-1139 <NA>          0    NA <NA>      <NA>      <NA>    
#>  6 437127 <NA>      0949-1775     1    NA <NA>      <NA>      <NA>    
#>  7 437124 0361-3682 1873-6289     2    NA <NA>      <NA>      <NA>    
#>  8 481203 0103-846X 0103-846X     1    NA <NA>      <NA>      <NA>    
#>  9 479825 2153-2184 2153-2192     0    NA <NA>      <NA>      <NA>    
#> 10 437136 0734-2071 1557-7333     2    NA <NA>      <NA>      <NA>    
#> 11     NA <NA>      <NA>         NA 41120 <NA>      2364-9534 E OA S C
#> 12     NA <NA>      <NA>         NA   261 <NA>      2366-0058 E OF S  
#> 13     NA <NA>      <NA>         NA 12188 0025-5858 1865-8784 PE OF S 
#> 14     NA <NA>      <NA>         NA 12129 0895-4852 1936-4709 PE OF   
#> 15     NA <NA>      <NA>         NA   769 0949-1775 1432-0517 PE OF S

【讨论】:

  • @jehaa:我猜你发现full_join 是困难的部分。看看哈德利关于这个主题的书:r4ds.had.co.nz/relational-data.html#mutating-joins
  • 您好 rmagno,感谢您的回答。我试图更多地解释我的例子,感谢您的反馈。我还是 R 的新手,所以我试图了解你的建议是如何工作的。是不是类似gather函数?因为它在连接之前合并了两列?从我在您的结果中可以看到,它似乎并没有抓住每一个组合。例如,对于 df1 ID 条目 489309。这应该通过 eISSN 连接到 df2 ID 261。
  • @jehaa,您能否使您的示例与您的dput 示例代码一致?
  • 感谢您的链接。我已经设置了示例,以便它们现在是一致的。结果的确切格式并不重要,重要的是匹配。
猜你喜欢
  • 2016-11-28
  • 1970-01-01
  • 1970-01-01
  • 2017-10-08
  • 1970-01-01
  • 2020-06-12
  • 2021-03-25
  • 2013-05-11
  • 1970-01-01
相关资源
最近更新 更多