跳过自身迭代列表

问题描述

我正在尝试在列表中找到相似的电子邮件。为此,

database.head()

    TID PID Names
0   22330   134575  tim
1   22333   134578  tim.rand
2   22328   134571  rand.001
3   22340   134568  pankit090
4   22325   134569  timrook

emailsdb = database['Names'].values.tolist() 现在是迭代部分

list = []
for email in emailsdb :
    result = process.extractBests(email,emailsdb,score_cutoff=85,limit=100)
    list.append(result)

列表输出

[[('tim',100),('tim.rand',90),('timrook',90)],[('tim.rand',('tim',[('rand.001',100)],[('pankit090',('pankit001',89),('pankit002',('pankit003',('pankit004',('pankit005',89)],[('timrook',[('pankit001',('pankit090',[('pankit002',

但是我要避免('tim',100),('tim.rand',100),('rand.001',100),('pankit090',100),('timrook',100 ),('pankit001',100),('pankit002',100),因为它们显然是完美的匹配

解决方法

根据我们在聊天中的讨论,将此问题发布为另一个答案,因为这以完全不同的方式解决了问题。

方法-

  1. 如果使用交换函数获取字符串匹配项,则实际上只能计算距离矩阵的下三角矩阵,因为f(x,y)与f(y,x)相同。

  2. 模糊比不是可交换的,而Levenshtein距离(又称编辑距离,这是模糊匹配的基础)是可交换的。这里不是获得最高分,而是找到它们之间的最小lev距离为

    的字符串。
  3. 因此,您首先获取下三角矩阵的索引,然后对它们进行迭代以计算具有这些索引的电子邮件的lev距离。您不要对距离矩阵f(x,x)的对角线进行迭代,因为它很琐碎,然后将其设置为999之类的较高值,以便可以在每一行中找到最小得分值。

  4. 这将为您提供距离矩阵(请参见输出)。接下来,对于每一行(电子邮件),您都找到最接近的字符串(最小距离)并将其创建为最佳匹配元组(请参见输出)

  5. 现在,这里的问题是,即使字符串NONE匹配得很好,它仍然会贪婪地获取最接近的那个。

  6. 因此,最终您可以在这些字符串之间进行fuzz.ratio运算,并获得模糊分数,然后对其进行过滤。因此,现在,您可以避免垃圾匹配,而仅使真正匹配的垃圾超过特定阈值。这给出了最终匹配字符串(参见输出)

这可能是从算法角度优化代码的最佳选择。

distance matrix = 
[[999.   5.   8.   8.   4.]
 [  5. 999.   8.   9.   4.]
 [  8.   8. 999.   6.   8.]
 [  8.   9.   6. 999.   9.]
 [  4.   4.   8.   9. 999.]]


Best matches = 
[('tim','timrook'),('tim.rand',('rand.001','pankit090'),('pankit090','rand.001'),('timrook','tim')]


final matching strings - 
[('tim','timrook',60),53),'tim',60)]
ArgumentParser
,

这是处理模糊查找然后删除原始电子邮件列表结果的代码。由于这是一个简短列表,因此删除匹配列表与清除原始列表具有相同的效果。请注意,删除过程可能包含在第一个电子邮件循环中,但是为了清楚起见,我将它们分开放置。我添加了zzzz条目以检查得分。

from fuzzywuzzy import fuzz
from fuzzywuzzy import process

# original email list
emailsdb = [
'tim','tim.rand','rand.001','pankit090','pankit001','pankit002','pankit003','pankit004','pankit005','zzzz'
]

print('emailsdb:',emailsdb)

# do fuzzy search and score other emails (including self)
lstres = []
for email in emailsdb :
    result = process.extractBests(email,emailsdb,score_cutoff=85,limit=50)
    lstres.append(result)
    
print('WuzzyResult:',lstres)

# scan match list and create set 
setremove = set()  # no duplicates
for r in lstres:
   setremove |= ({t[0] for t in r})
   
print('setremove:',setremove)

# remove matches from original set
for e in setremove:
   emailsdb.remove(e)
   
print('emailsdb:',emailsdb)

输出:

emailsdb: ['tim','zzzz']

WuzzyResult: [
[('tim',100),90),90)],[('tim.rand',('tim',[('rand.001',100)],[('pankit090',('pankit001',89),('pankit002',('pankit003',('pankit004',('pankit005',89)],[('timrook',[('pankit001',[('pankit002',[('pankit003',[('pankit004',[('pankit005',[('zzzz',100)]
]

setremove: {'pankit002','zzzz','tim.rand'}

emailsdb: []
,

您可以删除不想比较的已知项目(可能是预定义的知识)。可以通过弹出这些项目然后进行比较,然后再将其插入来完成。

这应该可以解决部分问题。在比较之前,我只是pop与列表中的相同元素,然后insert将其返回下一个循环。检查打印的输出以获取更多详细信息-

import pandas as pd
from fuzzywuzzy import fuzz,process

emailsdb = list(df['Names'])

l = []
for i in range(len(emailsdb)) : 
    email = emailsdb[i] #Popping based on index,rather than comparing email in emailsdb
    emailsdb.pop(i) #Pop the same email string in the list of emails
    print("comparing:",[email],'->',emailsdb)
    result = process.extractBests(email,limit=100)
    l.append(result)
    emailsdb.insert(i,email) #Insert it back

print(l)
comparing: ['tim'] -> ['tim.rand','timrook']
comparing: ['tim.rand'] -> ['tim','timrook']
comparing: ['rand.001'] -> ['tim','timrook']
comparing: ['pankit090'] -> ['tim','timrook']
comparing: ['timrook'] -> ['tim','pankit090']

[[('tim.rand',[('tim',[],90)]]

如您所见,冗余比较不是输出的一部分。

注意,我没有将电子邮件列表中的电子邮件与整个电子邮件列表进行比较以将其删除。我只是利用迭代的顺序性质从电子邮件列表中删除第0、1、2,...个索引电子邮件。这是一个很大的加速。

您可以修改此方法,以处理已经知道相似且无需比较的项目集。

P.S:请取悦,请勿使用list作为变量名:)