查看列表中的哪个向量包含在另一个列表的向量中(查找人名匹配项)

See which vector in a list is contained within a vector from another list (finding people's name matches)

我有一个人名向量列表,其中每个向量只有名字和姓氏,我有另一个向量列表,其中每个向量都有名字、中间名和姓氏。我需要匹配两个列表以找到同时包含在两个列表中的人。因为名称不按顺序(有些向量以名字作为第一个值,而其他向量以姓氏作为第一个值),我想通过在第二个列表中查找哪个向量(全名)来匹配两个向量) 包含第一个列表中向量的所有值(仅名字和姓氏)。

到目前为止我做了什么:

#reproducible example
first_last_names_list <- list(c("boy", "boy"),
                       c("bob", "orengo"),
                       c("kalonzo", "musyoka"),
                       c("anami", "lisamula"))

full_names_list <- list(c("boy", "juma", "boy"), 
                        c("stephen", "kalonzo", "musyoka"),
                        c("james", "bob", "orengo"),
                        c("lisamula", "silverse", "anami"))

首先,我尝试创建一个函数来检查一个向量是否包含在另一个向量中(主要基于 中的代码)。

my_contain <- function(values,x){
    tx <- table(x)
    tv <- table(values)
    z <- tv[names(tx)] - tx
    if(all(z >= 0 & !is.na(z))){
       paste(x, collapse = " ")
       }
    }

#value would be the longer vector (from full_name_list) 
#and x would be the shorter vector(from first_last_name_list)

然后,我尝试将此函数放在 sapply() 中,以便我可以使用列表,这就是我被卡住的地方。我可以查看一个向量是否包含在一个向量列表中,但我不确定如何检查一个列表中的所有向量并查看它是否包含在第二个列表中的任何向量中。

#testing with the first vector from first_last_names_list. 
#Need to make it run through all the vectors from first_last_names_list.

sapply(1:length(full_names_list),
   function(i) any(my_contain(full_names_list[[i]], 
                              first_last_names_list[[1]]) == 
                              paste(first_last_names_list[[1]], collapse = " ")))

#[1]  TRUE FALSE FALSE FALSE

最后 - 尽管在一个问题中提出的问题可能太多了 - 如果有人能给我任何关于如何合并 agrep() 进行模糊匹配以解决名称中的拼写错误的指示,那就太好了!如果没有,那也没关系,因为我想至少先把匹配的部分弄对。

而不是my_contain,试试

x %in% values

也许还取消列出并使用数据框?不确定您是否考虑过它——可能会让事情变得更容易:

# unlist to vectors
fl <- unlist(first_last_names_list)
fn <- unlist(full_names_list)

# grab individual names and convert to dfs; 
# assumptions: first_last_names_list only contains 2-element vectors
#              full_names_list only contains 3-element vectors
first_last_df <- data.frame(first_fl=fl[c(T, F)],last_fl=fl[c(F, T)])
full_name_df <- data.frame(first_fn=fn[c(T,F,F)],mid_fn=fn[c(F,T,F)],last_fn=fn[c(F,F,T)])

或者您可以这样做:

first_last_names_list <- list(c("boy", "boy"),
                          c("bob", "orengo"),
                          c("kalonzo", "musyoka"),
                          c("anami", "lisamula")) 

full_names_list <- list(c("boy", "juma", "boy"), 
                    c("stephen", "kalonzo", "musyoka"),
                    c("james", "bob", "orengo"),
                    c("lisamula", "silverse", "anami"),
                    c("musyoka", "jeremy", "kalonzo")) # added just to test

# create copies of full_names_list without middle name; 
# one list with matching name order, one with inverted order
full_names_short <- lapply(full_names_list,function(x){x[c(1,3)]})
full_names_inv <- lapply(full_names_list,function(x){x[c(3,1)]})

# check if names in full_names_list match either
full_names_list[full_names_short %in% first_last_names_list | full_names_inv %in% first_last_names_list]

在这种情况下 %in% 完全按照您的要求执行,它会检查完整的名称向量是否匹配。

Edit 我修改了解决方案以满足 'John John' 等重复名称不应与 [=35= 匹配的约束].

apply(sapply(first_last_names_list, unlist), 2, function(x){
        any(sapply(full_names_list, function(y) sum(unlist(y) %in% x) >= length(x)))
    })

此解决方案仍然使用 %in% 和应用函数,但它现在进行一种反向搜索 - 对于它所查找的 first_last 名称中的每个元素 full_names 列表中的每个名称中有多少个单词 是匹配的。如果此数字 大于或等于 正在考虑的 first_list 名称项中的单词数(在您的示例中始终为 2 个单词,但该代码适用于任何数字),它 returns 正确。然后将此逻辑数组与 ANY 聚合以传回单个向量,显示每个 first_last 是否与任何 full_name.

匹配

因此,例如,'John John' 不会 与 'John Smith Random' 匹配,因为 'John Smith Random' 中的 3 个单词中只有 1 个匹配.但是,它匹配到'John Adam John',因为'John Adam John'中的3个单词中有2个匹配,2等于[=34=的长度].它还会与 'John John John John John' 匹配,因为 5 个单词中有 5 个匹配,大于 2.

既然你正在处理 lists 最好将它们折叠成向量以便于处理正则表达式。但是你只是按升序排列它们。在这种情况下,您可以轻松匹配它们:

lst=sapply(first_last_names_list,function(x)paste0(sort(x),collapse=" "))
 lst1=gsub("\s|$",".*",lst)
 lst2=sapply(full_names_list,function(x)paste(sort(x),collapse=" "))
 (lst3 = Vectorize(grep)(lst1,list(lst2),value=T,ignore.case=T))
               boy.*boy.*             bob.*orengo.*        kalonzo.*musyoka.*         anami.*lisamula.* 
           "boy boy juma"        "bob james orengo" "kalonzo musyoka stephen" "anami lisamula silverse" 

现在如果你想 link first_name_last_name_listfull_name_list 那么:

setNames(full_names_list[ match(lst3,lst2)],sapply(first_last_names_list[grep(paste0(names(lst3),collapse = "|"),lst1)],paste,collapse=" "))
$`boy boy`
[1] "boy"  "juma" "boy" 

$`bob orengo`
[1] "james"  "bob"    "orengo"

$`kalonzo musyoka`
[1] "stephen" "kalonzo" "musyoka"

$`anami lisamula`
[1] "lisamula" "silverse" "anami"   

其中名称来自 first_last_list,元素来自 full_name_list。处理字符向量而不是列表对你来说会很棒: