亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Python批量模糊匹配的3種方法實(shí)例

 更新時(shí)間:2022年03月01日 09:18:54   作者:小小明-代碼實(shí)體  
模糊匹配可以算是現(xiàn)代編輯器的一個(gè)必備特性了,它所做的就是根據(jù)用戶輸入的部分內(nèi)容,猜測用戶想要的文件名,并提供一個(gè)推薦列表供用戶選擇,下面這篇文章主要給大家介紹了關(guān)于Python批量模糊匹配的3種方法,需要的朋友可以參考下

前言

當(dāng)然,基于排序的模糊匹配(類似于Excel的VLOOKUP函數(shù)的模糊匹配模式)也屬于模糊匹配的范疇,但那種過于簡單,不是本文討論的范疇。

本文主要討論的是以公司名稱或地址為主的字符串的模糊匹配。

使用編輯距離算法進(jìn)行模糊匹配

進(jìn)行模糊匹配的基本思路就是,計(jì)算每個(gè)字符串與目標(biāo)字符串的相似度,取相似度最高的字符串作為與目標(biāo)字符串的模糊匹配結(jié)果。

對于計(jì)算字符串之間的相似度,最常見的思路便是使用編輯距離算法。

下面我們有28條名稱需要從數(shù)據(jù)庫(390條數(shù)據(jù))中找出最相似的名稱:

import pandas as pd

excel = pd.ExcelFile("所有客戶.xlsx")
data = excel.parse(0)
find = excel.parse(1)
display(data.head())
print(data.shape)
display(find.head())
print(find.shape)

編輯距離算法,是指兩個(gè)字符串之間,由一個(gè)轉(zhuǎn)成另一個(gè)所需的最少編輯操作次數(shù)。允許的編輯操作包括將一個(gè)字符替換成另一個(gè)字符,插入一個(gè)字符,刪除一個(gè)字符。
一般來說,編輯距離越小,表示操作次數(shù)越少,兩個(gè)字符串的相似度越大。

創(chuàng)建計(jì)算編輯距離的函數(shù):

def minDistance(word1: str, word2: str):
    '編輯距離的計(jì)算函數(shù)'
    n = len(word1)
    m = len(word2)
    # 有一個(gè)字符串為空串
    if n * m == 0:
        return n + m
    # DP 數(shù)組
    D = [[0] * (m + 1) for _ in range(n + 1)]
    # 邊界狀態(tài)初始化
    for i in range(n + 1):
        D[i][0] = i
    for j in range(m + 1):
        D[0][j] = j
    # 計(jì)算所有 DP 值
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            left = D[i - 1][j] + 1
            down = D[i][j - 1] + 1
            left_down = D[i - 1][j - 1]
            if word1[i - 1] != word2[j - 1]:
                left_down += 1
            D[i][j] = min(left, down, left_down)
    return D[n][m]

關(guān)于上述代碼的解析可參考力扣題解:https://leetcode-cn.com/problems/edit-distance/solution/bian-ji-ju-chi-by-leetcode-solution/

遍歷每個(gè)被查找的名稱,計(jì)算它與數(shù)據(jù)庫所有客戶名稱的編輯距離,并取編輯距離最小的客戶名稱:

result = []
for name in find.name.values:
    a = data.user.apply(lambda user: minDistance(user, name))
    user = data.user[a.argmin()]
    result.append(user)
find["result"] = result
find

測試后發(fā)現(xiàn)部分地址的效果不佳。

我們?nèi)稳?個(gè)結(jié)果為信陽息縣淮河路店地址看看編輯距離最小的前10個(gè)地址和編輯距離:

a = data.user.apply(lambda user: minDistance(user, '河南美銳信陽息縣淮河路分店'))
a = a.nsmallest(10).reset_index()
a.columns = ["名稱", "編輯距離"]
a.名稱 = data.user[a.名稱].values
a

a = data.user.apply(lambda user: minDistance(user, '河南美銳信陽潢川四中分店'))
a = a.nsmallest(10).reset_index()
a.columns = ["名稱", "編輯距離"]
a.名稱 = data.user[a.名稱].values
a

可以看到,在前十個(gè)編輯距離最小的名稱中還是存在我們想要的結(jié)果。

使用fuzzywuzzy進(jìn)行批量模糊匹配

通過上面的代碼,我們已經(jīng)基本了解了通過編輯距離算法進(jìn)行批量模糊匹配的基本原理。不過自己編寫編輯距離算法的代碼較為復(fù)雜,轉(zhuǎn)換為相似度進(jìn)行分析也比較麻煩,如果已經(jīng)有現(xiàn)成的輪子就不用自己寫了。

