Python在迭代时从列表中删除
我有一个字符串列表,我只想保留最独特的字符串。下面是我如何实现的(可能循环有问题) 比率为0.99,使用Python在迭代时从列表中删除,python,Python,我有一个字符串列表,我只想保留最独特的字符串。下面是我如何实现的(可能循环有问题) 比率为0.99,使用SequenceMatcher(无,说明[1],说明[2])。比率()大约为0.98。但是使用SequenceMatcher(无,说明[0],说明[15])。比率()约为0.65(哪个更好) 我希望这会有所帮助。您的逻辑的问题是,每次从数组中删除一个项时,索引都会重新排列,并跳过其中的一个字符串。例如: 假设这是数组: 说明:[“A”、“A”、“A”、“B”、“C”] 文献1: i=0
SequenceMatcher(无,说明[1],说明[2])。比率()大约为0.98。但是使用SequenceMatcher(无,说明[0],说明[15])。比率()
约为0.65(哪个更好)
我希望这会有所帮助。您的逻辑的问题是,每次从数组中删除一个项时,索引都会重新排列,并跳过其中的一个字符串。例如:
假设这是数组:
说明:[“A”、“A”、“A”、“B”、“C”]
文献1:
i=0 -------------0
description[i]="A"
j=i+1 -------------1
description[j]="A"
similarity_ratio>0.6
del description[j]
现在,数组被重新索引,如下所示:
说明:[“A”、“A”、“B”、“C”]。下一步是:
j=j+1 ------------1+1= 2
说明[2]=“B”
您已跳过说明[1]=“A”
要解决此问题:
替换
与
如果删除。否则进行正常的j=j+1迭代删除列表中的项目时,j
的值不应改变(因为在下一次迭代中,该位置将出现不同的列表项目)。执行j=i+1
会在每次删除项目时重新启动迭代(这不是所需的)。在else条件下,更新后的代码现在仅增加j
def filter_descriptions(descriptions):
MAX_SIMILAR_ALLOWED = 0.6 #40% unique and 60% similar
i = 0
while i < len(descriptions):
print("Processing {}/{}...".format(i + 1, len(descriptions)))
desc_to_evaluate = descriptions[i]
j = i + 1
while j < len(descriptions):
similarity_ratio = SequenceMatcher(None, desc_to_evaluate, descriptions[j]).ratio()
if similarity_ratio > MAX_SIMILAR_ALLOWED:
del descriptions[j]
else:
j += 1
i += 1
return descriptions
def过滤器描述(描述):
允许的最大相似性=0.6#40%唯一性和60%相似性
i=0
而我允许的最大相似性:
del描述[j]
其他:
j+=1
i+=1
返回说明
如果颠倒逻辑,您可以避免修改列表,同时减少所需的比较次数。也就是说,从一个空的输出/唯一列表开始,迭代描述,看看是否可以添加每个描述。因此,对于第一个描述,您可以立即添加它,因为它不能与空列表中的任何内容相似。第二个描述只需要与第一个描述进行比较,而不是与所有其他描述进行比较。以后的迭代一旦找到与之相似的前一个描述(并丢弃候选描述),就会短路。即
示例:
assert unique([2,3,4,5,1,2,3,3,2,1]) == [2, 3, 4, 5, 1]
# note that order is preserved
assert unique([1, 2, 0, 3, 4, 5], compare=(lambda x, y: abs(x - y) <= 1))) == [1, 3, 5]
# using a custom comparison function we can exclude items that are too similar to previous
# items. Here 2 and 0 are excluded because they are too close to 1 which was accepted
# as unique first. Change the order of 3 and 4, and then 5 would also be excluded.
比较的次数应该完全相同。也就是说,在您的实现中,第一个元素与所有其他元素进行比较,第二个元素仅与被认为与第一个元素不相似的元素进行比较,依此类推。在这个实现中,最初不会将第一个项目与任何项目进行比较,但必须将所有其他项目与之进行比较,才能将其添加到唯一列表中。只有被认为与第一个项目不相似的项目才会与第二个唯一项目进行比较,依此类推
unique
实现将减少复制,因为它只需在备份阵列空间不足时复制unique列表。然而,对于del
语句,每次使用时必须复制列表的部分内容(将所有后续项移动到新的正确位置)。不过,这可能会对性能产生微不足道的影响,因为瓶颈可能是序列匹配器中的比率计算。我无法理解您在第一次实现中面临的问题?你能解释清楚它有什么问题吗?“最后的字符串太相似”是什么意思?帮助我们了解它是如何problem@JohnDoe请看编辑1是的,这似乎是问题所在。但是我认为只有当一个项目从列表中删除时才需要执行j=I+1
。如果未删除任何项,则j
只应递增(1),否则将使其变成无限循环。是的,必须保留“j=j+1”以在数组元素中循环。除此之外,如果您删除了一个项目,您必须添加一个“j=j+i”。@MJB如果答案有用,请接受/投票感谢您采用不同的方法来处理@Dunes。我将对这两种方法进行基准测试,并在这里分享结果。SequenceMatcher花费约3分钟10秒,反转逻辑花费约100微秒?听起来太快了。。。在这种速度下,CPU只能对输入列表中的每个项目执行大约1-10条指令。让我觉得我的逻辑或你执行的测试有问题。我自己的microbench标记显示独特的
版本,如果列表中所有内容都相似,则速度会快10倍,如果列表中所有内容都不相似,则速度会快2倍。没有比你报道的7个数量级更重要的了。您的版本具有破坏性(修改输入列表)。在运行了您的版本后,您是否在同一个列表上运行了我的版本?激动不已的是,我可能弄错了单元:)这是datetime.now()的输出。开始顺序匹配器:2018-10-23 16:43:37.221874结束顺序匹配器:2018-10-23 16:46:47.316437开始反转器:2018-10-23 16:46:47.316519结束反转器:2018-10-23 16:46:47.333900
j+=1
j=i+1
def filter_descriptions(descriptions):
MAX_SIMILAR_ALLOWED = 0.6 #40% unique and 60% similar
i = 0
while i < len(descriptions):
print("Processing {}/{}...".format(i + 1, len(descriptions)))
desc_to_evaluate = descriptions[i]
j = i + 1
while j < len(descriptions):
similarity_ratio = SequenceMatcher(None, desc_to_evaluate, descriptions[j]).ratio()
if similarity_ratio > MAX_SIMILAR_ALLOWED:
del descriptions[j]
else:
j += 1
i += 1
return descriptions
import operator
def unique(items, compare=operator.eq):
# compare is a function that returns True if its two arguments are deemed similar to
# each other and False otherwise.
unique_items = []
for item in items:
if not any(compare(item, uniq) for uniq in unique_items):
# any will stop as soon as compare(item, uniq) returns True
# you could also use `if all(not compare(item, uniq) ...` if you prefer
unique_items.append(item)
return unique_items
assert unique([2,3,4,5,1,2,3,3,2,1]) == [2, 3, 4, 5, 1]
# note that order is preserved
assert unique([1, 2, 0, 3, 4, 5], compare=(lambda x, y: abs(x - y) <= 1))) == [1, 3, 5]
# using a custom comparison function we can exclude items that are too similar to previous
# items. Here 2 and 0 are excluded because they are too close to 1 which was accepted
# as unique first. Change the order of 3 and 4, and then 5 would also be excluded.
MAX_SIMILAR_ALLOWED = 0.6 #40% unique and 60% similar
def description_cmp(candidate_desc, unique_desc):
# use unique_desc as first arg as this keeps the argument order the same as with your filter
# function where the first description is the one that is retained if the two descriptions
# are deemed to be too similar
similarity_ratio = SequenceMatcher(None, unique_desc, candidate_desc).ratio()
return similarity_ratio > MAX_SIMILAR_ALLOWED
def filter_descriptions(descriptions):
# This would be the new definition of your filter_descriptions function
return unique(descriptions, compare=descriptions_cmp)