Python算法模糊匹配:FuzzyWuzzy深度剖析,从入门到精通,解决你所有需要匹配的需求

在数据科学与机器学习的广阔领域中,处理不精确或模糊的数据是一项至关重要的技能。想象一下,当你面对的是一堆拼写错误、缩写、或是格式不一的文本数据时,如何高效地从中提取有价值的信息?这正是FuzzyWuzzy——Python中一个强大的模糊字符串匹配库,能够大展身手的地方。

本文将为你全面介绍FuzzyWuzzy——Python中一个强大的通过算法模糊字符串匹配库,全网最全最新,一看就会,接下来带你走进FuzzyWuzzy的神奇世界

一、引言

FuzzyWuzzy是一个强大的Python库,专门用于执行模糊字符串匹配和相似度比较。它基于Levenshtein距离(编辑距离)算法,能够处理字符串之间的拼写错误、格式差异以及部分匹配等问题,非常适合在数据清洗、文本匹配、搜索引擎优化等场景中使用。本文将详细介绍FuzzyWuzzy的功能参数、属性以及通过案例代码展示其使用方法。

在处理数据的过程中,难免会遇到下面类似的场景,自己手里头获得的是简化版的数据字段,但是要比对的或者要合并的却是完整版的数据(有时候也会反过来)

最常见的一个例子就是:在进行地理可视化中,自己收集的数据只保留的缩写,比如北京,广西,新疆,西藏等,但是待匹配的字段数据却是北京市,广西壮族自治区,新疆维吾尔自治区,西藏自治区等,如下。因此就需要有没有一种方式可以很快速便捷的直接进行对应字段的匹配并将结果单独生成一列,就可以用到FuzzyWuzzy库。

Python算法模糊匹配:FuzzyWuzzy深度剖析,从入门到精通,解决你所有需要匹配的需求_第1张图片

二、FuzzyWuzzy介绍

FuzzyWuzzy 是一个简单易用的模糊字符串匹配工具包。它依据 Levenshtein Distance 算法,计算两个序列之间的差异。

Levenshtein Distance算法,又叫 Edit Distance算法,是指两个字符串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。一般来说,编辑距离越小,两个串的相似度越大。

这里使用的是Anaconda下的jupyter notebook编程环境,因此在Anaconda的命令行中输入一下指令进行第三方库安装。

pip install FuzzyWuzzy

三、功能参数与属性

FuzzyWuzzy库主要包含两个模块:fuzzprocess

1、fuzz模块

fuzz模块主要用于计算两个字符串之间的相似度。

函数 作用 描述
fuzz.ratio(s1, s2) 简单匹配 计算两个字符串之间的相似度,返回一个介于0到100之间的数值,数值越高表示相似度越高。
fuzz.partial_ratio(s1, s2) 非完全匹配 部分匹配,不考虑字符串的顺序,仅匹配部分字符串。如果s1是s2的子串,依然返回100。
fuzz.token_sort_ratio(s1, s2) 忽略顺序匹配 忽略单词顺序,比较两个字符串中单词的相似度。以空格为分隔符,小写化所有字母,忽略其他标点符号。
fuzz.token_set_ratio(s1, s2) 去重子集匹配 将字符串视为单词集合,计算两个集合的相似度,不考虑单词的顺序和重复。

 

2、process模块

process模块主要用于在一组字符串中查找与给定字符串最相似的选项。

函数 作用 描述
process.extract(query, choices, limit=None) 提取多条数据 choices列表中找出与query最相似的字符串,并返回包含匹配项及其相似度得分的列表。limit参数用于限制返回结果的数量。
process.extractOne(query, choices) 提取一条数据 返回与query最相似的一个字符串及其相似度得分。

四、fuzz实战

1、简单匹配

1.1、案例1

简单匹配,这个不是特别精确,使用场景不是很多 例如河北跟河北省相差一个字。其实是一个地方, 但是根据每个字符相匹配相识度,一个字符未匹配导致结果为80%。而上海市和海南市都存在“海”和“市”,但是因为顺序不匹配导致相识度只有67%

