Работа с неправильным написанием при сопоставлении текстовых строк в R

Я собираю данные опросов (используя комплект открытых данных), и моя полевая команда, благословляя их сердца, иногда проявляет творческий подход к написанию имен людей. Поэтому у меня есть "правильное" имя респондента, а также переменная возраста для некоторых записей, связанных с переменной "имя члена семьи". Есть много членов семьи разных возрастов. Я хочу респондентский возраст.

Вот некоторые поддельные данные, которые иллюстрируют мою проблему:

#the respondent
    r = data.frame(name = c("Barack Obama", "George Bush", "Hillary Clinton"))
#a male member
    m = data.frame(name = c("Barack Obama","George", "Wulliam Clenton"), age = c(55,59,70)); m$name=as.character(m$name)
#a female member
    f = data.frame(name = c("Michelle O","Laura Busch", "Hillary Rodham Clinton"), age = c(54,58,69)); f$name=as.character(f$name)
#if the responsent is the the given member, record their age.  if not, NA
    a = cbind(
        ifelse(r$name==m$name,m$age,NA)
        ,ifelse(r$name==f$name,f$age,NA)
        )
    #make a function for plyr that gives me the age of the matched respondent
    f = function(row){
        d = row[is.na(row)==0]
        ifelse(length(d)==0,NA,d)
        }
    require(plyr)
    b = aaply(a,.margins=1,.fun=f)
    data.frame(names=r$name,age=b)
                names age
    1    Barack Obama  55
    2     George Bush  NA
    3 Hillary Clinton  NA

    what.I.would.like = data.frame(names=c("Barack Obama", "George Bush", "Hillary Clinton"),age = c(55,59,70))
    1> what.I.would.like
                names age
    1    Barack Obama  55
    2     George Bush  59
    3 Hillary Clinton  70

по моим реальным данным, у меня сотни людей и до 13 членов семьи. С тех пор я изменил опрос, чтобы записать возраст респондента отдельно, но у меня есть куча данных для очистки.

2 ответа

Решение

Проблемы с орфографией обычно решаются с помощью некоторого варианта алгоритма soundex. В пакете RecordLinkage есть реализация R. Тогда вам нужно сравнить не сами строки, а их "фонетические коды":

> soundex('Clenton') == soundex('Clinton')
[1] TRUE

ОБНОВЛЕНИЕ: Есть также другой способ определить, являются ли два слова "близкими" друг к другу - это "расстояние" в некотором смысле между словами. Одна стандартная мера расстояний - это минимальное количество однобуквенных замен, удалений и вставок, необходимых для преобразования первого слова во второе. Это называется расстояние Левенштейна. RecordLinkage, а также пакет vwr имеют соответствующие функции:

> levenshteinDist('Clinton', 'Clenton')
[1] 1

> vwr::levenshtein.distance('Clinton', 'Clenton')
Clenton 
  1 

Тогда вы можете использовать расстояния и считать слова "близко" достаточно, если расстояние не превышает некоторый порог.

Я рекомендую использовать расстояние Джаро-Винклера, метрику подобия строк, разработанную для решения этой конкретной проблемы в данных переписи населения США. Он более сложный, чем расстояние Левенштейна, и разработан специально для работы с именами. Вы можете найти реализацию R в пакете RecordLinkage. Вам нужно будет установить пороговое значение (например, 0,8) для того, насколько похожими должны быть две строки.

install.packages('RecordLinkage','RSQLite')
require(RecordLinkage)

jarowinkler('William Clinton', "Willam Clntn")
# 0.96
jarowinkler('William Clinton', "Wuliam Clinton")
# 0.8462637
jarowinkler('William Clinton', "Hilary Clinton")
# 0.7790765

Я бы рекомендовал установить разумно высокий порог (возможно, 0,9) для автоматического сопоставления, а затем отправлять записи ниже верхнего порога, но выше вторичного нижнего порога (возможно, 0,7) для проверки человеком. Вы должны поиграть с этими числами и посмотреть, что работает для вас. Эти значения будут определять ваш компромисс между чувствительностью и специфичностью.

Другие вопросы по тегам