而fuzzywuzzy庫就是基于編輯距離算法開發(fā)的庫,而且將數(shù)值量化為相似度評分,會比我們寫的沒有針對性優(yōu)化的算法效果要好很多,可以通過pip install FuzzyWuzzy來安裝。

對于fuzzywuzzy庫,主要包含fuzz模塊和process模塊,fuzz模塊用于計(jì)算兩個(gè)字符串之間的相似度,相當(dāng)于對上面的代碼的封裝和優(yōu)化。而process模塊則可以直接提取需要的結(jié)果。

fuzz模塊

from fuzzywuzzy import fuzz

簡單匹配(Ratio):

a = data.user.apply(lambda user: fuzz.ratio(user, '河南美銳信陽潢川四中分店'))
a = a.nlargest(10).reset_index()
a.columns = ["名稱", "相似度"]
a.名稱 = data.user[a.名稱].values
a

非完全匹配(Partial Ratio):

a = data.user.apply(lambda user: fuzz.partial_ratio(user, '河南美銳信陽潢川四中分店'))
a = a.nlargest(10).reset_index()
a.columns = ["名稱", "相似度"]
a.名稱 = data.user[a.名稱].values
a

顯然fuzzywuzzy庫的 ratio()函數(shù)比前面自己寫的編輯距離算法,準(zhǔn)確度高了很多。

process模塊

process模塊則是進(jìn)一步的封裝,可以直接獲取相似度最高的值和相似度:

from fuzzywuzzy import process

extract提取多條數(shù)據(jù):

users = data.user.to_list()
a = process.extract('河南美銳信陽潢川四中分店', users, limit=10)
a = pd.DataFrame(a, columns=["名稱", "相似度"])
a

從結(jié)果看,process模塊似乎同時(shí)綜合了fuzz模塊簡單匹配(Ratio)和非完全匹配(Partial Ratio)的結(jié)果。

當(dāng)我們只需要返回一條數(shù)據(jù)時(shí),使用extractOne會更加方便:

users = data.user.to_list()
find["result"] = find.name.apply(lambda x: process.extractOne(x, users)[0])
find

可以看到準(zhǔn)確率相對前面自寫的編輯距離算法有了大幅度提升,但個(gè)別名稱匹配結(jié)果依然不佳。

查看這兩個(gè)匹配不準(zhǔn)確的地址:

process.extract('許灣鄉(xiāng)許灣村焦艷芳衛(wèi)生室', users)

[('小寨溝村衛(wèi)生室', 51),
 ('周口城鄉(xiāng)一體化焦艷芳一體化衛(wèi)生室', 50),
 ('西華縣皮營鄉(xiāng)樓陳村衛(wèi)生室', 42),
 ('葉縣鄧?yán)钹l(xiāng)杜楊村第二衛(wèi)生室', 40),
 ('湯陰縣瓦崗鄉(xiāng)龍虎村東衛(wèi)生室', 40)]

process.extract('河南美銳信陽息縣淮河路分店', users)

[('信陽息縣淮河路店', 79),
 ('河南美銳大藥房連鎖有限公司息縣淮河路分店', 67),
 ('河南美銳大藥房連鎖有限公司息縣大河文錦分店', 53),
 ('河南美銳大藥房連鎖有限公司息縣千佛庵東路分店', 51),
 ('河南美銳大藥房連鎖有限公司息縣包信分店', 50)]

對于這樣的問題,個(gè)人并沒有一個(gè)很完美的解決方案,個(gè)人建議是將相似度最高的n個(gè)名稱都加入結(jié)果列表中,后期再人工篩選:

result = find.name.apply(lambda x: next(zip(*process.extract(x, users, limit=3)))).apply(pd.Series)
result.rename(columns=lambda i: f"匹配{i+1}", inplace=True)
result = pd.concat([find.drop(columns="result"), result], axis=1)
result

雖然可能有個(gè)別正確結(jié)果這5個(gè)都不是,但整體來說為人工篩查節(jié)省了大量時(shí)間。

整體代碼

from fuzzywuzzy import process
import pandas as pd

excel = pd.ExcelFile("所有客戶.xlsx")
data = excel.parse(0)
find = excel.parse(1)
users = data.user.to_list()
result = find.name.apply(lambda x: next(
    zip(*process.extract(x, users, limit=3)))).apply(pd.Series)
result.rename(columns=lambda i: f"匹配{i+1}", inplace=True)
result = pd.concat([find, result], axis=1)
result

使用Gensim進(jìn)行批量模糊匹配