print(fuzz.ratio('河北','河北省')) # 结果是:80,匹配相似度是80%

print(fuzz.ratio('上海市','海南市')) # 结果是:67,匹配相识度是67%

print(fuzz.ratio('河南省','河南省')) # 结果是:100% 匹配相识度是100%
1.2、案例2

最佳使用场景:

  1. 简单的拼写检查:当需要快速判断两个字符串是否非常接近,且对位置不敏感时。
  2. 初步的数据清洗:在数据清洗的初步阶段,用于识别可能相似的记录。
  3. 简单的文本匹配任务:在不需要考虑字符顺序或编辑距离的场景中,进行快速的文本匹配。
from fuzzywuzzy import fuzz

# 定义两个字符串,它们可能相似也可能不相似
s1 = "quick brown fox"
s2 = "the quick brown fox jumps"

# 使用fuzz.ratio计算两个字符串的相似度
# 这个函数会逐字符比较两个字符串,并计算它们之间相同字符的比例
# 它不考虑字符的位置变化,只关注字符的匹配情况
similarity_score = fuzz.ratio(s1, s2)

# 打印相似度得分
print(f"相似度: {similarity_score}")

# 注释:
# fuzz.ratio是一种非常基础的字符串相似度计算方法,它逐字符地比较两个字符串,
# 并计算相同字符所占的比例。这种方法不考虑字符的顺序、重复或任何形式的编辑距离,
# 只关注两个字符串中字符的直接匹配情况。


# 输出结果解释:
# 在这个例子中,s1和s2的前半部分("quick brown fox")是相同的,但s2还包含额外的单词("jumps")。
# fuzz.ratio会计算两个字符串中相同字符的比例,不考虑它们的位置。
# 因此,尽管"jumps"不在s1中,但s1中的字符在s2中都能找到匹配(除了额外的空格和"the"),
# 所以相似度得分会基于相同字符的比例来计算。
# 在这个特定例子中,由于s1的所有字符都在s2中找到了匹配(尽管s2还有额外的字符),
# 相似度得分会相对较高,但会低于100(因为s2比s1长)。
# 注意:实际得分可能会受到实现细节(如空格处理、大小写敏感性等)的影响。

注意事项

  • fuzz.ratio是一种非常简单的相似度计算方法,它不考虑字符的位置、顺序或编辑距离。因此,在某些情况下,它可能会给出误导性的结果。
  • 该函数默认对大小写敏感,并逐字符进行比较。如果需要忽略大小写或进行其他类型的预处理(如去除标点符号、空格等),请在比较之前对字符串进行相应的处理。
  • 由于fuzz.ratio只关注字符的直接匹配情况,因此在处理包含大量重复字符或模式相似的字符串时,它可能不是最佳选择。在这些情况下,可能需要考虑使用更复杂的相似度计算方法,如fuzz.token_sort_ratiofuzz.token_set_ratiofuzz.partial_ratio等。

2、非完全匹配

2.1、案例1

非完全匹配 这个函数使用的场景相对比较多, 通过结果可以看出,对于像河北跟河北省这样全名称和简易名称的匹配直接是100%

print(fuzz.partial_ratio('河北','河北省')) # 结果是:100,匹配相似度是100%

print(fuzz.partial_ratio('上海市','海南市')) # 结果是:67,匹配相似度是67%

print(fuzz.partial_ratio('河南省','河南省')) # 结果是:100,匹配相似度是100%
2.2、案例2

最佳使用场景:

  1. 拼写检查:判断用户输入的单词或短语是否接近字典中的某个单词或短语。
  2. 自动补全:在用户输入时,根据已输入的部分推荐最匹配的完整单词或短语。
  3. 文本摘要或关键词提取后的匹配:在大量文本中查找与给定关键词或短语最匹配的句子或段落。
from fuzzywuzzy import fuzz

