data.table 合并 R 中不同列的部分匹配
data.table merge on partial match of different columns in R
之前可能有人问过这个问题,但我正在寻找一个 data.table 解决方案(如果可能)而不使用其他软件包。我有一个 data.table DT1 作为参考:
> require(data.table)
> DT1 <- data.table(col1 = c("AA", "BA", "ABC", "ABC BC", "AB")
, col2 = c(1,4,5,3,2))
> DT1
col1 col2
1: AA 1
2: BA 4
3: ABC 5
4: ABC BC 3
5: AB 2
我想根据 DT1 中的 col1 和 DT2 中的 col2 的部分匹配,将第二个 data.table DT2 与 DT1 合并,从而在 DT2 中创建一个 col3。
> DT2 <- data.table(col1 = c(0,5,2,7,1,0)
, col2 = c("BA", "ABC", "DC", "AA", "AB", "R AB"))
> DT2
col1 col2
1: 0 BA
2: 5 ABC
3: 2 DC
4: 7 AA
5: 1 AB
6: 0 R AB
期望的输出
> desired_output <- data.table(col1 = c(0,5,5,2,7,1,1,1,0)
, col2 = c("BA", "ABC", "ABC", "DC", "AA", "AB", "AB", "AB", "R AB")
, col3 = c(4,5,3,NA,1,5,3,2,2))
> desired_output
col1 col2 col3
1: 0 BA 4
2: 5 ABC 5
3: 5 ABC 3
4: 2 DC NA
5: 7 AA 1
6: 1 AB 5
7: 1 AB 3
8: 1 AB 2
9: 0 R AB 2
有什么优雅的方法可以使用 data.table 操作来做到这一点吗?如果不是那么乐意考虑其他解决方案。这将是 运行 一个非常大的数据集。
编辑:指定部分匹配的条件,如果DT1中的col1字符串是DT2中col2字符串的子集,反之亦然(DT2中col2的字符串是DT1 中 col1 的字符串)。双向 grepl?
col1/DT1 col2/DT2
"AB" "There is ABhere" # it's a match
"ABC" "someABC" # it's a match
"ABC BC" "ABC" # it's a reverse match
"DR" "ADD" # no match
"BA" "HABAHA" # two matches
鉴于问题的规模 (DT1 [(1:50,000), (1:25)] - DT2[(1:50,000,000), (1:55)]),做一个可能是不可行的CJ of the IDs before a two-way grepl
.
分解不同的matches/approx。匹配,我们可以 1) 首先查找完全匹配,2) 然后大约。匹配 DT1 中的子字符串可以在 DT2 中找到的位置,然后 3) 反之亦然。
最后,我们对所有结果进行行绑定,并在原始 DT2 和行绑定结果之间进行左连接以获得所需的输出。
exactMatches <- DT1[DT2, on=c("ID1"="ID2"), nomatch=0L][,
ID2 := ID1]
substr1in2 <- DT2[, c(.SD, DT1[grepl(ID2, ID1) & ID1 != ID2]),
by=1:DT2[,.N]][!is.na(VAL1), -1L]
substr2in1 <- DT1[, c(.SD, DT2[grepl(ID1, ID2) & ID2 != ID1]),
by=1:DT1[,.N]][!is.na(VAL2), -1L]
binded <- rbindlist(list(exactMatches, substr1in2, substr2in1),
use.names=TRUE, fill=TRUE)
binded[DT2, on=.(ID2, VAL2)]
输出:
ID1 VAL1 VAL2 ID2
1: BA 4 0 BA
2: ABC 5 5 ABC
3: ABC BC 3 5 ABC
4: AB 2 5 ABC
5: <NA> NA 2 DC
6: AA 1 7 AA
7: AB 2 1 AB
8: ABC 5 1 AB
9: ABC BC 3 1 AB
10: AB 2 0 R AB
我更改了一些列名称以使代码更具可读性。数据:
DT1 <- data.table(ID1 = c("AA", "BA", "ABC", "ABC BC", "AB"),
VAL1 = c(1,4,5,3,2))
DT2 <- data.table(VAL2 = c(0,5,2,7,1,0),
ID2 = c("BA", "ABC", "DC", "AA", "AB", "R AB"))
之前可能有人问过这个问题,但我正在寻找一个 data.table 解决方案(如果可能)而不使用其他软件包。我有一个 data.table DT1 作为参考:
> require(data.table)
> DT1 <- data.table(col1 = c("AA", "BA", "ABC", "ABC BC", "AB")
, col2 = c(1,4,5,3,2))
> DT1
col1 col2
1: AA 1
2: BA 4
3: ABC 5
4: ABC BC 3
5: AB 2
我想根据 DT1 中的 col1 和 DT2 中的 col2 的部分匹配,将第二个 data.table DT2 与 DT1 合并,从而在 DT2 中创建一个 col3。
> DT2 <- data.table(col1 = c(0,5,2,7,1,0)
, col2 = c("BA", "ABC", "DC", "AA", "AB", "R AB"))
> DT2
col1 col2
1: 0 BA
2: 5 ABC
3: 2 DC
4: 7 AA
5: 1 AB
6: 0 R AB
期望的输出
> desired_output <- data.table(col1 = c(0,5,5,2,7,1,1,1,0)
, col2 = c("BA", "ABC", "ABC", "DC", "AA", "AB", "AB", "AB", "R AB")
, col3 = c(4,5,3,NA,1,5,3,2,2))
> desired_output
col1 col2 col3
1: 0 BA 4
2: 5 ABC 5
3: 5 ABC 3
4: 2 DC NA
5: 7 AA 1
6: 1 AB 5
7: 1 AB 3
8: 1 AB 2
9: 0 R AB 2
有什么优雅的方法可以使用 data.table 操作来做到这一点吗?如果不是那么乐意考虑其他解决方案。这将是 运行 一个非常大的数据集。
编辑:指定部分匹配的条件,如果DT1中的col1字符串是DT2中col2字符串的子集,反之亦然(DT2中col2的字符串是DT1 中 col1 的字符串)。双向 grepl?
col1/DT1 col2/DT2
"AB" "There is ABhere" # it's a match
"ABC" "someABC" # it's a match
"ABC BC" "ABC" # it's a reverse match
"DR" "ADD" # no match
"BA" "HABAHA" # two matches
鉴于问题的规模 (DT1 [(1:50,000), (1:25)] - DT2[(1:50,000,000), (1:55)]),做一个可能是不可行的CJ of the IDs before a two-way grepl
.
分解不同的matches/approx。匹配,我们可以 1) 首先查找完全匹配,2) 然后大约。匹配 DT1 中的子字符串可以在 DT2 中找到的位置,然后 3) 反之亦然。
最后,我们对所有结果进行行绑定,并在原始 DT2 和行绑定结果之间进行左连接以获得所需的输出。
exactMatches <- DT1[DT2, on=c("ID1"="ID2"), nomatch=0L][,
ID2 := ID1]
substr1in2 <- DT2[, c(.SD, DT1[grepl(ID2, ID1) & ID1 != ID2]),
by=1:DT2[,.N]][!is.na(VAL1), -1L]
substr2in1 <- DT1[, c(.SD, DT2[grepl(ID1, ID2) & ID2 != ID1]),
by=1:DT1[,.N]][!is.na(VAL2), -1L]
binded <- rbindlist(list(exactMatches, substr1in2, substr2in1),
use.names=TRUE, fill=TRUE)
binded[DT2, on=.(ID2, VAL2)]
输出:
ID1 VAL1 VAL2 ID2
1: BA 4 0 BA
2: ABC 5 5 ABC
3: ABC BC 3 5 ABC
4: AB 2 5 ABC
5: <NA> NA 2 DC
6: AA 1 7 AA
7: AB 2 1 AB
8: ABC 5 1 AB
9: ABC BC 3 1 AB
10: AB 2 0 R AB
我更改了一些列名称以使代码更具可读性。数据:
DT1 <- data.table(ID1 = c("AA", "BA", "ABC", "ABC BC", "AB"),
VAL1 = c(1,4,5,3,2))
DT2 <- data.table(VAL2 = c(0,5,2,7,1,0),
ID2 = c("BA", "ABC", "DC", "AA", "AB", "R AB"))