Gensim簡介

Gensim支持包括TF-IDF,LSA,LDA,和word2vec在內(nèi)的多種主題模型算法,支持流式訓(xùn)練,并提供了諸如相似度計(jì)算,信息檢索等一些常用任務(wù)的API接口。

基本概念:

  • 語料(Corpus):一組原始文本的集合,用于無監(jiān)督地訓(xùn)練文本主題的隱層結(jié)構(gòu)。語料中不需要人工標(biāo)注的附加信息。在Gensim中,Corpus通常是一個(gè)可迭代的對象(比如列表)。每一次迭代返回一個(gè)可用于表達(dá)文本對象的稀疏向量。
  • 向量(Vector):由一組文本特征構(gòu)成的列表。是一段文本在Gensim中的內(nèi)部表達(dá)。
  • 稀疏向量(SparseVector):可以略去向量中多余的0元素。此時(shí),向量中的每一個(gè)元素是一個(gè)(key, value)的元組
  • 模型(Model):是一個(gè)抽象的術(shù)語。定義了兩個(gè)向量空間的變換(即從文本的一種向量表達(dá)變換為另一種向量表達(dá))。

安裝:pip install gensim

官網(wǎng):https://radimrehurek.com/gensim/

什么情況下需要使用NLP來進(jìn)行批量模糊匹配呢?那就是數(shù)據(jù)庫數(shù)據(jù)過于龐大時(shí),例如達(dá)到幾萬級別:

import pandas as pd

data = pd.read_csv("所有客戶.csv", encoding="gbk")
find = pd.read_csv("被查找的客戶.csv", encoding="gbk")
display(data.head())
print(data.shape)
display(find.head())
print(find.shape)

此時(shí)如果依然用編輯距離或fuzzywuzzy暴力遍歷計(jì)算,預(yù)計(jì)1小時(shí)也無法計(jì)算出結(jié)果,但使用NLP神器Gensim僅需幾秒鐘,即可計(jì)算出結(jié)果。

使用詞袋模型直接進(jìn)行批量相似度匹配

首先,我們需要先對原始的文本進(jìn)行分詞,得到每一篇名稱的特征列表:

import jieba

data_split_word = data.user.apply(jieba.lcut)
data_split_word.head(10)

0        [珠海, 廣藥, 康鳴, 醫(yī)藥, 有限公司]
1              [深圳市, 寶安區(qū), 中心醫(yī)院]
2         [中山, 火炬, 開發(fā)區(qū), 伴康, 藥店]
3           [中山市, 同方, 醫(yī)藥, 有限公司]
4    [廣州市, 天河區(qū), 元崗金, 健民, 醫(yī)藥, 店]
5       [廣州市, 天河區(qū), 元崗居, 健堂, 藥房]
6          [廣州市, 天河區(qū), 元崗潤佰, 藥店]
7        [廣州市, 天河區(qū), 元崗, 協(xié)心, 藥房]
8        [廣州市, 天河區(qū), 元崗, 心怡, 藥店]
9         [廣州市, 天河區(qū), 元崗永亨堂, 藥店]
Name: user, dtype: object

接下來,建立語料特征的索引字典,并將文本特征的原始表達(dá)轉(zhuǎn)化成詞袋模型對應(yīng)的稀疏向量的表達(dá):

from gensim import corpora

dictionary = corpora.Dictionary(data_split_word.values)
data_corpus = data_split_word.apply(dictionary.doc2bow)
data_corpus.head()

