这是后续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
in DT1
更新为TRUE
.
它按预期工作在一个微不足道的可重现的例子。但我在使用真实数据时遇到了一些意想不到的行为,并且无法深究其根源。我可能遗漏了一些东西或者它可能是一个错误。不幸的是,我无法提供最小的可重现示例,因为数据很大,并且仅在大数据中显示。但我会尽力记录下来。
意外行为或错误
注意到这一点的是,由于历史原因,需要在两个单独的数据库中寻找匹配,因此,过滤器!(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
and 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)
,运行之间不再有任何差异,并且所有行第一次似乎都正确匹配。
不幸的是,为了解决现实世界的问题,我must存在多个非对等匹配条件。不仅要确保DT1
's DATE
在。。。之间DT2
's START_DATE
and END_DATE
s,而且还有DT1
's CHECKING_DATE
是在之前DT2
's EFFECTIVE_DATE
, etc.
总结一下
非股权加盟data.table
在以下情况下,行为方式会出现错误:
- 其中一张表中存在/不存在某些行
AND
- 多个非对等条件
更新:可重现的示例
set.seed(123)
library(data.table)
library(stringi)
n <- 100000
DT1 <- data.table(RANDOM_STRING = stri_rand_strings(n, 5, pattern = "[a-k]"),
DATE = sample(seq(as.Date('2016-01-01'), as.Date('2016-12-31'), by="day"), n, replace=T))
DT2 <- data.table(RANDOM_STRING = stri_rand_strings(n, 5, pattern = "[a-k]"),
START_DATE = sample(seq(as.Date('2015-01-01'), as.Date('2017-12-31'), by="day"), n, replace=T))
DT2[, EXPIRY_DATE := START_DATE + floor(runif(1000, 200,300))]
#Initialization
DT1[, MATCHED := FALSE]
#First run
DT1[!(MATCHED), MATCHED := DT2[.SD, on=.(RANDOM_STRING, START_DATE <= DATE, EXPIRY_DATE >= DATE), .N, by=.EACHI ]$N > 0L ]
DT1[, .N, by=MATCHED]
MATCHED N
1: FALSE 85833
2: TRUE 14167
#Second run
DT1[!(MATCHED), MATCHED := DT2[.SD, on=.(RANDOM_STRING, START_DATE <= DATE, EXPIRY_DATE >= DATE), .N, by=.EACHI ]$N > 0L ]
DT1[, .N, by=MATCHED]
MATCHED N
1: FALSE 73733
2: TRUE 26267
#And so on with subsequent runs...