这是对this问题的跟进,其中接受的答案显示了使用data.table
进行匹配练习的示例,包括非等距条件。
背景
基本设置是我们DT1
包含人员详细信息的样本,DT2
是一种主数据库。目的是确定DT1
中的每个人是否与DT2
中的至少一个条目匹配。
首先,我们初始化一个表示与FALSE
匹配的列,以便每当找到匹配项时,其值都可以更新为TRUE
。
DT1[, MATCHED := FALSE]
然后使用以下通用解决方案更新列:
DT1[, MATCHED := DT2[.SD, on=.(Criteria), .N, by=.EACHI ]$N > 0L ]
理论上,它看起来(并且应该工作)很好。子表达式DT2[.SD, on=.(Criteria), .N, by=.EACHI]
生成一个子表,其中每一行都来自DT1
,并计算N
列,该列是DT2
中找到的该行的匹配数。然后,只要N
大于零,MATCHED
中DT1
的值就会更新为TRUE
。
它按照预期的reproducible example工作。但是我遇到了一些与真实数据一起使用它的意外行为,并且无法找到它的底部。我可能会遗漏某些东西或者它可能是一个错误。不幸的是,我无法提供一个可重复性最小的示例,因为数据很大,而且它只显示在大数据中。但我会尽力记录下来。
意外行为或错误
有助于注意到这一点的是,出于历史原因,需要在两个单独的数据库中搜索匹配,因此,过滤器!(MATCHED)
被添加到表达式中以仅更新那些尚未存在的值匹配:
DT1[!(MATCHED), MATCHED := DT2[.SD, on=.(Criteria), .N, by=.EACHI ]$N > 0L ]
然后我注意到如果该行重新运行多次,每次后续运行,将会有越来越多的匹配,这些匹配在前面的运行中不匹配。 (与单独的数据库无关,每次运行都与DT2匹配)。
首先运行:
MATCHED N
1: FALSE 3248007
2: TRUE 2379514
第二轮:
MATCHED N
1: FALSE 2149648
2: TRUE 3477873
为了调查,我然后过滤了第一次运行时没有匹配但在第二次运行时匹配的情况。看起来大多数情况都是假阴性,即那些应该在第一次运行时匹配的情况,但事实并非如此。 (但随着许多次运行,最终也出现了许多误报)。
例如,以下是DT1
的一个条目:
DATE FORENAME SURNAME
1: 2016-01-01 JOHN SMITH
来自DT2的匹配条目:
START_DATE EXPIRY_DATE FORENAME SURNAME
1: 2015-09-09 2017-05-01 JOHN SMITH
在主表达式之外单独运行子表达式(如上所述)来查看N
数字,我们发现它不会导致匹配,应该是什么时候(N=0
)。 (您可能还会注意到START_DATE
和END_DATE
在输出中采用DATE
的值,但这是另一个问题。)
SUB <- DF2[DF1, on=.(FORENAME, SURNAME, START_DATE <= DATE, EXPIRY_DATE >= DATE), .N, by=.EACHI]
SUB[FORENAME=="JOHN" & "SURNAME=="SMITH"]
FORENAME SURNAME START_DATE EXPIRY_DATE N
1: JOHN SMITH 2016-01-01 2016-01-01 0
然而,错误的行为是结果受DF1
中其他行的影响。例如,假设我知道JOHN SMITH
中DF1
的行号为149,并且只将DF1
过滤到该行:
DF2[DF1[149], on=.(FORENAME, SURNAME, START_DATE <= DATE, EXPIRY_DATE >= DATE), .N, by=.EACHI]
FORENAME SURNAME START_DATE EXPIRY_DATE N
1: JOHN SMITH 2016-01-01 2016-01-01 1
其次,我还注意到,在条件中只有一个以上的非等标准才会出现有缺陷的行为。如果条件为on=.(FORENAME, SURNAME, START_DATE <= DATE)
,则运行之间不再存在任何差异,并且所有行似乎在第一次正确匹配。
不幸的是,为了解决现实问题,我必须有几个非等匹配条件。不仅要确保DT1
的{{1}}介于DATE
的{{1}}和DT2
之间,还要确保START_DATE
的{{1}} { {1}}在END_DATE
之前DT1
等等。
总结
CHECKING_DATE
中的非等连接在以下情况下以错误的方式运行:
和
更新:可重现的示例
DT2
答案 0 :(得分:1)
这是一种解决方案,虽然不是很优雅,但在错误未修复的情况下似乎能够提供正确的结果。
首先,我们需要DT1
和DT2
中的每一行都有唯一的ID。行号可以。
DT1[, DT1_ID := 1:nrow(DT1)]
DT2[, DT2_ID := 1:nrow(DT2)]
然后,我们执行以下右连接以查找匹配项:
M <- DT2[DT1, on=.(RANDOM_STRING, START_DATE <= DATE, EXPIRY_DATE >= DATE)]
head(M, 3)
RANDOM_STRING START_DATE EXPIRY_DATE DT2_ID DT1_ID
1: diejk 2016-03-30 2016-03-30 NA 1
2: afjgf 2016-09-14 2016-09-14 NA 2
3: kehgb 2016-12-11 2016-12-11 NA 3
M
的{{1}}中的每一行都位于DT1
中该行的所有匹配项旁边。当DT2
时,没有匹配项。 DT2_ID = NA
,表示某些nrow(M) = 100969
行与&gt; 1 DT1
行匹配。 (日期也接受了错误的价值观。)
接下来,我们可以使用DT2
语句标记原始ifelse()
中的行,具体取决于它们是否匹配。
DT1
最终结果:13,316场比赛为100,000
DT1$MATCHED <- ifelse(DT1$DT1_ID %in% M[!is.na(DT2_ID)]$DT1_ID, TRUE, FALSE)