【问题标题】:See which vector in a list is contained within a vector from another list (finding people's name matches)查看列表中的哪个向量包含在另一个列表中的向量中(查找人名匹配)
【发布时间】:2019-01-02 18:30:57
【问题描述】:

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

到目前为止我做了什么:

#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"))

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

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() 用于模糊匹配以解决名称中的拼写错误的指示,那就太好了!如果没有,那也没关系,因为我想至少先找到匹配的部分。

【问题讨论】:

  • Wewe wajua 斯瓦希里语? Hayo majina nayajua。
  • @Onyambu Ndiyo, lakini najua Kidogo。 Wakati uliopita,niliishi 内罗毕。 Sasa nimesahau maneno mengi...
  • 你说话流利..没关系..我确实回答了你的问题。只是我不明白你到底想匹配什么
  • 如果您知道任何公司/组织有统计工作要做,例如数据分析、挖掘等,您可以给他们我的联系方式
  • @Onyambu 是的,确实如此。我将您的答案选为最有帮助的答案,因为我可以轻松修改它以使用 agrep 进行模糊匹配。

标签: r list match sapply


【解决方案1】:

由于您正在处理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" 

现在如果你想链接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。处理字符向量而不是列表对您来说会很棒:

【讨论】:

    【解决方案2】:

    编辑我已修改解决方案以满足“John John”等重复名称不应与“John Smith”匹配的约束。

    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% 和 apply 函数,但它现在执行一种反向搜索 - 对它查看的 first_last 名称中的每个元素 在full_names 列表中的每个名称中有多少字匹配。如果此数字大于或等于正在考虑的 first_list 名称项中的单词数(在您的示例中始终为 2 个单词,但代码适用于任何数字),它返回 TRUE .然后这个逻辑数组与ANY 聚合,以传回单个向量,显示每个 first_last 是否与任何 full_name 匹配。

    因此,例如,“John John”不会与“John Smith Random”匹配,因为“John Smith Random”中的 3 个单词中只有 1 个匹配。但是,它匹配到“John Adam John”,因为“John Adam John”中的 3 个单词中有 2 个匹配,并且 2 等于“John John”的长度。它也会与“John John John John John”匹配,因为 5 个单词中有 5 个匹配,大于 2。

    【讨论】:

    • 对不起,如果我不清楚。我不希望“John John”与“John Smith”匹配,这就是我不能使用 %in% 的原因。然而,这给了我一个想法——也许我可以先浏览这些名字,然后在一个人的名字中发现的任何重复部分附加一些后缀。 (例如,将“John John”转换为“John John1”)。这样我想我也能捕捉到重复。
    • @SaewonPark 现在明白了。我已经编辑了一个可以在这个约束下工作的解决方案。
    【解决方案3】:

    代替 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)])
    

    【讨论】:

    • 谢谢。我没有使用 %in% 的原因是它没有考虑重复。有些人的名字和姓氏相同,我想找到一个包含重复的向量的匹配项(前约翰·约翰会与约翰·史密斯匹配,尽管他们不应该匹配)。你能解释一下为什么数据框可能更有益吗?
    • 我明白了。如果您使用 dfs,您可以检查第一个中的每个名称与第二组中的每个名称(不完美):# find row numbers that have either the first name or the last namematch1 &lt;- sapply(first_last_df$first,function(x){match(x,full_name_df$first)})match2 &lt;- sapply(first_last_df$last,function(x){match(x,full_name_df$last)})match3 &lt;- sapply(first_last_df$first,function(x){match(x,full_name_df$last)})match4 &lt;- sapply(first_last_df$last,function(x){match(x,full_name_df$first)})first_last_df[!is.na(match1==match2 | match3==match4),]
    • 对不起,这里完全破坏了格式。要点是匹配向量为您提供数据框中每个名称匹配的行索引。 match1 和 2 假设名字和姓氏正确存储在每个数据帧中,match3 和 4 将其切换。最后一行中的行选择器检查 1 和 2 或 3 和 4 的这些索引是否相同,即是否在数据框的同一行中找到了名字和姓氏,这意味着他们在两个列表中。
    【解决方案4】:

    或者你可以这样做:

    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% 完全按照您的要求执行,它会检查完整的名称向量是否匹配。

    【讨论】:

    • 再次感谢。如果我未能找到更优雅的方式来做到这一点,这类似于我的方式。实际的代码会比这更长,因为有些人的中间名是用他们的名字或姓氏交换的,所以我不能删除中间名。我会置换得到这三个名字的所有组合(基本上是 P(3,2))。
    猜你喜欢
    • 1970-01-01
    • 2021-01-07
    • 1970-01-01
    • 2015-03-31
    • 2013-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多