将一个数据框中的列中的字符串与 R 中另一个数据框中的两列中的任一个匹配

问题描述 投票:0回答:4

我有两个数据框 df1 和 df2。 df1 包含一个带有产品名称的列 df1$product。第二个包括此处感兴趣的三列:df2$product_code、df2$product_name 和 df2$product_aka_name。 我需要在 df1 中添加一个列 df1$product_code 基于匹配但是产品被称为(假设它可以用 df2 中列出的两个不同的名称知道)。

到目前为止,我在整理 df2$product_name 和 df2$product_aka_name(失败)后尝试使用 match()。我也尝试过使用 grep() 但几个小时后不得不停止,但没有任何结果。最后,我查看了 fuzzyjoin,但它似乎不适合我的问题。 真实数据集df1分别为70万行20列,df2为4万行5列。所以我需要一些快速高效的东西……

MRE 可以如下所示:

df1 <- data.frame(product = c("Abcd", "Efgh", "Ijkl", "Mnop", "Qrst", "Uvwx"), col2 = c("DLK", "CBN", "ABC", "ZHU", "HSC", "LJK"), col3 = c("qdsf88", "sdf63", "dd995", "dgsg1", "xxx587", "dfr55"))
df1
          product      col2            col3
1         Abcd         DLK             qdsf88
2         Efgh         CBN             sdf63
3         Ijkl         ABC             dd995
4         Mnop         ZHU             dgsg1
5         Qrst         HSC             xxx587
6         Uvwx         LJK             dfr55

df2 <- data.frame(product_code = c("1001", "1002", "1003", "1004", "1005", "1006", "1007", "1008"), product_name = c("Fcde", "Abcd", "Efgh", "Mlfr", "Mnop", "Plor", "Kdlr", "Vfsd"), product_aka_name = c(NA, NA, NA, "Qrst", NA, "Uvwx", "Azer", "Qwer"))
df2
 product_code product_name product_aka_name
1         1001         Fcde             <NA>
2         1002         Abcd             <NA>
3         1003         Efgh             <NA>
4         1004         Mlfr             Qrst
5         1005         Mnop             <NA>
6         1006         Plor             Uvwx
7         1007         Kdlr             Azer
8         1008         Vfsd             Qwer

我需要的结果如下:

df1 <- data.frame(product = c("Abcd", "Efgh", "Ijkl", "Mnop", "Qrst", "Uvwx"), col2 = c("DLK", "CBN", "ABC", "ZHU", "HSC", "LJK"), col3 = c("qdsf88", "sdf63", "dd995", "dgsg1", "xxx587", "dfr55"), product_code = c("1002", "1003", NA, "1005", "1004", "1006"))
df1
 product col2   col3 product_code
1    Abcd  DLK qdsf88         1002
2    Efgh  CBN  sdf63         1003
3    Ijkl  ABC  dd995         <NA>
4    Mnop  ZHU  dgsg1         1005
5    Qrst  HSC xxx587         1004
6    Uvwx  LJK  dfr55         1006

非常感谢您的帮助。

r matching
4个回答
2
投票

我们可以这样使用

dplyr

library(dplyr)

df1 %>% 
  left_join(df2, by = c("product" = "product_name")) %>% 
  left_join(df2, by = c("product" = "product_aka_name")) %>% 
  mutate(product_code = coalesce(product_code.x, product_code.y), .keep = "unused") %>% 
  select(-c(product_aka_name, product_name))

product col2   col3 product_code
1    Abcd  DLK qdsf88         1002
2    Efgh  CBN  sdf63         1003
3    Ijkl  ABC  dd995         <NA>
4    Mnop  ZHU  dgsg1         1005
5    Qrst  HSC xxx587         1004
6    Uvwx  LJK  dfr55         1006

2
投票

当您正在寻找快速高效的东西时,我会为此使用

data.table

library(data.table)

setDT(df1); setDT(df2)

df1[df2,
    on = c("product" = "product_name"),
    code1 := i.product_code
][df2,
    on = c("product" = "product_aka_name"),
    code2 := i.product_code
][, 
    product_code := fcoalesce(
        code1, code2
    )
][, `:=`(
    code1 = NULL,
    code2 = NULL
)]

df1
#    product col2   col3 product_code
# 1:    Abcd  DLK qdsf88         1002
# 2:    Efgh  CBN  sdf63         1003
# 3:    Ijkl  ABC  dd995         <NA>
# 4:    Mnop  ZHU  dgsg1         1005
# 5:    Qrst  HSC xxx587         1004
# 6:    Uvwx  LJK  dfr55         1006

data.table
允许您通过引用修改,这通常可以避免创建列或数据框的副本。这通常意味着对于大型数据集,它比等效的
tidyverse
方法更快。


2
投票

分两步完成并用

NA
更新。

i <- match(df1$product, df2$product_name)
j <- is.na(i)
i[j] <- match(df1$product[j], df2$product_aka_name)
df2$product_code[i]
#[1] "1002" "1003" NA     "1005" "1004" "1006"

如果这是缓慢使用

fastmatch::fmatch
而不是
match
并且可能使用
which(is.na(i))
而不是
is.na(i)
.

或者创建一个索引向量,设置名称并将其用于子集。

df2$product_code[setNames(rep(seq_len(nrow(df2)), 2),
        c(df2$product_name, df2$product_aka_name))[df1$product]]
#[1] "1002" "1003" NA     "1005" "1004" "1006"

1
投票

这是一个使用

dplyr
tiydr
的函数的连接策略。它的工作原理是将
df2
旋转成更长的形式,其中每一行都是一个与产品代码匹配的名称(包括别名):

library(dplyr)
library(tidyr)
df1 %>%
  left_join( # keeps all rows in df1 even if returns NA
    df2 %>%
      pivot_longer(cols = ends_with("name"), # pivots the columns with product names
                   values_to = "product", # names the new column to match df1
                   names_to = NULL) # discards old column names
  )

Joining, by = "product"
  product col2   col3 product_code
1    Abcd  DLK qdsf88         1002
2    Efgh  CBN  sdf63         1003
3    Ijkl  ABC  dd995         <NA>
4    Mnop  ZHU  dgsg1         1005
5    Qrst  HSC xxx587         1004
6    Uvwx  LJK  dfr55         1006
© www.soinside.com 2019 - 2024. All rights reserved.