# 定义两个字符串,其中一个字符串是另一个字符串的部分或相似片段
s1 = "quick brown fox"
s2 = "the quick brown fox jumps over the lazy dog"

# 使用fuzz.partial_ratio计算两个字符串的相似度
# 这个函数会寻找s1在s2中最长的连续公共子串,并基于这个子串计算相似度
# 它不考虑s1和s2之外的部分,只关注s1与s2之间的最佳匹配部分
similarity_score = fuzz.partial_ratio(s1, s2)

# 打印相似度得分
print(f"相似度: {similarity_score}")

# 注释:
# fuzz.partial_ratio非常适合于那些需要评估一个字符串是否是另一个字符串的部分或相似片段的场景。
# 它通过寻找两个字符串之间的最长连续公共子串来计算相似度,这个子串必须是s1的连续部分。
# 这种方式特别适用于拼写检查、自动补全、文本摘要中的关键词匹配等场景。


# 输出结果解释:
# 在这个例子中,s1是s2的一个连续子串("quick brown fox")。
# fuzz.partial_ratio会找到这个最长的连续公共子串,并基于这个子串的长度来计算相似度。
# 因为s1完全包含在s2中,所以相似度得分会非常高(在这个例子中应该是100,表示完全匹配)。
# 如果s1只是s2的一部分,但非常相似(例如,只有一两个字符的差异),相似度得分也会很高,但会低于完全匹配的情况。

注意事项

  • fuzz.partial_ratio只关注s1在s2中的最长连续公共子串,不考虑s2中剩余的部分。因此,如果s2比s1长得多,但s1只是s2中的一个小片段,这个函数仍然能给出较高的相似度得分。
  • 该函数默认忽略大小写,并基于空格来分割字符串成单词(尽管在连续子串的匹配中,单词边界不是关键因素)。如果需要处理非空格分隔符或保留大小写,可能需要进行预处理。
  • 在某些情况下,如果s1和s2之间存在多个较长的连续公共子串,但没有一个完全覆盖s1,fuzz.partial_ratio只会选择其中一个来计算相似度,而不是所有可能匹配的子串的平均值或最大值。这可能会影响相似度得分的解释性,但通常对于大多数应用场景来说已经足够。

3、忽略顺序匹配

3.1、案例1

忽略单词顺序,比较两个字符串中单词的相似度。以空格为分隔符,小写化所有字母,忽略其他标点符号。

可以看到跟简单匹配的区别就是简单匹配会匹配字符的顺序,如果顺序不同也会影响最终判断结果,但是token_dort_ratio会忽略顺序,并且将字符小写然后匹配字符相识度,很适用于两个字符顺序不同或者大小写不同以及标点符号不同的场景

print(fuzz.ratio('西藏 自治区', '自治区 西藏')) # 结果是:50,匹配相似度是50%

print(fuzz.ratio('I love YOU','YOU LOVE I')) # 结果是:30,匹配相似度是30%

print(fuzz.token_sort_ratio("西藏 自治区", "自治区 西藏")) # 结果是:100,匹配相似度是100%

print(fuzz.token_sort_ratio('I love YOU','YOU LOVE I')) # 结果是:100,匹配相似度是100%
3.2、案例2

最佳使用场景:  

  1. 文本摘要或关键词提取后的相似度比较,当关键词的顺序不重要时。  
  2. 数据清洗中,当需要合并或去重包含相似内容但顺序不同的记录时。  
  3. 搜索引擎优化,特别是在处理用户查询和文档标题、描述等元数据的匹配时。
from fuzzywuzzy import fuzz  
  
# 定义两个字符串,它们包含相似的单词,但顺序不同  
s1 = "the quick brown fox jumps over the lazy dog"  
s2 = "lazy the jumps fox over quick dog the brown"  
  
# 使用fuzz.token_sort_ratio计算两个字符串的相似度  
# 这个函数会将字符串分割成单词(基于空格),然后将单词排序后比较两个字符串的相似度  
# 忽略单词的原始顺序,但考虑重复单词  
similarity_score = fuzz.token_sort_ratio(s1, s2)  
  
