python字符串模糊匹配,并计算匹配分数

news2024/9/27 21:27:41

一、thefuzz

thefuzz包以前叫fuzzywuzzy,0.19版本开始改名为thefuzz,github地址:

GitHub - seatgeek/thefuzz: Fuzzy String Matching in Python

可以通过命令pip install thefuzz安装此包。用法还是比较简单的:

from thefuzz import fuzz

fuzz.ratio("test", "test!")

>>89

上面两个字符串的相似度为89%。

二、相似度ratio的计算

我们先看看这个包下面的源码,来查看thefuzz是怎么实现模糊匹配的。thefuzz源码包的结构如下:

先看看ratio方法源码:

def ratio(s1, s2):
    s1, s2 = utils.make_type_consistent(s1, s2)

    m = SequenceMatcher(None, s1, s2)
    return utils.intr(100 * m.ratio())

 可以看到,ratio方法用到了一个比较关键的类SequenceMatcher,但是这个类却有可能来自两个不同的地方。

2.1 SequenceMatcher的来源

看看fuzzy.py的头部代码:

import platform
import warnings

try:
    #从当前文件夹的StringMatcher中导入StringMatcher
    from .StringMatcher import StringMatcher as SequenceMatcher
except ImportError:
    if platform.python_implementation() != "PyPy":
        warnings.warn('Using slow pure-python SequenceMatcher. Install python-Levenshtein to remove this warning')
    from difflib import SequenceMatcher

#导入当前文件夹的utils包,.代表当前目录
from . import utils

上面代码涉及了一个导入问题,即先从当前文件StringMatcher中导入StringMatcher,如果导入出现异常,就去difflib中导入SequenceMatcher。

正如上面第一张图中看到的,当然文件夹下面确实有一个叫StringMatcher.py的文件,也看看它前面的代码:

from Levenshtein import *
from warnings import warn

class StringMatcher:
.............
.............

可以看出,这个StringMatcher类引用了Levenshtein包,这个包也是用来计算字符串模糊匹配的,效率上来说,有可能比difflib中的SequenceMatcher快4-10倍。

Levenshtein包是用C语言写的,比较复杂,最初的项目地址:

GitHub - miohtama/python-Levenshtein: The Levenshtein Python C extension module contains functions for fast computation of Levenshtein distance and string similarity

后来这个作者没有维护了,然后由另一个在维护,项目的地址在这里:

GitHub - ztane/python-Levenshtein: The Levenshtein Python C extension module contains functions for fast computation of Levenshtein distance and string similarity

但是,这个页面上,作者也说了,他也7年不维护了,现在没找到新的维护者。

不管怎么说,如果要使用Levenshtein,还是可以安装的:

pip install python-Levenshtein

总结就是,thefuzz有两种实现方式,一种是依赖difflib,另一种依靠 python-Levenshtein。先看简单的difflib。

2.2 difflib包中的SequenceMatcher

首先导入:

from difflib import SequenceMatcher

这个类的主要作用是计算两个匹配字符串的相似度,如下:

s = SequenceMatcher(None, "abcde", "bcde")
s.ratio()

输出值为0.888888。这个是怎么计算的呢?可以查看difflib.py的源代码(我的电脑在D:\ProgramData\Miniconda3\Lib目录下),如下:

def ratio(self):
    matches = sum(triple[-1] for triple in self.get_matching_blocks())
    return _calculate_ratio(matches, len(self.a) + len(self.b))

这个方法涉及到两个比较重要的方法,一个是get_matching_blocks(),这个方法用于获取匹配的字符块。另一个方法_calculate_ratio,用于计算相似度,先看_calculate_ratio,代码如下:

def _calculate_ratio(matches, length):
    if length:
        return 2.0 * matches / length
    return 1.0

上面代码的第三行是关键,matches表示的字符个数,length是两个字符串加起来的总长度。如上面的"abcde"和 "bcde",ratio的计算方法就是2*4/9,即8/9=0.888888。

再看看get_matching_blocks方法。这个方法比较复杂,我们先来看下,这个方法的用法:

s = SequenceMatcher(None, "abchde", "bcde")
print(s.get_matching_blocks())

