Сопоставить два набора данных по нескольким «грязным» столбцам в R - PullRequest
0 голосов
/ 18 февраля 2019

Мне часто нужно сопоставлять два набора данных по нескольким совпадающим столбцам по двум причинам.Во-первых, каждая из этих характеристик является «грязной», то есть один столбец не всегда совпадает, даже когда должен (для действительно совпадающей строки).Во-вторых, характеристики не являются уникальными (например, мужской и женский).Подобное сопоставление полезно для сопоставления по времени (предварительное тестирование с оценками после тестирования), различных модальностей данных (наблюдаемые характеристики и лабораторные значения) или нескольких наборов данных для участников исследования.

Мне нужна эвристика, которая выбираетлучший матч Затем я могу выполнить анализ двух вместе, как описано в этом вопросе. Обратите внимание, что есть много совпадающих столбцов и много идентификаторов, поэтому они оба должны быть указаны в виде списка или вектора.Как пример, я создал два набора данных ниже, чтобы соответствовать.В этом примере строка 1 DT1 (ID 1) является наилучшим соответствием для строки 1 DT2 (ID 55), даже если совпадает только столбец match4;это связано с тем, что строки 2 и 3 DT2 лучше подходят для строк 2 и 3 DT1. Бонус: строка 7 DT1 одинаково совпадает со строками 7 и 8 DT2, но имеет частичное соответствие строке 7 DT2, поэтому в идеале это должно быть выбрано.

Вопрос: Для DT1 выберите «наилучшее предположение» для соответствующей строки из DT2 и используйте каждую строку из DT2 только один раз.Каков наилучший способ сделать это (эффективным и «идиотским» идиоматическим способом) в R?

Мой предварительный подход: я создал третий файл data.table со столбцом идентификаторов из DT1, который называется DTmatch.Все последующие столбцы будут идентификаторами из DT2.Для второго столбца DTmatch (названного в честь первого идентификатора DT2) каждое значение должно представлять количество совпадающих столбцов (в данном примере от 0 до 4).Затем найдите самые высокие значения соответствия в таблице соответствия, уникальной для каждой строки и столбца.Наконец, создайте последний столбец, в котором указан идентификатор DT2, соответствующий идентификатору DT1 (столбец 1 в DTmatch).

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 голосов
/ 18 февраля 2019

Возможно, это вариант для начала:

сначала создайте новый столбец, вставив все значения из совпадающих столбцов вместе

#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 -package, вы можете выполнить соединение на основе строки-расстояния.Ниже максимальное расстояние установлено на 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".
При объединении Fuzzy всегда есть (на мой взгляд) много ошибок и ошибок.И часто вам придется признать, что «идеальный ответ» - это просто , а не там ...

...