0             [(0, 1), (1, 1), (2, 1), (3, 1), (4, 1)]
1                             [(5, 1), (6, 1), (7, 1)]
2          [(8, 1), (9, 1), (10, 1), (11, 1), (12, 1)]
3                   [(0, 1), (3, 1), (13, 1), (14, 1)]
4    [(0, 1), (15, 1), (16, 1), (17, 1), (18, 1), (...
Name: user, dtype: object

這樣得到了每一個(gè)名稱對應(yīng)的稀疏向量(這里是bow向量),向量的每一個(gè)元素代表了一個(gè)詞在這個(gè)名稱中出現(xiàn)的次數(shù)。

至此我們就可以構(gòu)建相似度矩陣:

from gensim import similarities

index = similarities.SparseMatrixSimilarity(data_corpus.values, num_features=len(dictionary))

再對被查找的名稱作相同的處理,即可進(jìn)行相似度批量匹配:

find_corpus = find.name.apply(jieba.lcut).apply(dictionary.doc2bow)
sim = index[find_corpus]
find["result"] = data.user[sim.argmax(axis=1)].values
find.head(30)

可以看到該模型計(jì)算速度非???,準(zhǔn)確率似乎整體上比fuzzywuzzy更高,但fuzzywuzzy對河南美銳大藥房連鎖有限公司308廠分店的匹配結(jié)果是正確的。

使用TF-IDF主題向量變換后進(jìn)行批量相似度匹配

之前我們使用的Corpus都是詞頻向量的稀疏矩陣,現(xiàn)在將其轉(zhuǎn)換為TF-IDF模型后再構(gòu)建相似度矩陣:

from gensim import models

tfidf = models.TfidfModel(data_corpus.to_list())
index = similarities.SparseMatrixSimilarity(
    tfidf[data_corpus], num_features=len(dictionary))

被查找的名稱也作相同的處理:

sim = index[tfidf[find_corpus]]
find["result"] = data.user[sim.argmax(axis=1)].values
find.head(30)

可以看到許灣鄉(xiāng)許灣村焦艷芳衛(wèi)生室匹配正確了,但河南美銳信陽息縣淮河路分店又匹配錯(cuò)誤了,這是因?yàn)樵赥F-IDF模型中,由于美銳在很多條數(shù)據(jù)中都出現(xiàn)被降權(quán)。

假如只對數(shù)據(jù)庫做TF-IDF轉(zhuǎn)換,被查找的名稱只使用詞頻向量,匹配效果又如何呢?

from gensim import models

tfidf = models.TfidfModel(data_corpus.to_list())
index = similarities.SparseMatrixSimilarity(
    tfidf[data_corpus], num_features=len(dictionary))
sim = index[find_corpus]
find["result"] = data.user[sim.argmax(axis=1)].values
find.head(30)

可以看到除了數(shù)據(jù)庫本來不包含正確名稱的愛聯(lián)寶之林大藥房外還剩下河南美銳大藥房連鎖有限公司308廠分店匹配不正確。這是因?yàn)椴荒茏R別出308的語義等于三零八。如果這類數(shù)據(jù)較多,我們可以先將被查找的數(shù)據(jù)統(tǒng)一由小寫數(shù)字轉(zhuǎn)換為大寫數(shù)字(保持與數(shù)據(jù)庫一致)后,再分詞處理:

trantab = str.maketrans("0123456789", "零一二三四五六七八九")
find_corpus = find.name.apply(lambda x: dictionary.doc2bow(jieba.lcut(x.translate(trantab))))

sim = index[find_corpus]
find["result"] = data.user[sim.argmax(axis=1)].values
find.head(30)

經(jīng)過這樣處理后,308廠分店也被正確匹配上了,其他類似的問題都可以使用該思路進(jìn)行轉(zhuǎn)換。

雖然經(jīng)過上面的處理,匹配準(zhǔn)確率幾乎達(dá)到100%,但不代表其他類型的數(shù)據(jù)也會有如此高的準(zhǔn)確率,還需根據(jù)數(shù)據(jù)的情況具體去分析轉(zhuǎn)換。并沒有一個(gè)很完美的批量模糊匹配的處理辦法,對于這類問題,我們不能完全信任程序匹配的結(jié)果,都需要人工的二次檢查,除非能夠接受一定的錯(cuò)誤率。

為了我們?nèi)斯ずY選的方便,我們可以將前N個(gè)相似度最高的數(shù)據(jù)都保存到結(jié)果中,這里我們以三個(gè)為例:

同時(shí)獲取最大的3個(gè)結(jié)果

下面我們將相似度最高的3個(gè)值都添加到結(jié)果中:

result = []
for corpus in find_corpus.values:
    sim = pd.Series(index[corpus])
    result.append(data.user[sim.nlargest(3).index].values)
result = pd.DataFrame(result)
result.rename(columns=lambda i: f"匹配{i+1}", inplace=True)
result = pd.concat([find.drop(columns="result"), result], axis=1)
result.head(30)

完整代碼

from gensim import corpora, similarities, models
import jieba
import pandas as pd

data = pd.read_csv("所有客戶.csv", encoding="gbk")
find = pd.read_csv("被查找的客戶.csv", encoding="gbk")

data_split_word = data.user.apply(jieba.lcut)
dictionary = corpora.Dictionary(data_split_word.values)
data_corpus = data_split_word.apply(dictionary.doc2bow)
trantab = str.maketrans("0123456789", "零一二三四五六七八九")
find_corpus = find.name.apply(
    lambda x: dictionary.doc2bow(jieba.lcut(x.translate(trantab))))

tfidf = models.TfidfModel(data_corpus.to_list())
index = similarities.SparseMatrixSimilarity(
    tfidf[data_corpus], num_features=len(dictionary))