# 打印相似度得分  
print(f"相识度: {similarity_score}")  # 相识度: 100
  
# 注释:  
# fuzz.token_sort_ratio非常适合于那些需要比较两个文本中单词集合相似度,  
# 但不关心单词原始顺序的场景。它首先将字符串分割成单词,然后对这些单词进行排序,  
# 最后比较排序后的单词序列是否相同。这个函数也考虑单词的重复情况。  
    
  
# 输出结果解释:  
# 在这个例子中,s1和s2包含相同的单词,但顺序完全不同。  
# fuzz.token_sort_ratio通过先对单词进行排序,然后比较排序后的字符串,  
# 能够忽略原始顺序的差异,只关注单词及其重复情况。  
# 因此,尽管s1和s2的顺序不同,但由于它们包含相同的单词集合(包括重复),  
# 相似度得分会非常高(在这个例子中应该是100,因为两个字符串完全由相同的单词组成,只是顺序不同)。

注意事项

  • fuzz.token_sort_ratio在比较时会将字符串分割成单词,并默认忽略大小写。如果需要保留大小写或处理非空格分隔符,可能需要进行预处理。
  • 该函数考虑单词的重复情况,因此如果两个字符串中某个单词出现的次数不同,相似度得分可能会受到影响。
  • fuzz.token_set_ratio不同,fuzz.token_sort_ratio在排序后进行比较,因此它对于单词的顺序变化是敏感的,但这种敏感性仅限于比较前的排序过程。一旦排序完成,顺序就不再影响相似度得分。然而,在实际应用中,这种差异通常很小,因为大多数情况下我们关注的是单词的存在性和重复情况,而不是它们在原始字符串中的具体顺序。

4、去重子集匹配

4.1、案例

最佳使用场景

fuzz.token_set_ratio的最佳使用场景包括:

  1. 关键词匹配:在搜索或推荐系统中,当需要判断用户输入的关键词与数据库中的关键词集合是否相似时,可以使用这个函数来忽略关键词的顺序和重复。

  2. 文本分类:在文本分类任务中,如果分类的依据是文本中包含的关键词集合,而不是具体的句子结构或顺序,这个函数就非常有用。

  3. 数据清洗:在数据清洗过程中,当需要识别并合并包含相似信息但格式不同的记录时,可以使用这个函数来比较记录的关键词集合。

from fuzzywuzzy import fuzz

# 定义两个字符串,它们包含相似的单词,但顺序和重复情况可能不同
s1 = "quick brown fox jumps over the lazy dog"
s2 = "the fox jumps over the quick brown dog"

# 使用fuzz.token_set_ratio计算两个字符串的相似度
# 这个函数会将字符串分割成单词(基于空格),然后比较两个单词集合的相似度
# 忽略单词的顺序和重复
similarity_score = fuzz.token_set_ratio(s1, s2)

# 打印相似度得分
print(f"相识度: {similarity_score}") # 相识度: 100

# 注释:
# fuzz.token_set_ratio非常适合于那些需要比较两个文本集合(如单词列表)相似度,
# 但不关心单词顺序或重复次数的场景。
# 例如,在文本分类、关键词匹配或数据清洗中,当需要判断两个文本是否包含相似的信息时,
# 而不必考虑信息的具体排列方式或重复程度,这个函数就非常有用。

# 输出结果解释:
# 在这个例子中,s1和s2都包含了相同的单词('quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog'),
# 尽管它们的顺序和'dog'的重复次数不同。
# fuzz.token_set_ratio会忽略这些差异,只关注单词是否存在于两个字符串中。
# 因此,即使顺序和重复次数不同,只要单词集合相同,相似度得分就会很高(在这个例子中接近100,具体取决于实现细节)。
# 注意:由于实现细节(如小写化、标点符号处理等)可能略有不同,实际得分可能略有差异。

