Python 有效地从列表列表中删除重复项(顺序不可知)
以下列表中有一些重复的子列表,其中的元素顺序不同:Python 有效地从列表列表中删除重复项(顺序不可知),python,python-3.x,list,list-comprehension,unordered,Python,Python 3.x,List,List Comprehension,Unordered,以下列表中有一些重复的子列表,其中的元素顺序不同: l1 = [ ['The', 'quick', 'brown', 'fox'], ['hi', 'there'], ['jumps', 'over', 'the', 'lazy', 'dog'], ['there', 'hi'], ['jumps', 'dog', 'over','lazy', 'the'], ] 如何删除重复项,保留看到的第一个实例,以获得: l1 = [ ['The', 'q
l1 = [
['The', 'quick', 'brown', 'fox'],
['hi', 'there'],
['jumps', 'over', 'the', 'lazy', 'dog'],
['there', 'hi'],
['jumps', 'dog', 'over','lazy', 'the'],
]
如何删除重复项,保留看到的第一个实例,以获得:
l1 = [
['The', 'quick', 'brown', 'fox'],
['hi', 'there'],
['jumps', 'over', 'the', 'lazy', 'dog'],
]
我试图:
[list(i) for i in set(map(tuple, l1))]
尽管如此,我不知道这是否是处理大型列表的最快方法,我的尝试也没有达到预期效果。你知道如何有效地移除它们吗?这个有点棘手。您希望为冻结计数器的dict设置关键帧,但在Python中计数器是不可散列的。对于渐进复杂性的小幅度降低,您可以使用排序元组替代冻结计数器:
seen = set()
result = []
for x in l1:
key = tuple(sorted(x))
if key not in seen:
result.append(x)
seen.add(key)
一行中的相同想法如下所示:
[*{tuple(sorted(k)): k for k in reversed(l1)}.values()][::-1]
这个有点棘手。您希望为冻结计数器的dict设置关键帧,但在Python中计数器是不可散列的。对于渐进复杂性的小幅度降低,您可以使用排序元组替代冻结计数器:
seen = set()
result = []
for x in l1:
key = tuple(sorted(x))
if key not in seen:
result.append(x)
seen.add(key)
一行中的相同想法如下所示:
[*{tuple(sorted(k)): k for k in reversed(l1)}.values()][::-1]
@wim的答案效率低下,因为它将列表项排序作为唯一标识列表项的一组计数的一种方式,每个子列表的时间复杂度为O(n logn) 为了在线性时间复杂度中实现同样的效果,您可以在
collections.Counter
类中使用项计数的冻结集。由于dict CONTRUSION保留具有重复键的项目的最后一个值,并且您希望保留问题中具有重复键的项目的第一个值,因此您必须按照与列表相反的顺序构造dict,并在构造消除重复的子列表后再次将其反转:
from collections import Counter
list({frozenset(Counter(lst).items()): lst for lst in reversed(l1)}.values())[::-1]
这将返回:
[['The', 'quick', 'brown', 'fox'], ['hi', 'there'], ['jumps', 'over', 'the', 'lazy', 'dog']]
@wim的答案效率低下,因为它将列表项排序作为唯一标识列表项的一组计数的一种方式,每个子列表的时间复杂度为O(n logn) 为了在线性时间复杂度中实现同样的效果,您可以在
collections.Counter
类中使用项计数的冻结集。由于dict CONTRUSION保留具有重复键的项目的最后一个值,并且您希望保留问题中具有重复键的项目的第一个值,因此您必须按照与列表相反的顺序构造dict,并在构造消除重复的子列表后再次将其反转:
from collections import Counter
list({frozenset(Counter(lst).items()): lst for lst in reversed(l1)}.values())[::-1]
这将返回:
[['The', 'quick', 'brown', 'fox'], ['hi', 'there'], ['jumps', 'over', 'the', 'lazy', 'dog']]
这:
l2给出了已删除反向重复项的列表。
与之比较:此:
l2给出了已删除反向重复项的列表。
比较:我做了一个快速基准测试,比较了各种答案:
l1 = [['The', 'quick', 'brown', 'fox'], ['hi', 'there'], ['jumps', 'over', 'the', 'lazy', 'dog'], ['there', 'hi'], ['jumps', 'dog', 'over','lazy', 'the']]
from collections import Counter
def method1():
"""manually construct set, keyed on sorted tuple"""
seen = set()
result = []
for x in l1:
key = tuple(sorted(x))
if key not in seen:
result.append(x)
seen.add(key)
return result
def method2():
"""frozenset-of-Counter"""
return list({frozenset(Counter(lst).items()): lst for lst in reversed(l1)}.values())
def method3():
"""wim"""
return [*{tuple(sorted(k)): k for k in reversed(l1)}.values()][::-1]
from timeit import timeit
print(timeit(lambda: method1(), number=1000))
print(timeit(lambda: method2(), number=1000))
print(timeit(lambda: method3(), number=1000))
印刷品:
0.0025010189856402576
0.016385524009820074
0.0026451340527273715
我做了一个快速基准测试,比较了各种答案:
l1 = [['The', 'quick', 'brown', 'fox'], ['hi', 'there'], ['jumps', 'over', 'the', 'lazy', 'dog'], ['there', 'hi'], ['jumps', 'dog', 'over','lazy', 'the']]
from collections import Counter
def method1():
"""manually construct set, keyed on sorted tuple"""
seen = set()
result = []
for x in l1:
key = tuple(sorted(x))
if key not in seen:
result.append(x)
seen.add(key)
return result
def method2():
"""frozenset-of-Counter"""
return list({frozenset(Counter(lst).items()): lst for lst in reversed(l1)}.values())
def method3():
"""wim"""
return [*{tuple(sorted(k)): k for k in reversed(l1)}.values()][::-1]
from timeit import timeit
print(timeit(lambda: method1(), number=1000))
print(timeit(lambda: method2(), number=1000))
print(timeit(lambda: method3(), number=1000))
印刷品:
0.0025010189856402576
0.016385524009820074
0.0026451340527273715
感谢您的帮助如果您不关心保留l1中条目的顺序,您可以使用
set(tuple(sorted(x))处理l1中的x)
。感谢您的帮助如果您不关心保留l1中条目的顺序,您可以使用set(tuple(sorted(x))处理l1中的x)
。感谢您的帮助我考虑过这一点,但是,除非数据中的内部列表非常长,否则构建所有这些冻结集和计数器实例的开销可能比一开始的排序要严重得多。i、 e.在实践中,O(k)上的大系数可能比渐近O(k*logk)更糟糕。@wim如果您考虑到这一点,您就不会说“计数器在Python中不可散列”,并且您的解决方案是以“性能的小幅度下降”为代价的。在不知道OP的实际用例的情况下,我们应该总是在更好的可伸缩性方面犯错。但是,哪一个轴的可伸缩性?我认为这里最明显的大数据案例是一个很长的短内部列表列表,在这种情况下,您的解决方案的可伸缩性会更差。渐进复杂性的降低不一定是性能的降低-你必须用真实数据来衡量才能得出这些结论。谢谢你的帮助我考虑了这一点,但是,除非数据中的内部列表非常长,否则构建所有这些冻结集和计数器实例的开销可能比一开始的排序要严重得多。i、 e.在实践中,O(k)上的大系数可能比渐近O(k*logk)更糟糕。@wim如果您考虑到这一点,您就不会说“计数器在Python中不可散列”,并且您的解决方案是以“性能的小幅度下降”为代价的。在不知道OP的实际用例的情况下,我们应该总是在更好的可伸缩性方面犯错。但是,哪一个轴的可伸缩性?我认为这里最明显的大数据案例是一个很长的短内部列表列表,在这种情况下,您的解决方案的可伸缩性会更差。渐近复杂性的降低不一定是性能的降低-你必须用真实数据进行测量才能得出这些结论。@wim你能解释一下输出是如何不正确的吗?我已经检查过了,似乎得到了正确的输出(三个嵌套列表)。问题中写入了预期的输出,这一个不匹配,因为顺序丢失了。虽然顺序已更改,但问题的实质似乎是删除具有类似元素的列表。@wim您能解释一下输出是如何不正确的吗?我已经检查过了,似乎得到了正确的输出(三个嵌套列表)预期的输出写在问题中,这一个不匹配,因为排序丢失了,排序已更改,但问题的实质似乎是删除具有类似元素的列表。这有点问题,因为字符串非常小(每个字符串有3或4个字符),相对于计数器方法,O(n log(n))几乎不重要,计数器方法具有更好的渐近复杂性,但分配开销更大。我认为一个更公平的方法应该是使用千长度字符串进行基准测试。这是排序