result = []
for corpus in find_corpus.values:
    sim = pd.Series(index[corpus])
    result.append(data.user[sim.nlargest(3).index].values)
result = pd.DataFrame(result)
result.rename(columns=lambda i: f"匹配{i+1}", inplace=True)
result = pd.concat([find, result], axis=1)
result.head(30)

總結(jié)

本文首先分享了編輯距離的概念,以及如何使用編輯距離進(jìn)行相似度模糊匹配。然后介紹了基于該算法的輪子fuzzwuzzy,封裝的較好,使用起來也很方便,但是當(dāng)數(shù)據(jù)庫量級達(dá)到萬條以上時(shí),效率極度下降,特別是數(shù)據(jù)量達(dá)到10萬級別以上時(shí),跑一整天也出不了結(jié)果。于是通過Gensim計(jì)算分詞后對應(yīng)的tf-idf向量來計(jì)算相似度,計(jì)算時(shí)間由幾小時(shí)降低到幾秒,而且準(zhǔn)確率也有了較大提升,能應(yīng)對大部分批量相似度模糊匹配問題。

到此這篇關(guān)于Python批量模糊匹配的3種方法的文章就介紹到這了,更多相關(guān)Python批量模糊匹配內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python使用random模塊實(shí)現(xiàn)擲骰子游戲的示例代碼

    Python使用random模塊實(shí)現(xiàn)擲骰子游戲的示例代碼

    這篇文章主要介紹了Python使用random模塊實(shí)現(xiàn)擲骰子游戲的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Python中Collection的使用小技巧

    Python中Collection的使用小技巧

    這篇文章主要介紹了Python中Collection的使用小技巧,對初學(xué)者來說有不錯(cuò)的學(xué)習(xí)借鑒價(jià)值,需要的朋友可以參考下
    2014-08-08
  • Qt調(diào)用Python詳細(xì)圖文過程記錄

    Qt調(diào)用Python詳細(xì)圖文過程記錄

    Qt調(diào)用python實(shí)際上就是c++調(diào)python,網(wǎng)上搜會出來很多,介紹得也比較全,這里做個(gè)記錄,下面這篇文章主要給大家介紹了關(guān)于Qt調(diào)用Python詳細(xì)圖文過程,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • tensorflow+k-means聚類簡單實(shí)現(xiàn)貓狗圖像分類的方法

    tensorflow+k-means聚類簡單實(shí)現(xiàn)貓狗圖像分類的方法

    這篇文章主要介紹了tensorflow+k-means聚類簡單實(shí)現(xiàn)貓狗圖像分類,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • Python實(shí)現(xiàn)的多項(xiàng)式擬合功能示例【基于matplotlib】

    Python實(shí)現(xiàn)的多項(xiàng)式擬合功能示例【基于matplotlib】

    這篇文章主要介紹了Python實(shí)現(xiàn)的多項(xiàng)式擬合功能,結(jié)合實(shí)例形式分析了Python基于matplotlib模塊進(jìn)行數(shù)值運(yùn)算與圖形繪制相關(guān)操作技巧,需要的朋友可以參考下
    2018-05-05
  • 詳解python調(diào)用cmd命令三種方法

    詳解python調(diào)用cmd命令三種方法

    這篇文章主要介紹了詳解python調(diào)用cmd命令三種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 詳解Pandas如何高效對比處理DataFrame的兩列數(shù)據(jù)

    詳解Pandas如何高效對比處理DataFrame的兩列數(shù)據(jù)

    我們在用?pandas?處理數(shù)據(jù)的時(shí)候,經(jīng)常會遇到用其中一列數(shù)據(jù)替換另一列數(shù)據(jù)的場景。這一類的需求估計(jì)很多人都遇到,當(dāng)然還有其它更復(fù)雜的。解決這類需求的辦法有很多,這里我們來推薦幾個(gè)
    2022-09-09
  • 基于python實(shí)現(xiàn)生成指定大小txt文檔

    基于python實(shí)現(xiàn)生成指定大小txt文檔

    這篇文章主要介紹了基于python實(shí)現(xiàn)生成指定大小txt文檔,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Python使用pyexecjs代碼案例解析

    Python使用pyexecjs代碼案例解析

    這篇文章主要介紹了Python使用pyexecjs代碼實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • python中從for循環(huán)延申到推導(dǎo)式的具體使用

    python中從for循環(huán)延申到推導(dǎo)式的具體使用

    這篇文章主要介紹了python中從for循環(huán)延申到推導(dǎo)式的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11

最新評論