注意事项

  • fuzz.token_set_ratio默认基于空格分割字符串为单词,并忽略大小写。如果字符串中包含非空格的分隔符或需要保留大小写,可能需要进行额外的预处理。
  • 由于这个函数只关注单词集合的相似度,而不考虑单词的顺序或上下文,因此在某些情况下可能会产生误导性的结果。例如,当两个字符串包含完全不同的句子但恰好包含相同的单词集合时,这个函数会给出较高的相似度得分。因此,在使用时需要结合具体场景和需求进行评估。

五、process实战

1、提取多条数据

1.1、案例1

extract用于提取多条数据,返回结果是一个列表,extract会把相匹配的数据全部返回出来
limit = 2返回两条数据,如果说只返回一条数据,结果也是列表
如果只返回一条数据,那么会返回匹配度最高的值,根据相似度依次展示

'''
process
用于处理备选答案有限的情况,返回模糊匹配的字符串和相似度。
'''
city = ['安阳市','唐山市','温州市','浙江省','杭州市','河北省']


process_extract_limit2 = process.extract('杭州',city,limit=2)
print(type(process_extract_limit2)) # 
print(process_extract_limit2) # [('杭州市', 90), ('温州市', 45)]

#返回一条数据
process_extract_limit1 = process.extract('河北',city,limit=1)
print(type(process_extract_limit1)) # 
print(process_extract_limit1) # [('河北省', 90)]

1.2、案例2

最佳使用场景:

  1. 自动补全:在用户输入查询时,根据已知的数据集提供相关的自动补全建议。
  2. 数据清洗:在数据清洗过程中,识别并纠正可能的拼写错误或不一致的命名。
  3. 搜索优化:提高搜索功能的准确性,通过优先显示与用户查询最相关的结果。
from fuzzywuzzy import process

# 定义查询字符串
query = "Apple Computer"

# 定义一系列可能与查询相关的选择
choices = ["Apple Inc.", "Apple Store", "Dell Computers", "Microsoft Corporation", "Apple Computer Inc.", "IBM",
           "Apple MacBook Pro"]

# 使用process.extract方法获取与查询最相关的选项列表
# limit参数用于限制返回结果的数量,默认为None,即返回所有结果
# 这里我们设置limit为3,以获取最相关的前三个选项
extracted_choices = process.extract(query, choices, limit=3)

# 打印结果
for choice, score in extracted_choices:
    print(f"匹配到的字符串: {choice}, 相似度: {score}")

'''
输出结果:
匹配到的字符串: Apple Computer Inc., 相似度: 95
匹配到的字符串: Apple Inc., 相似度: 86
匹配到的字符串: Dell Computers, 相似度: 71
'''

# 注释:
# process.extract方法通过计算查询字符串与每个选择之间的相似度分数,
# 来对选择列表进行排序。然后,它返回一个包含最相关选项的列表,
# 每个选项都是一个包含三个元素的元组:(选择字符串, 相似度分数, 在原列表中的索引)。

# 输出结果解释:
# 在这个例子中,输出将列出与查询"Apple Computer"最相关的前三个选项。
# 每个选项都包含三个信息:选择字符串(即与查询最相似的选择)、相似度分数(表示匹配的质量)和在原列表中的索引(即choices列表中的位置)。
# 相似度分数越高,表示该选项与查询的匹配度越高。
# 注意:实际输出的相似度分数和顺序可能会根据`fuzzywuzzy`库的具体实现和查询字符串的特点而有所不同。

注意事项

  • process.extract方法依赖于底层的相似度计算函数(如fuzz.ratiofuzz.partial_ratio等),这些函数定义了如何计算两个字符串之间的相似度。process.extract默认使用fuzz.partial_ratio,但你可以通过scorer参数指定其他评分方法。
  • limit参数用于控制返回结果的数量,如果你只想获取最相关的几个选项,设置这个参数可以提高效率。
  • 返回的相似度分数是一个介于0和100之间的整数,表示查询字符串与选择字符串之间的相似度。分数越高,相似度越高。
  • 由于fuzzywuzzy的相似度计算是基于字符串匹配的,因此它可能无法处理更复杂的语义相似性问题。在处理需要理解文本含义的场景时,可能需要考虑使用更高级的NLP技术。

