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

8

我正在使用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”和“PAULO CESAR FERREIRA DE ARAUJO”(拼写错误)都出现了两次。整个数据集中的每个候选人应分配唯一的数字候选人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)

编辑 2:现在运行速度良好。问题是每一步都与许多因素进行比较(感谢Blue Magister指出这一点)。将比较仅限于一个组(即一个城市)中的候选人可以在5秒内对80,000行运行命令 - 这是我可以接受的速度。

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)

你目前尝试了什么?问题是关于如何让agrep匹配还是如何高效地进行匹配? - Ari B. Friedman
1
主要是效率问题。我已经遍历了所有城市和候选人,但这需要很长时间。我能够在每个城市中找到模糊匹配,但在整个数据集中创建唯一的ID时遇到了困难。 - thomasB
你能发一下你的循环代码吗?另外请参阅https://dev59.com/yXE85IYBdhLWcg3wLAQV#8474941。 - Ari B. Friedman
жҲ‘зҡ„еҫӘзҺҜжҳҜ || 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 <- as.numeric(as.factor(paste(df$match,"-",data$citycode,sep=""))) || 然еҗҺпјҲйҖҡиҝҮеҫӘзҺҜпјүдёәжҜҸдёӘеҹҺеёӮйҮҚеӨҚгҖӮ - thomasB
2个回答

4

这是我的尝试。它可能不太高效,但我认为它可以完成任务。我假设df$candidates的类别是因子(factor)。

#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]

第一部分很好。第二部分似乎无法运行。无论如何,感谢您让我开始。也许您有建议如何改进我添加到问题中的“解决方案”。 - thomasB
我更新了一些我认为更好的东西。你不需要通过那么多元素进行agrep。它在样本数据集上运行良好,但我不确定它在你的大型数据集上的表现如何。 - Blue Magister
非常感谢。确实,与许多级别进行比较是问题所在。现在,我将比较限制在每个组内的级别,并且速度快了数百倍。 - thomasB
如果您允许,关于您之前的解决方案,我有一个最后的问题。在以下这行代码中,我能否添加一些选项到agrep命令:lapply(levels(x), agrep, x=levels(x),fixed=TRUE, value=FALSE) 我想指定“max.distance”。 - thomasB
1
是的:lapply(levels(x), agrep, x=levels(x),fixed=TRUE, value=FALSE, max.distance = .2)。 (请注意,您可以使用反引号```将文本格式化为代码。) - Blue Magister

3

好的,考虑到效率问题,我建议如下。

首先,需要注意从基本原则上来说,我们可以预测精确匹配比grep要快得多,而grep比模糊匹配更快。因此,优先采用精确匹配,然后采用模糊匹配处理剩余观察数据。

其次,向量化处理并避免使用循环。如果可以,请使用本地向量化,而不是apply命令。所有grep命令都具有本地向量化,但很难避免使用*ply或循环将每个元素与其他向量进行比较以进行匹配。

第三,利用外部信息缩小问题范围。例如,在每个城市或州内仅对名称进行模糊匹配,从而大大减少必须进行的比较次数。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接