输出如下:

[Match(a=1, b=0, size=2), Match(a=4, b=2, size=2), Match(a=6, b=4, size=0)] 

什么意思?从方法的名字大概就能看出来,就是获得匹配的所有字符块。上面的代码输出了3个Match对象,Match(a=1, b=0, size=2)的意思是"abchde"从索引1(a=1)开始,"bcde"从索引0(b=0)开始,匹配到2(size=2)个相等字符。

最后一个Match(a=6, b=4, size=0)是固定的,a、b代表两个字符串的长度,size=0固定不变。用代码描述如下:

(len(a), len(b), 0)

三、process模块

从第一张图中可以看到,除了fuzz.py这个文件,还有一个叫process.py的文件,process模块常用的是从候选列表中,返回与目标字符串最相似的一个结果。来看一个简单的例子:

from thefuzz import fuzz,process

choices = ["hello world", "hello china", "hello beijing"]
print(process.extractOne("china",choices))

#输出内容
>>('hello china', 90)

正如上面代码所示,process最常用的用法是从众多字符串中,找到最佳匹配的字符串。

process.extractOne的格式如下:

extractOne(query, choices, processor=default_processor, scorer=default_scorer, score_cutoff=0):

"""
Args:
    query: A string to match against
    choices: A list or dictionary of choices, suitable for use with extract().
    processor: Optional function for transforming choices before matching.
    scorer: Scoring function for extract().
    score_cutoff: Optional argument for score threshold. If the best
            match is found, but it is not greater than this number, then
            return None anyway ("not a good enough match").  Defaults to 0.

Returns:
    A tuple containing a single match and its score, if a match
    was found that was above score_cutoff. Otherwise, returns None.
"""

query:查询的字符串;

choices: 待匹配的字符串列表或者字典;

processor:可选参数,转换器,在匹配前先对choices进行转换处理;

scorer:可选参数,分数器,用于计算分数;

score_cutoff:可选参数,这个参数的作用是设置一个分数门槛(默认为0),如果小于这个分数,就不返回匹配的字符串,而是返回一个None。

extractOne返回的结果是一个tuple元组(最佳匹配结果,分数)。

我们比较关心的一个问题是,这个分数是怎么计算的?看看下面例子:

from thefuzz import fuzz,process

print(fuzz.ratio("china","hello china"))

choices = ["hello world", "hello china", "hello beijing"]
print(process.extractOne("china",choices))

#输出内容
>>62
>>('hello china', 90)

可以看出,fuzz.ratio与process.extractOne分数的计算方式不一样(一个是62分,一个90分)。fuzz.ratio的计分方式,上面已经讲了,下面来看看extractOne的计分方式。

extractOne的源码如下:

def extractOne(query, choices, processor=default_processor, scorer=default_scorer, score_cutoff=0):
    best_list = extractWithoutOrder(query, choices, processor, scorer, score_cutoff)
    try:
        return max(best_list, key=lambda i: i[1])
    except ValueError:
        return None

我们刚才说了,第三个参数scorer是用于计分的,它的默认值为default_scorer,那我们先找到这个default_scorer的值:

default_scorer = fuzz.WRatio

即默认的计分方式为fuzz.WRatio,那么我们回到fuzz.py中,看看WRatio是做什么的?

from thefuzz import fuzz

default_scorer = fuzz.WRatio
default_scorer("china", "hello china")

#输出
>> 90

 可以看出,WRatio的计分方式确实和上面的extractOne相同,都是90分。WRatio的计分方式比较复杂,涉及到一个权重(weight)的概念,它是基于fuzz.ratio()的基础上,做了进一步的校正。

如果我们不想采用WRatio的计分方式,或者想采用fuzz.ratio()的计分方式来提取最佳匹配结果,可以这样:

from thefuzz import fuzz,process

print(fuzz.ratio("china","hello china"))

choices = ["hello world", "hello china", "hello beijing"]
print(process.extractOne("china",choices,scorer=fuzz.QRatio))

#输出
>>62
>>('hello china', 62)

