【问题标题】:Matching strings within two lists匹配两个列表中的字符串
【发布时间】:2022-01-19 12:47:42
【问题描述】:

问题来了: 我想定义一个函数,该函数将在 2 个列表(大小不同)中使用 blur.ration() 比较字符串比率。 它应该返回列表 1 中的实体,这些实体与第二个相比,至少有一个大于 60 的比率。

def Matching(list1, list2):
    no_matching = []
    matching = []
    for item1 in list1:    
        for item2 in list2:        
            m_score = fuzz.ratio(item1, item2)
        if m.score < 60:
            no_matching.append(item1)
        if m.score > 60:
            matching.append(item1)
    return(matching, no_matching)

输出不是我的目标。 我在哪一部分做错了 - 为了只获取列表 1 中的项目,如果它们与列表 2 至少有一个匹配 大于 60.

例如:

list1 = ["Real Madrid", "Benfica", "Lazio", "FC Milan"]
list2 = ["Madrid", "Barcelona", "Milan"]

for item1 in list1:
    for item2 in list2:
        m_score = fuzz.ratio(item1, item2)
        print(item1, "&", item1, m_score)

输出是:

Real Madrid & Madrid 71 # greater than 60
Real Madrid & Barcelona 20
Real Madrid & Milan 12
Benfica & Madrid 15
Benfica & Barcelona 50
Benfica & Milan 17
Lazio & Madrid 36
Lazio & Barcelona 29
Lazio & Milan 20
FC Milan & Madrid 29
FC Milan & Barcelona 24
FC Milan & Milan 77 # greater than 60

函数输出应该是:

matching = ["Real Madrid", "FC Milan"] # since they have at least one ratio bigger than 60
no_matching = ["Benfica", "Lazio"]

【问题讨论】:

  • 您当前的输出是多少?你只得到 list1 中最后一个匹配的元素吗?
  • @KabilanMohanraj 我编辑了问题
  • 你可以查看我的答案

标签: python string list function


【解决方案1】:

特别是考虑到您对在大型列表 (10.000 * 1.200) 上执行此操作的评论,我建议使用 RapidFuzz(我是作者)。使用RapidFuzz 的解决方案可以通过以下方式实现:

from rapidfuzz import process, fuzz
import numpy as np

list1 = ["Real Madrid", "Benfica", "Lazio", "FC Milan"]
list2 = ["Madrid", "Barcelona", "Milan"]

scores = process.cdist(
    list1, list2, scorer=fuzz.ratio,
    dtype=np.uint8, score_cutoff=60)
# scores is array([[71,  0,  0],
#                  [ 0,  0,  0],
#                  [ 0,  0,  0],
#                  [ 0,  0, 77]], dtype=uint8)

matches = np.any(scores, 1)
# matches is array([ True, False, False,  True])

这仍然处理整个 N*M 矩阵,但它比使用 fuzzywuzzy/thefuzz 执行相同操作要快得多。当处理非常大的列表时,可以通过传递命名参数workers(例如workers=-1 以使用所有可用内核)在process.cdist 中启用多线程。如果需要,上面的结果可以转换为您在示例中显示的列表:

matching = [x for x, is_match in zip(list1, matches) if is_match]
# ['Real Madrid', 'FC Milan']
not_matching = [x for x, is_match in zip(list1, matches) if not is_match]
# ['Benfica', 'Lazio']

我使用两个大列表 (10.000 * 1.200) 在 i7-8550U 上对该解决方案进行了基准测试:

print(timeit(
"""
scores = process.cdist(
    list1, list2, scorer=fuzz.ratio,
    dtype=np.uint8, score_cutoff=60)

matches = np.any(scores, 1)

matching = [x for x, is_match in zip(list1, matches) if is_match]
not_matching = [x for x, is_match in zip(list1, matches) if not is_match]
""",
setup="""
from rapidfuzz import process, fuzz
import numpy as np

list1 = ["Real Madrid", "Benfica", "Lazio", "FC Milan"] * 2500
list2 = ["Madrid", "Barcelona", "Milan"] * 400
""", number=1
))

耗时 0.33 秒。使用workers=-1 将运行时间缩短至 0.08 秒。

【讨论】:

  • 这对我来说是个好发现
  • 很好。干得好!
【解决方案2】:

list1 和 list2 中有重复的组合,它们在 no_matching 列表中创建了副本。检查元素是否已经在匹配列表中。如果是,请不要添加到no_matching 列表中。下面的代码给出了预期的输出。

from fuzzywuzzy import fuzz

def Matching(list1, list2):
    no_matching = []
    matching = []
    m_score = 0
    for item1 in list1:    
        for item2 in list2:        
            m_score = fuzz.ratio(item1, item2)
            if m_score > 60:
                matching.append(item1)
        if m_score < 60 and not(item1 in matching):
            no_matching.append(item1)
    return(matching, no_matching)


list1 = ["Real Madrid", "Benfica", "Lazio", "FC Milan"]
list2 = ["Madrid", "Barcelona", "Milan"]
print(Matching(list1, list2))

输出:

(['Real Madrid', 'FC Milan'], ['Benfica', 'Lazio'])

【讨论】:

  • 效果很好!您对使用更大的列表进行相同的计算有什么建议吗?
  • @feration48 列表有多大?当前的解决方案看起来并不理想。
  • 对于 10.000 个实例的 list1 和 1200 个实体的 list2。以下函数将迭代 1200 万次:-/
  • 是的,如果我们可以减少生成的组合数量,我们会得到更好的解决方案。
  • 意味着应该替换蛮力方法...。您是否遇到过类似问题?在这种情况下,recordlinkage 包更好吗?
【解决方案3】:

编辑: 而不是 run 2 for 循环,您可以遍历所有组合:

import itertools
new_list = list(itertools.product(list1, list2))

输出:

[('Real Madrid', 'Madrid'), ('Real Madrid', 'Barcelona'), ('Real Madrid', 'Milan'), ('Benfica', 'Madrid'), ('Benfica', 'Barcelona'), ('Benfica', 'Milan'), ('Lazio', 'Madrid'), ('Lazio', 'Barcelona'), ('Lazio', 'Milan'), ('FC Milan', 'Madrid'), ('FC Milan', 'Barcelona'), ('FC Milan', 'Milan')]

你有缩进的问题:

from fuzzywuzzy import fuzz

def Matching(list1, list2):
    no_matching = []
    matching = []
    m_score = 0
    for item1 in list1:    
        for item2 in list2:        
            m_score = fuzz.ratio(item1, item2)
            if m_score > 60:
                matching.append(item1)
        if m_score < 60 and not(item1 in matching):
            no_matching.append(item1)
    return(matching, no_matching)

【讨论】:

  • 是的,是的,但是您跳过了我测量字符串之间的距离并获得比率的部分(fuzzy.ration 返回数值)。问题似乎是将特定实体存储到列表中。
  • 看看下面的编辑
  • 现在看@feration48
  • 这会在no_matching 列表中提供副本。
  • @TalFolkman 匹配列表很好。如何避免在 no_match 列表中重复复制?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-12
  • 2018-12-07
  • 2020-09-04
  • 2020-04-28
相关资源
最近更新 更多