通过模糊匹配名称创建唯一ID(通过使用R的agrep)

通过模糊匹配名称创建唯一ID(通过使用R的agrep),r,string-matching,fuzzy,agrep,R,String Matching,Fuzzy,Agrep,使用R,我尝试在按年份和城市构建的数据集中匹配人名。由于一些拼写错误,无法精确匹配,因此我尝试使用agrep()模糊匹配名称 数据集的样本块的结构如下所示: df <- data.frame(matrix( c("1200013","1200013","1200013","1200013","1200013","1200013","1200013",&

使用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")) ))
我想分别在每个城市登记,几年后是否有候选人出现。例如,在示例中

保罗·塞扎·费雷拉·德阿劳乔

保罗·塞萨尔·费雷拉·德阿劳乔

出现两次(有拼写错误)。整个数据集中的每个候选对象都应分配一个唯一的数字候选ID。数据集相当大(5500个城市,约100K个条目),因此某种程度上高效的编码可能会有所帮助。有没有关于如何实施的建议

编辑:以下是我的尝试(在目前评论的帮助下),它在完成手头的任务方面非常缓慢(低效)。对此有何改进建议

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)

f这是我的尝试。这可能不是很有效,但我认为它会完成工作。我假设
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]
#fuzzy将候选名称与其他候选名称进行匹配
#仅对每对名称进行一次比较
##通过查看索引较大的名称

匹配好的,考虑到重点是效率,我建议如下

首先,请注意,根据第一原理的效率顺序,我们可以预测精确匹配将比grep快得多,grep将比模糊grep快得多。所以精确匹配,然后对剩余的观测值进行模糊grep

第二,矢量化和避免循环。
apply
命令并不一定更快,因此如果可以,请坚持使用本机矢量化。所有的
grep
命令都是本机矢量化的,但是很难避免使用
*ply
或循环将每个元素与其他元素的矢量进行比较

第三,利用外部信息缩小问题范围。例如,只对每个城市或州的名字进行模糊匹配,这将大大减少必须进行的比较


您可以结合第一和第三原则:您甚至可以尝试对每个字符串的第一个字符进行精确匹配,然后在其中进行模糊匹配。

到目前为止您尝试了什么?让agrep与之匹配或有效地进行匹配有问题吗?主要是效率方面的问题。我在所有城市和候选人中进行了一次循环,但这需要相当长的时间。我能够在每个城市中找到模糊匹配,但很难在整个数据集中创建唯一的ID。你能为你的循环发布代码吗?另请参见.My循环是| | for(i in 1:dim(df)[1]){df$match[i]=sort(agrep(df$candidate[i],df$candidate,ignore.case=FALSE,value=TRUE,max.distance=0.1))[1]}df$candid第一部分很棒。第二部分似乎没有运行。无论如何,谢谢你让我开始。也许你有一个关于如何改进我添加到问题中的“解决方案”的建议。我已经更新了一些我认为更好的东西。您不必通过尽可能多的元素执行
agreep
。它在样本数据集上工作,但我“我不确定它在更大的数据集上的效果如何。非常感谢。与许多层次的比较确实是个问题。现在,我将比较限制在每个组内的级别,速度快了数百倍。如果您允许,最后一个问题是关于您以前的解决方案。我可以在行中为agrep命令添加一些选项吗:lappy(levels(x),agrep,x=levels(x),fixed=TRUE,value=FALSE)我想指定“max.distance”。是:
lappy(levels(x),agrep,x=levels(x),fixed=TRUE,value=FALSE,max.distance=.2)
。(注意,您可以使用反引号
`
将文本格式化为代码)
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)
#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]