我有两个数据框 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
非常感谢您的帮助。
我们可以这样使用
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
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
方法更快。
分两步完成并用
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"
这是一个使用
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