Сравнить и связать строки с различными порядками слов / количеством слов
Я пытаюсь использовать пакет recordLinkage, чтобы связать вместе два набора данных, где один набор данных имеет тенденцию давать несколько фамилий / отличий, а другой просто дает одну фамилию. В настоящее время используемой функцией сравнения строк является функция Jaro-Winkler, однако возвращаемая оценка зависит от того, как строки совпадают случайно, а не от того, содержится ли содержимое более короткой строки где-либо в более длинной строке. Это приводит к тому, что создается много ссылок низкого качества. Воспроизводимый пример неправильных взвешиваний таков:
library(RecordLinkage)
data1 <- as.data.frame(list("lname" = c("lolli gaggen nazeem", "lolli gaggen nazeem", "lolli gaggen nazeem"),
"bday" = c("1908-08-08", "1979-12-12", "1560-06-06") ) )
data2 <- as.data.frame(list("lname" = c("lolli", "gaggen", "nazeem"),
"bday" = c("1908-08-08", "1979-12-12", "1560-06-06") ) )
blocking_variable <- c("bday")
pass <- compare.linkage(data1, data2, blockfld = blocking_variable, strcmp = T)
pass_weights <- epiWeights(pass)
getPairs(pass_weights, single.rows = TRUE)
id1 lname.1 bday.1 id2 lname.2 bday.2 Weight
1 1 lolli gaggen nazheem 1908-08-08 1 lolli 1908-08-08 0.9162463
2 2 lolli gaggen nazheem 1979-12-12 2 gaggen 1979-12-12 0.8697165
3 3 lolli gaggen nazheem 1560-06-06 3 nazheem 1560-06-06 0.6995502
Я хочу, чтобы идентификаторы 2 и 3 получали примерно те же веса, что и идентификатор № 1, однако в настоящее время они намного ниже, поскольку их фамилии не совпадают в одинаковых позициях в обоих наборах данных (хотя содержание совпадает). Есть ли способ, которым я могу изменить используемую здесь функцию сравнения строк / структуру данных, чтобы я мог учесть различные порядки?
Дополнительные примечания:
Оба набора данных имеют миллионы строк, поэтому эффективность памяти здесь очень важна!
Иногда в другом наборе данных может быть больше одной фамилии, поэтому мы будем сравнивать 3 слова с 2 словами - вероятно, было бы лучше начать сначала с простого случая
- Чаще всего между двумя наборами данных возникают орфографические различия имен
- В настоящее время мы используем стадию качества IBM для создания этой связи, и они используют функцию сравнения "MULT_UNCERT" ( https://www.ibm.com/support/knowledgecenter/en/SSZJPZ_11.7.0/com.ibm.swg.im.iis.ds.design.help.doc/topics/r_qresfgde_MULT_UNCERT_comparison.html). Я хочу повторить это в R.
0 ответов
Задумывались ли вы о следующем подходе?
Записать связь и имена, как я знаю, вы знаете, сложно. В идеале вы хотите заблокировать другую доступную информацию (пол, уникальные идентификаторы, dob, информацию о местоположении и т. Д.), А затем выполнить сравнение строк в именах.
Вы упоминаете большие наборы данных с миллионами записей. Смотрите не дальше, чем data.table
пакет от великого Мэтта Доула ( Matt Dowle).
Пакет RecordLinkage медленный по сравнению. Вы можете легко улучшить приведенный ниже код, чтобы подумать о методах хеширования строк, используя soundex, двойной метафон, nysiis и т. Д.
# install.packages("data.table")
library(RecordLinkage)
library(data.table)
data1 <- as.data.frame(list("lname" = c("lolli gaggen nazeeem", "lolli gaggen nazeem", "lollly gaggen nazeem", "matt dowle", "john-smith"),
"bday" = c("1908-08-08", "1979-12-12", "1560-06-06", "1979-12-12", "1560-06-06") ) )
data2 <- as.data.frame(list("lname" = c("lolli", "gaggen", "nazeem", "m dowl", "johnny smith"),
"bday" = c("1908-08-08", "1979-12-12", "1560-06-06", "1979-12-12", "1560-06-06") ) )
# Coerce to data.tables
setDT(data1)
setDT(data2)
# Define a regex split (we will split all words based on space or hyphen)
split <- " |-"
# Apply a blocking strategy based on bday. Ideally your dataset would allow for additional blocking strategies(?).
block_pairs <- merge(data1, data2, by = "bday", all = T,
sort = TRUE, suffixes = c(".x", ".y"))
# Store the split up components of each comparison variable.
split1 <- strsplit(block_pairs[["lname.x"]], split)
split2 <- strsplit(block_pairs[["lname.y"]], split)
# Perform jarowinkler comparisons on each combination of components of each string
fc <- jarowinkler(block_pairs[["lname.x"]], block_pairs[["lname.y"]])
pc <- mapply(function(x, y) max(outer(x, y, jarowinkler)), split1, split2)
# Store the max of the full and partial comparisons
block_pairs[, ("winkler.lname") := mapply(function(x,y) max(x,y), fc, pc)]
# Sort by the jarowinkler score
block_pairs <- block_pairs[order(winkler.lname)]
# Inspect
block_pairs
# 0.96 is an appropriate threshold in this instance
block_pairs <- block_pairs[winkler.lname >= 0.96]
Дополнение, которое я сделал к ответу Хайенеса, изложено в комментарии:
library(gtools)
...
# Store the split up components of each comparison variable.
split1 <- strsplit(block_pairs[["lname.x"]], split)
split2 <- strsplit(block_pairs[["lname.y"]], split)
# Recombine tokens into all possible orderings:
make_combinations <- function(x) {
# Use permutations from the gtools package
split_names <- permutations(length(x),length(x),x)
apply(X=split_names, MARGIN=1, FUN=paste0, collapse=' ')
}
split1 <- lapply(X=split1, FUN=`make_combinations`)
split2 <- lapply(X=split2, FUN=`make_combinations`)
# Perform jarowinkler comparisons on each string combination and append it to the table
block_pairs[ ,("winkler.lname") := mapply(function(x, y) max(outer(x, y, jarowinkler)), split1, split2)]
# Sort by the jarowinkler score
block_pairs <- block_pairs[order(winkler.lname)]
# 0.85 is an appropriate threshold in this instance
block_pairs <- block_pairs[winkler.lname >= 0.85]
bday lname.x lname.y winkler.lname
1: 1908-08-08 lolli gaggen nazeem lolli 0.8526316
2: 1560-06-06 lolli gaggen nazeem nazeem 0.8631579
3: 1979-12-12 lolli gaggen nazeem gaggen 0.8631579
4: 1979-12-12 matt dowle m dowl 0.9200000
5: 1560-06-06 john-smith johnny smith 0.9666667