我有两个大型数据集d1
和d2
,我想根据EITHER变量idA
和idB
的匹配进行合并。
两者都存在于两个数据集中,但两者都可以包含错误和缺失值(NA),或者idA
和idB
指向其他数据集中的不同观察值。例如,下面的例子。
下面列出了预期的结果。基本上,匹配在idA或idB上。如果重复匹配,则应优先idA
次匹配idB
匹配。
实际数据集非常大(约10亿)。
是否有有效的方法在R中实现这一点?
此外,SQL中是否存在此类连接的技术术语?
library(tidyverse)
library(data.table)
d1 <- read.table(text=
"idA idB value1
A 10 500
B 1 111
C 4 234
D NA 400
E 7 500
NA 3 700
Z 5 543
Q 9 567
U 23 723
",
header=T) %>% data.table
d2 <- read.table(text=
"idA idB value2
A 10 11
B 1 12
L 21 15
D 15 12
E 8 17
M 3 18
N 5 13
Z 25 17
Q 23 12
",
header=T) %>% data.table
期望的结果是:
Out <- read.table(text=
"d1.idA d2.idA d1.idB d2.idB d1.v1 d2.v2
A A 10 10 500 11 # matched on idA and idB
B B 1 1 111 12 # matched on idA and idB
D D NA 15 400 12 # matched on idA. d2.idB had NAs
E E 7 8 500 17 # matched on idA. idB had divergent values
NA M 3 3 700 18 # matched on idB. d1.idA had NAs
Z Z 5 25 543 13 # d1[7,] matched to d2[8,] on idA and d2[9,] on idB. Priority given to idA match.
Q Q 9 23 657 17 # d2[9,] matched to d1[8,] on idA and d1[9,] on idB. Priority given to idA match.
",
header=T) %>% data.table
#Non matched rows
# d1[3,]
# d2[3,]
EDIT1:
EDIT2:从期望的结果中删除不匹配的行
答案 0 :(得分:2)
我不知道实现所需结果的优雅方式(而且我也不知道SQL中此类操作的技术术语。)
因此,我建议分四步完成:
idA
和idB
上的两个data.tables,确定每个data.tables中的剩余行。idA
上两个data.tables的剩余行,再次确定剩余的行。idB
上的两个data.tables的剩余行。所有4个步骤的代码:
library(data.table)
# create index column in both data.tables
d1[, idx := .I]
d2[, idx := .I]
# inner join on idA and idB
j1 <- d1[d2, .(idx, i.idx), on = c("idA", "idB"), nomatch = 0L]
m1 <- unique(j1$idx)
m2 <- unique(j1$i.idx)
# inner join on idA
j2 <- d1[!(idx %in% m1)][d2[!(idx %in% m2)], .(idx, i.idx), on = c("idA"), nomatch = 0L]
m1 <- append(m1, unique(j2$idx))
m2 <- append(m2, unique(j2$i.idx))
# inner join on idB
j3 <- d1[!(idx %in% m1)][d2[!(idx %in% m2)], .(idx, i.idx), on = c("idB"), nomatch = 0L]
m1 <- append(m1, unique(j3$idx))
m2 <- append(m2, unique(j3$i.idx))
# combine results
rbindlist(
list(
AB = cbind(
d1[idx %in% j1[, idx]],
d2[idx %in% j1[, i.idx]]),
A. = cbind(
d1[idx %in% j2[, idx]],
d2[idx %in% j2[, i.idx]]),
.B = cbind(
d1[idx %in% j3[, idx]],
d2[idx %in% j3[, i.idx]])),
fill = TRUE,
idcol = "match_on")
产生
# match_on idA idB value1 idx idA idB value2 idx
#1: AB A 10 500 1 A 10 11 1
#2: AB B 1 111 2 B 1 12 2
#3: A. D NA 400 4 D 15 12 4
#4: A. E 7 500 5 E 8 17 5
#5: A. Z 5 543 7 Z 25 17 8
#6: A. Q 9 567 8 Q 23 12 9
#7: .B NA 3 700 6 M 3 18 6
m1
和m2
用于记住d1
和d2
中的行的行ID,而这些行已经在其中一个中使用过了。以前的加入操作。
因此,最后可以打印d1
和d2
中未找到匹配项的剩余行:
d1[!(idx %in% m1)]
# idA idB value1 idx
#1: C 4 234 3
#2: U 23 723 9
d2[!(idx %in% m2)]
# idA idB value2 idx
#1: L 21 15 3
#2: N 5 13 7
请注意,在每个连接操作中,仅保留行索引而不是保留所有列。不同连接操作的结果在列的名称和位置上不同。
在最后的组合步骤中,使用这些索引选择原始data.tables d1
和d2
的行,以生成统一的结果表。
答案 1 :(得分:1)
一年后,我相信我找到了一种比my previous answer更直接的方法。这种方法截然不同,可以扩展到不止两个id列,因此为了清楚起见,最好将其作为单独的答案发布。
OP已请求在idA
或 idB
上找到匹配项。如果出现重复的匹配,则应优先选择idA
个匹配项,而不是idB
个匹配项。
这种方法将中的两个data.tables表中的id列从宽到长格式进行重塑,以使id列的名称成为可在后续使用的数据项加入行动。重塑是可以将此方法缩放为包括任意数量的id列的原因,例如,以在idA
或 idB
或 idC
。
library(data.table)
library(magrittr)
# create index column in both data.tables
d1[, idx := .I]
d2[, idx := .I]
# reshape only id columns from wide to long format
# rename columns just for clarity
l1 <- d1[, .(idx, idA, idB)] %>%
melt(id.vars = "idx", variable.name = "id.col", value.name = "key")
l2 <- d2[, .(idx, idA, idB)] %>%
melt(id.vars = "idx", variable.name = "id.col", value.name = "key")
# inner join to find all matching combinations of name of id col & key value
m <- l1[l2, on = .(id.col, key),
.(match_on = id.col, i1 = idx, i2 = i.idx), nomatch = 0L]
# remove duplicate entries with precedence of "idA"
m %<>%
setorder(match_on) %>%
unique(by = c("i1")) %>%
unique(by = c("i2")) %>%
setorder(i1, i2)
# double join of index table with original tables
d1[d2[m, on = .(idx = i2)], on = .(idx = i1)]
idA idB value1 idx i.idA i.idB value2 i.idx match_on 1: A 10 500 1 A 10 11 1 idA 2: B 1 111 2 B 1 12 2 idA 3: D NA 400 4 D 15 12 4 idA 4: E 7 500 5 E 8 17 5 idA 5: <NA> 3 700 6 M 3 18 6 idB 6: Z 5 543 7 Z 25 17 8 idA 7: Q 9 567 8 Q 23 12 9 idA
一些补充说明:
idA
和idB
的值通过重塑在key
列中组合时被强制键入字符。setorder(match_on)
是决定idA
比idB
优先级的关键部分。偶然地,idA
在字典上排在idB
之前。可以通过重新排列因子级别来强制使用其他优先级,例如使用forcats
包。