在R

时间:2019-02-18 03:33:36

标签: r merge data.table fuzzy-comparison record-linkage

出于两个原因,我经常需要通过多个匹配列来匹配两个数据集。首先,这些特征中的每一个都是“脏”的,这意味着即使在应有的情况下,单个列也并不一致(对于真正匹配的行)。其次,特征不是唯一的(例如,男性和女性)。这样的匹配对于跨时间(测试前和测试后分数),不同的数据模式(观察到的特征和实验室值)或研究参与者的多个数据集进行匹配非常有用。

我需要选择最佳匹配的启发式方法。 Then I can perform analyses of the two together, as described in this question.请注意,有许多匹配的列和许多ID,因此必须将它们都指定为列表或向量。例如,我在下面创建了两个数据集以进行匹配。在此示例中,即使只有“ match4”列匹配,DT1第1行(ID 1)也是DT2第1行(ID 55)的最佳匹配。这是因为DT2第2行和第3行与DT1第2行和第3行更好地匹配。 >

问题:对于DT1,请为DT2中的匹配行选择一个“最佳猜测”,并仅使用DT2中的每一行。在R中执行此操作的最佳方法是什么(以一种有效的“最佳实践”惯用方式)?

我的初步方法: 我创建了第三个data.table,其中包含来自DT1的ID列,称为DTmatch。所有后续列将是DT2中的ID。对于DTmatch的第二列(以DT2的第一个ID命名),每个值都应代表匹配列的计数(在此示例中为0到4)。接下来,在每一行和每一列唯一的匹配表中找到最高匹配值。最后,创建最后一列,指定与DT1 ID匹配的DT2 ID(DTmatch中的第1列)。

library(data.table)
# In this example, the datasets are matched by row number, but the real data is not.
DT1 = data.table(
  ID = 1:7,
  match1 = c("b","b","b","a","a","c",NA),
  match2 = c(7, 8, 9, NA, NA, NA, NA),
  match3 = c(0, 0, 0, "j", 13:15),
  match4 = c(rep("m", 4), rep("f", 3)),
  value1 = 45:51,
  value2 = 100:106
)

DT2 = data.table(
  ID = 55:62,
  match1 = c("b","b",4,"a","a","c","j","j"),
  match2 = c(77, 8:14),
  match3 = c(9:14, 155, 16),
  match4 = c(rep("m", 4), NA, rep("f", 3)),
  value1 = 145:152,
  value2 = 101:108
)

# Fix numeric IDs
DT1[, ID := make.names(ID)]
DT2[, ID := make.names(ID)]

# Make new matching table
DTmatch <- DT1[, .(make.names(ID))]
setnames(DTmatch, old = "V1", new = "DT1ID")

# Start with one ID and one matching column
DT2ID <- DT2$ID[1]
DTmatch[, (DT2ID) := 0]
matchingCols <- c("match1")

# Code for first ID and match1, to be adapted for all IDs and all columns
DTmatch[, (DT2ID) := eval(parse(text=DT2ID)) + as.numeric(DT1[, (matchingCols), with=F] == DT2[ID==DT2ID, matchingCols, with=F][[1]])]

# First attempt at matching doesn't work due to NAs
for (thisID in DT2$ID) {
  DTmatch[, (thisID) := 0]
  for (matchingCol in matchingCols) {
#    if (!is.na(DT1[, matchingCol, with=F]) & !is.na(DT2[ID==thisID, matchingCol, with=F])) {
      DTmatch[, (thisID) := eval(parse(text=thisID)) + as.numeric(DT1[, (matchingCol), with=F] == DT2[ID==thisID, matchingCol, with=F][[1]])]
#    }
  }
}

1 个答案:

答案 0 :(得分:0)

也许这是一个开始的选项:

首先,通过将匹配列中的所有值粘贴在一起,创建一个新列

#create new column based on matching cols
DT1[, col_join := do.call( paste, c(.SD, sep="")), .SDcols= match1:match4][]
DT2[, col_join := do.call( paste, c(.SD, sep="")), .SDcols= match1:match4][]

然后,使用fuzzyjoin包,您可以基于字符串距离执行连接。 下面,最大距离设置为2。因此,如果在2的距离内找不到匹配的字符串,则连接的结果将为<NA>
您可以/应该尝试不同的stringdist方法和最大距离...

library(fuzzyjoin)
result <- stringdist_join( DT2, DT1, 
                           by = "col_join", 
                           max_dist = 2, 
                           mode = "left", 
                           distance_col = "string_distance" )

result[,c(1,8,9,16,17)][]
# ID.x col_join.x ID.y col_join.y string_distance
# 1:   55      b779m    1       b70m               2
# 2:   56      b810m    1       b70m               2
# 3:   56      b810m    2       b80m               1
# 4:   56      b810m    3       b90m               2
# 5:   57      4911m   NA       <NA>              NA
# 6:   58     a1012m   NA       <NA>              NA
# 7:   59    a1113NA   NA       <NA>              NA
# 8:   60     c1214f    6     cNA14f               2
# 9:   61    j13155f   NA       <NA>              NA
# 10:   62     j1416f   NA       <NA>              NA

如您所见,您仍然需要弄清楚一些东西,例如“如何使用NA值”。
在我看来,使用模糊连接总是会涉及很多错误。很多时候,您不得不接受“完美答案”只是在那里...