2、提取一条数据

1.1、案例1
'''
如何只想匹配一条数据,并且是相似度最高的数据
可以使用extractOne,只返回一条数据,返回结果类型是元组
'''
process_extractOne = process.extractOne('温州',city)
print(type(process_extractOne)) # 
print(process_extractOne) # ('温州市', 90)

2.2、案例2

最佳使用场景:

  1. 精确匹配:当你需要从一组选项中找到与查询字符串精确匹配或最接近的一个选项时。
  2. 自动补全(单个结果):在自动补全功能中,当用户输入查询时,直接提供一个最相关的建议。
  3. 数据清洗(单个替换):在数据清洗过程中,识别并替换可能的拼写错误或不一致的命名,但只替换为最相似的单个选项。
from fuzzywuzzy import process

# 定义查询字符串
query = "Apple Computer"

# 定义一系列可能与查询相关的选择
choices = ["Apple Inc.", "Apple Store", "Dell Computers", "Microsoft Corporation", "Apple Computer Inc.", "IBM",
           "Apple MacBook Pro"]

# 使用process.extractOne方法获取与查询最相似的一个选项
# 该方法返回一个元组,包含最相似的选择字符串、相似度分数和在原列表中的索引
best_choice = process.extractOne(query, choices)

# 打印结果
print(f"匹配到的字符串: {best_choice[0]}, 相似度: {best_choice[1]}") # 匹配到的字符串: Apple Computer Inc., 相似度: 95

# 注释:
# process.extractOne方法通过计算查询字符串与每个选择之间的相似度分数,
# 来找出与查询最相似的一个选项。它返回的是一个包含三个元素的元组:
# (最相似的选择字符串, 相似度分数, 在原列表中的索引)。

# 输出结果解释:
# 在这个例子中,输出将显示与查询"Apple Computer"最相似的一个选项。
# 输出包括三个信息:最相似的选择字符串(即与查询最匹配的选择)、相似度分数(表示匹配的质量)和在原列表中的索引(即choices列表中的位置)。
# 相似度分数越高,表示该选项与查询的匹配度越高。
# 注意:如果choices列表中没有与查询足够相似的选项,返回的相似度分数可能会很低,但仍然会返回一个选项(通常是列表中与查询最相似的那个,即使相似度不高)。

注意事项

  • process.extractOne方法只返回一个最相似的选项,如果你需要获取多个最相似的选项,应该使用process.extract方法并设置limit参数。
  • 该方法默认使用fuzz.partial_ratio作为评分函数,但你可以通过scorer参数指定其他评分方法。
  • 相似度分数是一个介于0和100之间的整数,分数越高表示匹配度越高。然而,即使分数很高,也不一定能保证两个字符串在语义上完全相同,因为相似度计算是基于字符串匹配的。
  • 在处理大量数据时,process.extractOne可能会比process.extract更快,因为它只需要找到并返回一个最相似的选项,而不需要对整个列表进行排序。

六、总结

"至此,我们已一同探索了FuzzyWuzzy在Python模糊匹配中的广泛应用与强大功能。从基础概念到高级技巧,每一步都见证了它如何化繁为简,让数据处理变得更加高效与精准。希望这篇文章不仅为你提供了实用的技能,更激发了你在数据科学领域继续探索的热情。未来,我将继续分享更多关于Python、数据科学及机器学习的精彩内容,敬请期待。感谢你的关注与支持,让我们在知识的海洋中携手前行!"

通过这篇文章给大家分享我在学习过程中的一些经验和心得,希望能够对大家有所帮助,同时也接受大家的建议和意见,共同进步、共同学习。

如果你觉得我的文章对你有所帮助,我诚挚地邀请你关注、点赞和分享。

你可能感兴趣的:(Python,人工智能,算法,python,算法,大数据,网络,dba)