Работа с неправильным написанием при сопоставлении текстовых строк в 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) для проверки человеком. Вы должны поиграть с этими числами и посмотреть, что работает для вас. Эти значения будут определять ваш компромисс между чувствительностью и специфичностью.