上面代码的计分结果都是62分,因为fuzz.QRatio的内部,除了对参数进行了一些简单的处理以外,直接调用fuzz.ratio()方法返回了结果。所以fuzz.QRatio和fuzz.ratio()的计分方式完全相同。

fuzz.QRatio源代码:

# q is for quick
def QRatio(s1, s2, force_ascii=True, full_process=True):

    if full_process:
        p1 = utils.full_process(s1, force_ascii=force_ascii)
        p2 = utils.full_process(s2, force_ascii=force_ascii)
    else:
        p1 = s1
        p2 = s2

    if not utils.validate_string(p1):
        return 0
    if not utils.validate_string(p2):
        return 0

    return ratio(p1, p2)

通过上面的例子可以看出,如果我们对QRatio、WRatio这些计分方式不满意的话,完全可以自己实现了一个Ratio,将它做为extractOne的参数,实现定制的返回结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/438944.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

redis_exporter 结合prometheus 监控redis cluster集群

redis_exporter 结合prometheus 监控redis cluster集群 前提1:已经搭建好redis cluster集群前提2:已搭建好prometheus 1、下载redis_exporter wget https://github.com/oliver006/redis_exporter/releases/download/v1.50.0/redis_exporter-v1.50.0.l…

WebServer项目(三)->linux网络编程基础知识

WebServer项目[三]->linux网络编程基础知识 1. I/O多路复用(I/O多路转接)2. select1)select简介2)select详解select具体怎么用?那FD_CLR函数是干嘛的?关于 fd_set,它具体是什么? 3. poll(改进select)4. epoll5.epoll的两种工作模式6.UDP通…

qemu-img resize gpt分区 parted修复分区信息 虚拟机 lvm 扩容根分区

扩容qcow2虚拟盘 关闭虚拟机 virsh destroy redflag1 qemu-img resize从20G扩容至40G qemu-img resize redflag.qcow2 40G 启动 virsh start redflag1 查看状态,当前无任何变化 fdisk 查看vda,已经变大 查看lvm信息 xfs_info 扩容虚拟机根分区 修…

KD2684S电机匝间耐电压测试仪

一、产品简介 试验仪适用于电机、变压器、电器线圈等这些由漆包线绕制的产品。因漆包线的绝缘涂敷层本身存在着质量问题,以及在绕线、嵌线、刮线、接头端部整形、绝缘浸漆、装配等工序工艺中不慎而引起绝缘层的损伤等,都会造成线圈层间或匝间绝缘层的绝缘…

BGP的路径属性及选路规则

路径属性 路径属性对于BGP而言,BGP路径属性描述了该条路由的各项特征,同时,路由携带的路径属性也在某些场景下影响BGP路由优选的决策。 公认属性-----所有的BGP路由器均可以识别的属性 强制属性-----指当BGP路由器使用update报文通报路由更新…

机器学习实战:Python基于DT决策树模型进行分类预测(六)

文章目录 1 前言1.1 决策树的介绍1.2 决策树的应用 2 Scikit-learn数据集演示2.1 导入函数2.2 导入数据2.3 建模2.4 评估模型2.5 可视化决策树2.6 优化模型2.7 可视化优化模型 3 讨论 1 前言 1.1 决策树的介绍 决策树(Decision Tree,DT)是一…

R语言的Meta分析【全流程、不确定性分析】方法与Meta机器学习技术应用

Meta分析是针对某一科研问题,根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法,对来源不同的研究成果进行收集、合并及定量统计分析的方法,最早出现于“循证医学”,现已广泛应用于农林生态,资源环境等方面。…

【springboot】缓存之@Cacheable、@CachePut、@CacheEvict的用法

目录 一、注解参数说明1.1 属性说明1.1.1 value/cacheNames 属性1.1.2 key属性1.1.3 keyGenerator属性1.1.4 cacheManager属性1.1.5 cacheResolver属性1.1.6 condition属性1.1.7 unless 属性1.1.8 sync 属性 1.2 Cacheable注解1.3 CachePut注解1.4 CacheEvict注解1.4.1 allEntr…

低代码产品如何分类,大部分人都没有搞清楚

