R Соответствие строки для адреса с использованием stringdist, stringdistmatrix
У меня есть два больших набора данных, один около полумиллиона записей, а другой около 70K. Эти наборы данных имеют адрес. Я хочу сопоставить, если какой-либо адрес в меньшем наборе данных присутствует в большом. Как вы можете себе представить, адрес может быть записан по-разному и в разных случаях, поэтому довольно неприятно видеть, что нет совпадения, когда оно должно совпадать, и есть совпадение, когда оно не должно совпадать. Я провел небольшое исследование и выяснил, какой пакет строки можно использовать. Однако я застрял и чувствую, что не использую в полной мере свои возможности, и некоторые предложения по этому помогут.
Ниже приведен пример фиктивных данных вместе с кодом, который я создал для объяснения ситуации.
Address1 <- c("786, GALI NO 5, XYZ","rambo, 45, strret 4, atlast, pqr","23/4, 23RD FLOOR, STREET 2, ABC-E, PQR","45-B, GALI NO5, XYZ","HECTIC, 99 STREET, PQR")
df1 <- data.table(Address1)
Address2 <- c("abc, pqr, xyz","786, GALI NO 4 XYZ","45B, GALI NO 5, XYZ","del, 546, strret2, towards east, pqr","23/4, STREET 2, PQR")
df2 <- data.table(Address2)
df1[, key_match := gsub("[^[:alnum:]]", "", Address1)]
df2[, key_match := gsub("[^[:alnum:]]", "", Address2)]
fn_match = function(str, strVec, n){
strVec[amatch(str, strVec, method = "dl", maxDist=n,useBytes = T)]
}
df1[!is.na(key_match)
, address_match :=
fn_match(key_match, df2$key_match,3)
]
Если вы видите вывод, он дает мне совпадения под address_match в df1. Если я применяю тот же код к своим основным данным, код все еще работает с последних 30 часов. Хотя я преобразовал в data.table. Не уверен, как я могу ускорить это.
Я продолжал читать и наткнулся на матрицу строк. Это кажется более полезным, и я могу разделить адрес по пространству и проверить наличие каждого слова в каждом списке адресов, и в зависимости от максимального совпадения можно создать сводку совпадений. Однако я не очень хорош в петлях. Как мне пройти через каждый адрес из меньшего файла для каждого слова и проверить индивидуальный адрес в большем файле и создать матрицу совпадений? Любая помощь!!
1 ответ
У меня есть решение, которое не требует data.table
но если набор огромен, может работать с package:parallel
rbind.pages(
parallel::mclapply(Address1, function(i){
data.frame(
src = i,
match = Address2[which.min(adist(i, Address2))]
)
}, mc.cores = parallel::detectCores() - 2)) %>%
select(`src (Address1)`= 1, `match (Address2)` = 2)
Что тогда дает выходное решение:
src (Address1) match (Address2)
1 786, GALI NO 5, XYZ 786, GALI NO 4 XYZ
2 rambo, 45, strret 4, atlast, pqr del, 546, strret2, towards east, pqr
3 23/4, 23RD FLOOR, STREET 2, ABC-E, PQR 23/4, STREET 2, PQR
4 45-B, GALI NO5, XYZ 45B, GALI NO 5, XYZ
5 HECTIC, 99 STREET, PQR 23/4, STREET 2, PQR
Редактировать:
Я понял, что это может быть не очень полезно, не видя вычислений расстояния, так что вы можете настроить для своих нужд; поэтому я скопировал данные в более крупные случайные множества, а затем исправил функцию, чтобы показать вычисления расстояния до строки и время обработки
rand_addy_one <- rep(Address1, 1000)[sample(1:1000, 1000)]
rand_addy_two <- rep(Address2, 3000)[sample(1:3000, 3000)]
system.time({
test_one <<- rbind.pages(parallel::mclapply(rand_addy_one, function(i) {
calc <- as.data.frame(drop(attr(adist(i, rand_addy_two, counts = TRUE), "counts")))
calc$totals <- (rowSums(calc))
calc %>% mutate(src = i, target = rand_addy_two) %>%
filter(totals == min(totals))
}, mc.cores = parallel::detectCores() - 2)) %>%
select(`source Address1` = src, `target Address2(matched)` = target,
insertions = ins, deletions = del, substitutions = sub,
total_approx_dist = totals)
})
user system elapsed
24.940 1.480 3.384
> nrow(test_one)
[1] 600000
Теперь обратимся и применим больший набор к меньшему:
system.time({
test_two <<- rbind.pages(parallel::mclapply(rand_addy_two, function(i) {
calc <- as.data.frame(drop(attr(adist(i, rand_addy_one, counts = TRUE), "counts")))
calc$totals <- (rowSums(calc))
calc %>% mutate(src = i, target = rand_addy_one) %>%
filter(totals == min(totals))
}, mc.cores = parallel::detectCores() - 2)) %>%
select(`source Address2` = src, `target Address1(matched)` = target,
insertions = ins, deletions = del, substitutions = sub,
total_approx_dist = totals)
})
user system elapsed
27.512 1.280 4.077
nrow(test_two)
[1] 720000