Создать уникальный идентификатор путем нечеткого сопоставления имен (через agrep с использованием R)
Используя R, я пытаюсь сопоставить имена людей в наборе данных, структурированном по году и городу. Из-за некоторых орфографических ошибок точное совпадение невозможно, поэтому я пытаюсь использовать agrep() для нечетких имен совпадений.
Примерный фрагмент набора данных имеет следующую структуру:
df <- data.frame(matrix( c("1200013","1200013","1200013","1200013","1200013","1200013","1200013","1200013", "1996","1996","1996","1996","2000","2000","2004","2004","AGUSTINHO FORTUNATO FILHO","ANTONIO PEREIRA NETO","FERNANDO JOSE DA COSTA","PAULO CEZAR FERREIRA DE ARAUJO","PAULO CESAR FERREIRA DE ARAUJO","SEBASTIAO BOCALOM RODRIGUES","JOAO DE ALMEIDA","PAULO CESAR FERREIRA DE ARAUJO"), ncol=3,dimnames=list(seq(1:8),c("citycode","year","candidate")) ))
Аккуратная версия:
citycode year candidate
1 1200013 1996 AGUSTINHO FORTUNATO FILHO
2 1200013 1996 ANTONIO PEREIRA NETO
3 1200013 1996 FERNANDO JOSE DA COSTA
4 1200013 1996 PAULO CEZAR FERREIRA DE ARAUJO
5 1200013 2000 PAULO CESAR FERREIRA DE ARAUJO
6 1200013 2000 SEBASTIAO BOCALOM RODRIGUES
7 1200013 2004 JOAO DE ALMEIDA
8 1200013 2004 PAULO CESAR FERREIRA DE ARAUJO
Я хотел бы проверить в каждом городе отдельно, есть ли кандидаты, появляющиеся через несколько лет. Например, в примере
PAULO CEZAR FERREIRA DE ARAUJO
ПАУЛО СЕСАР ФЕРРЕЙРА ДЕ АРАУХО
появляется дважды (с орфографической ошибкой). Каждому кандидату во всем наборе данных должен быть присвоен уникальный числовой идентификатор кандидата. Набор данных довольно большой (5500 городов, около 100 тыс. Записей), поэтому было бы полезно несколько более эффективное кодирование. Любые предложения относительно того, как это реализовать?
РЕДАКТИРОВАТЬ: Вот моя попытка (с помощью комментариев до сих пор), что очень медленно (неэффективно) в достижении поставленной задачи. Есть предложения по улучшению этого?
f <- function(x) {matches <- lapply(levels(x), agrep, x=levels(x),fixed=TRUE, value=FALSE)
levels(x) <- levels(x)[unlist(lapply(matches, function(x) x[1]))]
x
}
temp <- tapply(df$candidate, df$citycode, f, simplify=TRUE)
df$candidatenew <- unlist(temp)
df$spellerror <- ifelse(as.character(df$candidate)==as.character(df$candidatenew), 0, 1)
РЕДАКТИРОВАТЬ 2: Теперь работает на хорошей скорости. Проблемой было сравнение со многими факторами на каждом этапе (спасибо за указание на это, Blue Magister). Сокращение сравнения только до кандидатов в одной группе (т.е. городе) запускает команду за 5 секунд для 80000 строк - скорость, с которой я могу жить.
df$candidate <- as.character(df$candidate)
f <- function(x) {x <- as.factor(x)
matches <- lapply(levels(x), agrep, x=levels(x),fixed=TRUE, value=FALSE)
levels(x) <- levels(x)[unlist(lapply(matches, function(x) x[1]))]
as.character(x)
}
temp <- tapply(df$candidate, df$citycode, f, simplify=TRUE)
df$candidatenew <- unlist(temp)
df$spellerror <- ifelse(as.character(df$candidate)==as.character(df$candidatenew), 0, 1)
2 ответа
Вот мой выстрел в это. Это, вероятно, не очень эффективно, но я думаю, что это сделает работу. Я предполагаю что df$candidates
имеет классовый фактор.
#fuzzy matches candidate names to other candidate names
#compares each pair of names only once
##by looking at names that have a greater index
matches <- unlist(lapply(1:(length(levels(df[["candidate"]]))-1),
function(x) {max(x,x + agrep(
pattern=levels(df[["candidate"]])[x],
x=levels(df[["candidate"]])[-seq_len(x)]
))}
))
#assigns new levels (omits the last level because that doesn't change)
levels(df[["candidate"]])[-length(levels(df[["candidate"]]))] <-
levels(df[["candidate"]])[matches]
Хорошо, учитывая, что основное внимание уделяется эффективности, я бы предложил следующее.
Во-первых, обратите внимание, что в порядке эффективности из первых принципов мы могли бы предсказать, что точное сопоставление будет намного быстрее, чем grep, который будет быстрее, чем нечеткий grep. Итак, точное совпадение, а затем нечеткий grep для оставшихся наблюдений.
Во-вторых, векторизация и избежать петель. apply
Команды не обязательно быстрее, поэтому придерживайтесь нативной векторизации, если можете. Все grep
Команды изначально векторизованы, но будет трудно избежать *ply
или цикл, чтобы сравнить каждый элемент с вектором других, чтобы соответствовать.
В-третьих, используйте внешнюю информацию, чтобы сузить проблему. Делайте нечеткие сопоставления только с именами в каждом городе или штате, что, к примеру, значительно сократит количество сравнений, которые необходимо выполнить.
Вы можете комбинировать первый и третий принципы: вы можете даже попробовать точное совпадение по первому символу каждой строки, а затем нечеткое совпадение внутри него.