最近许多技术峰会都出现了低代码这个名词,可以说,低代码是中台之后,又一个热门话题和名词了。 一、什么是低代码平台? 低代码平台是无需编码或通过少量代码就可以快速生成应用程序的开发平台。也是一款图形化、拖拉拽方式快速实…

hadoop伪分布式安装

文章目录 1. 将安装包hadoop-3.1.3.tar.gz上次至linux中2. 进行解压操作3. 修改目录名称4. 配置环境变量5. 修改自定义配置文件5.1 hadoop-env.sh5.2 core-site.xml5.3 hdfs-site.xml5.4 workers 6. 格式化集群7. 免密登录8. 启动hdfs9. 关闭hdfs 1. 将安装包hadoop-3.1.3.tar.…

群晖NAS与阿里云盘同步的方法

同步方法:通过在 docker 中安装 aliyundrive-webdav 实现与阿里云盘同步。 下载和安装 aliyundrive-webdav 在 docker 的注册表中搜素 aliyun,选择点赞比较多的 messense/aliyundrive-webdav: 下载后安装。建议在配置和启动之前&#xff…

Docker 的安装和镜像容器的基本操作

文章目录 一、Docker 概述1、Docker的概念2、容器的优点3、容器与虚拟机的区别4、容器在内核中支持2种重要技术5、Docker核心概念 二、Docker的安装1、docker的安装步骤2、实例操作:安装docker 三、Docker 镜像操作1、搜索镜像2、获取镜像3、镜像加速下载4、查看镜像…

基础工业工程(易树平、郭伏)——第三草 工作研究

第三草 工作研究 第一节 工作研究概述 一、工作研究的对象 工作研究的对象是作业系统,这是一个由多个相互关联的因素所组成的有机整体,旨在实现预定的功能和目标。作业系统的目标表现为输出一定的“产品”或“服务”,主要由材料、设备、能…

超级简洁、彻底组件化的轻量级Android Kotlin Jetpack MVVM组件化框架

结构 特点: 彻底组件化,且更简洁,Module具有独立的Application、AndroidMinifast、资源文件等;Application和Library的切换更加快捷;超级简洁、且多功能的网络层封装,自带2级缓存,App端内嵌了日…

盛元广通高校实验室安全智能管理平台

实验室安全问题一直以来都是高校管理的重点,依据《高等学校实验室安全规范》相关要求,应教育相关部门以及应急管理部门的相关规定,关于安全工作的系列重要指示和部署,必须按照危险源管控分级管理体系对实验室进行分级分类管理&…

ebay、速卖通、ozon销量下滑怎么办?怎样可以提高转化率

单量不好,就开始焦虑,而真正需要了解为什么会出现销量下滑的原因,从不断更新自己的知识,提高自己的技能,如何提高自己的技能呢? 把所学到的知识学以致用,listing的评分,退货率&…

ESP32学习四-自定义分区表

1、简介 ESP32-WROOM-32集成了4MB SPI FLASH。对应的,也会对这4MB FLAHS进行分区处理。在编译esp32程序时,通过make menuconfig -> Partition Table可以设置三种分区。 工厂程序(无OTA分区) 工厂程序(双OTA分…

自旋锁/读者写者问题

自旋锁 自旋锁的概念和理解 锁在处理需要申请加锁的线程的时候,一般有两种处理方法:一种是挂起等待,另外一种是自旋。自旋即轮询。 挂起等待: 当一个线程成功申请锁,并进入临界区后,其它线程在申请的时候…

【经验分享】硬件工程师需要知道的DFM可制造性设计

最近,有硬件工程师朋友找我讨论DFM,也就是可制造性设计。Design for Manufacturability。 什么是可制造性设计,看一张图很容易明白: 过大的PCB,无法上产线批量生产,极大的PCB面积浪费,自然是失…

Spring Batch 指南

SpringBatch 介绍 目前,Spring Batch是批处理框架界为数不多的优秀框架(Java语言开发)。 Spring Batch 是一个轻量级的、完善的批处理框架,旨在帮助企业建立健壮、高效的批处理应用。 Spring Batch是Spring的一个子项目,使用Java语言并基于Spring框架为基础开发,…