#r #string #match #character
#r #строка #совпадение #символ
Вопрос:
У меня есть два фрейма данных с двумя символьными векторами разной длины, которые я хотел бы сопоставить, например:
company.a <- c("heinz", "hawkings mcgill", "heinz ketchup", "heinz vinegars", "davis and smith", "dell computers", "dell", "O organics", "organics")
company.b <- c("heinz", "hawkings-mcgill", "oyster bay", "company x", "dell")
Я хотел бы сравнить company.b с company.a, возвращая вектор, содержащий элемент из company.b, который был сопоставлен с company.a. Я попытался использовать следующий код для подмножества большего кадра
match.comp <- subset(company.a, grep(paste(company.b, collapse = "|"), company.a, value = TRUE)).
Однако то, что я получаю взамен, — это ошибка, указывающая, что «подмножество» должно быть логичным. Я хотел бы получить следующий результат:
match <- c("heinz", "hawkings mcgill", "heinz", "heinz", "FALSE", "dell", "dell", FALSE, FALSE)
Учитывая ошибку, очевидно, что я что-то упускаю в grep или подмножестве. У меня два вопроса:
-
Является ли grep лучшим способом сделать это? Или есть другой способ? Я знаю о точном сопоставлении с использованием подхода which (A %in% B), но я не могу гарантировать, что строки будут точно совпадать.
-
Grep вернет первое совпадение, но есть ли способ извлечь все возможные совпадения, которые были рассмотрены, скажем, с помощью расстояния Левенштейна? Я знаю о функции adist в пакете utils, но я хочу знать, можно ли ее объединить с grep.
Любая помощь или совет будут с благодарностью приняты. Спасибо.
Комментарии:
1.
agrep
? Ваш желаемый результат неясен.2. @alistaire спасибо. agrep выглядит хорошо. я хочу вернуть сопоставленные элементы, если они работают, а если нет, логическое значение. это понятнее?
Ответ №1:
Вы можете использовать adist
или agrep
, но это очень субъективный процесс с точки зрения того, какими будут затраты и точки отсечения. В этом случае вы могли бы получить желаемый результат с помощью
d <- adist(company.b, company.a, partial = TRUE)
d <- apply(d, 2, prop.table) # working with proportions instead of costs can be useful
matches <- apply(d, 2, function(x){
x <- setNames(x, company.b)
names(which.min(x[x < 0.05])) # set cutoff carefully
})
matches <- sapply(matches, function(x){ifelse(is.null(x), NA, x)}) # clean out NULLs
matches
## [1] "heinz" "hawkings-mcgill" "heinz" "heinz" NA
## [6] "dell" "dell" NA NA
Комментарии:
1. спасибо, это действительно отличное решение! Я действительно ценю помощь. есть ли у вас какие-либо рекомендации по процедурам определения оптимальных точек отсечения для очень больших фреймов данных? или, по крайней мере, где я мог бы посмотреть, чтобы понять это? еще раз спасибо.
2. Это немного зависит от того, что вы знаете о строках и с чем вы хотите, чтобы они соответствовали. Учитывая приведенный выше пример, строки для сопоставления были в основном короче, поэтому я использовал
partial = TRUE
, который соответствует подстрокам без стоимости удаления символов, но вы можете явно задать стоимость удаления. Прочитайте документы и поиграйте, но для данных, слишком больших для проверки вручную, я бы проверил частоту ошибок в подмножестве, которое вы можете проверить вручную, сродни ошибке обучения при построении модели.3. не могли бы вы подробнее рассказать об ограничении? соответствует ли оно тем, у которых доля одинаковых символов составляет менее 5%?
4. В этой настройке это то, что определяет, какие из них дают вам
NA
s. Всегда есть какое-то значение с минимальным расстоянием редактирования, но это значение может быть очень высоким. Из-заprop.table
вызова0.05
действительно фильтрует значения, которые содержат менее 5% расстояния редактирования для всех расстояний редактирования для этой строки, что отдаленно похоже наagrep
max.distance
параметр. Вы можете использовать редактирование расстояний напрямую вместо пропорций, если хотите.
Ответ №2:
Это может частично удовлетворить ваши требования:
library(utils)
company.a <- c("heinz", "hawkings mcgill", "heinz ketchup", "heinz vinegars", "davis and smith", "dell computers", "dell", "O organics", "organics")
company.b <- c("heinz", "hawkings-mcgill", "oyster bay", "company x", "dell")
limit <- 2
res <- sapply(company.a, function(wa) {
d <- sapply(company.b, function(wb){
adist(wb, wa)
})
d <- d[d<=limit]
names(d)
})
Приведенный выше фрагмент кода извлечет все совпадения во втором массиве для каждого слова в первом массиве. Здесь сопоставляются два слова, если расстояние Левенштейна является самым «пределом».
Также обратите внимание, что некоторые из совпадений, которые вы указали выше, не так просты. Например, если «heinz» должен соответствовать «heinz ketchup», для этого потребуется ограничение расстояния Левенштейна в 8, что в целом будет слишком высоким. Для решения этих случаев необходимо будет создать более сложную функцию расстояния.