我有一对配对列表:
[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]
我想删除任何重复的地方
[a,b] == [b,a]
所以我们最终只有
[0, 1], [0, 4], [1, 4]
我可以做一个内心的&外部循环检查反向对并附加到列表中,如果不是这样,但我确信有更多的Pythonic方法可以实现相同的结果。
答案 0 :(得分:18)
如果您需要保留列表中元素的顺序,则可以使用sorted
函数并使用map
设置理解,如下所示:
lst = [0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]
data = {tuple(item) for item in map(sorted, lst)}
# {(0, 1), (0, 4), (1, 4)}
或者根本就没有map
:
data = {tuple(sorted(item)) for item in lst}
另一种方法是使用显示为here的frozenset
,但请注意,只有列表中包含不同的元素才能使用此功能。因为像set
一样,frozenset
始终包含唯一值。因此,您最终会在子列表中找到唯一的值(丢失数据),这可能不是您想要的。
要输出列表,您始终可以使用list(map(list, result))
,其中result只是Python-3.0或更新版本中的一组元组。
答案 1 :(得分:14)
如果您只想删除反向对并且不想要外部库,则可以使用简单的生成器函数(基于itertools
"unique_everseen" recipe):
def remove_reversed_duplicates(iterable):
# Create a set for already seen elements
seen = set()
for item in iterable:
# Lists are mutable so we need tuples for the set-operations.
tup = tuple(item)
if tup not in seen:
# If the tuple is not in the set append it in REVERSED order.
seen.add(tup[::-1])
# If you also want to remove normal duplicates uncomment the next line
# seen.add(tup)
yield item
>>> list(remove_reversed_duplicates(a))
[[0, 1], [0, 4], [1, 4]]
生成器函数可能是解决此问题的一种非常快捷的方法,因为set-lookups非常便宜。 此方法还保留了初始列表的顺序,而仅删除了反向重复项,而更快比大多数替代项
如果您不介意使用外部库并且想要删除所有重复项(反向和相同),则可以选择:iteration_utilities.unique_everseen
>>> a = [[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]]
>>> from iteration_utilities import unique_everseen
>>> list(unique_everseen(a, key=set))
[[0, 1], [0, 4], [1, 4]]
这将检查任何项目是否具有与其他项目相同的仲裁顺序(因此key=set
)。在这种情况下,这可以按预期工作,但它也会删除重复的[a, b]
而不是[b, a]
次出现。您也可以使用key=sorted
(与其他答案一样)。像unique_everseen
这样的算法复杂度很差,因为key
函数的结果不可清除,因此快速查找被慢速查找替换。为了加快这一点,你需要使密钥可以清除,例如将它们转换为已排序的元组(如其他一些答案所示):
>>> from iteration_utilities import chained
>>> list(unique_everseen(a, key=chained(sorted, tuple)))
[[0, 1], [0, 4], [1, 4]]
chained
只不过是lambda x: tuple(sorted(x))
的更快替代品。
编辑:正如@ jpmc26所提到的,可以使用frozenset
代替普通集:
>>> list(unique_everseen(a, key=frozenset))
[[0, 1], [0, 4], [1, 4]]
为了了解性能,我对不同的建议进行了一些timeit
比较:
>>> a = [[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]]
>>> %timeit list(remove_reversed_duplicates(a))
100000 loops, best of 3: 16.1 µs per loop
>>> %timeit list(unique_everseen(a, key=frozenset))
100000 loops, best of 3: 13.6 µs per loop
>>> %timeit list(set(map(frozenset, a)))
100000 loops, best of 3: 7.23 µs per loop
>>> %timeit list(unique_everseen(a, key=set))
10000 loops, best of 3: 26.4 µs per loop
>>> %timeit list(unique_everseen(a, key=chained(sorted, tuple)))
10000 loops, best of 3: 25.8 µs per loop
>>> %timeit [list(tpl) for tpl in list(set([tuple(sorted(pair)) for pair in a]))]
10000 loops, best of 3: 29.8 µs per loop
>>> %timeit set(tuple(item) for item in map(sorted, a))
10000 loops, best of 3: 28.5 µs per loop
包含许多重复项的长列表:
>>> import random
>>> a = [[random.randint(0, 10), random.randint(0,10)] for _ in range(10000)]
>>> %timeit list(remove_reversed_duplicates(a))
100 loops, best of 3: 12.5 ms per loop
>>> %timeit list(unique_everseen(a, key=frozenset))
100 loops, best of 3: 10 ms per loop
>>> %timeit set(map(frozenset, a))
100 loops, best of 3: 10.4 ms per loop
>>> %timeit list(unique_everseen(a, key=set))
10 loops, best of 3: 47.7 ms per loop
>>> %timeit list(unique_everseen(a, key=chained(sorted, tuple)))
10 loops, best of 3: 22.4 ms per loop
>>> %timeit [list(tpl) for tpl in list(set([tuple(sorted(pair)) for pair in a]))]
10 loops, best of 3: 24 ms per loop
>>> %timeit set(tuple(item) for item in map(sorted, a))
10 loops, best of 3: 35 ms per loop
重复次数减少:
>>> a = [[random.randint(0, 100), random.randint(0,100)] for _ in range(10000)]
>>> %timeit list(remove_reversed_duplicates(a))
100 loops, best of 3: 15.4 ms per loop
>>> %timeit list(unique_everseen(a, key=frozenset))
100 loops, best of 3: 13.1 ms per loop
>>> %timeit set(map(frozenset, a))
100 loops, best of 3: 11.8 ms per loop
>>> %timeit list(unique_everseen(a, key=set))
1 loop, best of 3: 1.96 s per loop
>>> %timeit list(unique_everseen(a, key=chained(sorted, tuple)))
10 loops, best of 3: 24.2 ms per loop
>>> %timeit [list(tpl) for tpl in list(set([tuple(sorted(pair)) for pair in a]))]
10 loops, best of 3: 31.1 ms per loop
>>> %timeit set(tuple(item) for item in map(sorted, a))
10 loops, best of 3: 36.7 ms per loop
因此,remove_reversed_duplicates
,unique_everseen
(key=frozenset
)和set(map(frozenset, a))
的变体似乎是目前最快的解决方案。哪一个取决于输入的长度和重复的数量。
答案 2 :(得分:6)
set(map(frozenset, lst))
如果这些对在逻辑上是无序的,则它们更自然地表示为集合。在你达到这一点之前将它们作为集合更好,但你可以像这样转换它们:
lst = [[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]]
lst_as_sets = map(frozenset, lst)
然后在迭代中消除重复的自然方法是将其转换为set
:
deduped = set(lst_as_sets)
(这是我在第一步中选择frozenset
的主要原因。可变set
s不可清,因此无法将其添加到set
。)
或者你可以像TL中那样在一行中进行; DR部分。
我认为这更简单,更直观,并且与您对数据的思考的方式更加匹配,而不是与排序和元组混淆。
如果出于某种原因,您确实需要list
list
作为最终结果,那么转换回来是微不足道的:
result_list = list(map(list, deduped))
但是尽可能长时间地将它全部留作set
可能更合乎逻辑。我只能想到你可能需要这个的一个原因,以及它与现有代码/库的兼容性。
答案 3 :(得分:4)
您可以对每对进行排序,将您的对列表转换为一组元组,然后再将其转换回来:
l = [[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]]
[list(tpl) for tpl in list(set([tuple(sorted(pair)) for pair in l]))]
#=> [[0, 1], [1, 4], [0, 4]]
这些步骤可能比长期单行更容易理解:
>>> l = [[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]]
>>> [sorted(pair) for pair in l]
# [[0, 1], [0, 4], [0, 1], [1, 4], [0, 4], [1, 4]]
>>> [tuple(pair) for pair in _]
# [(0, 1), (0, 4), (0, 1), (1, 4), (0, 4), (1, 4)]
>>> set(_)
# set([(0, 1), (1, 4), (0, 4)])
>>> list(_)
# [(0, 1), (1, 4), (0, 4)]
>>> [list(tpl) for tpl in _]
# [[0, 1], [1, 4], [0, 4]]
答案 4 :(得分:4)
您可以使用内置filter
功能。
from __future__ import print_function
def my_filter(l):
seen = set()
def not_seen(it):
s = min(*it), max(*it)
if s in seen:
return False
else:
seen.add(s)
return True
out = filter(not_seen, l)
return out
myList = [[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]]
print(my_filter(myList)) # [[0, 1], [0, 4], [1, 4]]
作为补充,我会将你定位到描述unique_everseen
函数的Python itertools module,该函数与上面的函数基本相同,但是在基于生成器的惰性版本中。如果您正在处理大型阵列,可能比我们的任何解决方案都要好。以下是如何使用它:
from itertools import ifilterfalse
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
gen = unique_everseen(myList, lambda x: (min(x), max(x))) # gen is an iterator
print(gen) # <generator object unique_everseen at 0x7f82af492fa0>
result = list(gen) # consume generator into a list.
print(result) # [[0, 1], [0, 4], [1, 4]]
我还没有做任何指标来查看谁是最快的。然而,在这个版本中,内存效率和O复杂度似乎更好。
内置sorted
函数可以传递给unique_everseen
来订购内部向量中的项目。相反,我通过lambda x: (min(x), max(x))
。因为我知道矢量大小正好是2,所以我可以像这样进行。
要使用sorted
我需要传递lambda x: tuple(sorted(x))
,这会增加开销。不是戏剧性的,但仍然。
myList = [[random.randint(0, 10), random.randint(0,10)] for _ in range(10000)]
timeit.timeit("list(unique_everseen(myList, lambda x: (min(x), max(x))))", globals=globals(), number=20000)
>>> 156.81979029000013
timeit.timeit("list(unique_everseen(myList, lambda x: tuple(sorted(x))))", globals=globals(), number=20000)
>>> 168.8286430349999
在Python 3中完成计时,将globals
kwarg添加到timeit.timeit
。
答案 5 :(得分:3)
一个简单的 unnested 解决方案:
pairs = [[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]]
s=set()
for p in pairs:
# Lists are unhashable so make the "elements" into tuples
p = tuple(p)
if p not in s and p[::-1] not in s:
s.add(p)
print s
答案 6 :(得分:3)
首先对每个列表进行排序,然后使用词典键获取一组唯一的元素,并列出列表理解。
为什么选择元组?为了避免使用&#34; unhashable&#34;通过fromkeys()函数时出错
my_list = [[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]]
tuple_list = [ tuple(sorted(item)) for item in my_list ]
final_list = [ list(item) for item in list({}.fromkeys(tuple_list)) ]
使用OrderedDict甚至可以保留列表顺序。
from collections import OrderedDict
my_list = [[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]]
tuple_list = [ tuple(sorted(item)) for item in my_list ]
final_list = [ list(item) for item in list(OrderedDict.fromkeys(tuple_list)) ]
以上代码将生成所需的列表
[[0, 1], [0, 4], [1, 4]]
答案 7 :(得分:1)
如果对和配对项的顺序很重要,那么通过测试成员资格来创建新列表可能就是这里的方法。
pairs = [0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]
no_dups = []
for pair in pairs:
if not any( all( i in p for i in pair ) for p in no_dups ):
no_dups.append(pair)
否则,我会选择Styvane's answer。
顺便提一下,上述解决方案不适用于匹配对的情况。例如,[0,0]
不会添加到列表中。为此,您需要添加一个额外的支票:
for pair in pairs:
if not any( all( i in p for i in pair ) for p in no_dups ) or ( len(set(pair)) == 1 and not pair in no_dups ):
no_dups.append(pair)
但是,该解决方案不会选择空“对”(例如[]
)。为此,您还需要进行一次调整:
if not any( all( i in p for i in pair ) for p in no_dups ) or ( len(set(pair)) in (0,1) and not pair in no_dups ):
no_dups.append(pair)
要求and not pair in no_dups
位阻止将[0,0]
或[]
添加到no_dups
两次。
答案 8 :(得分:1)
好吧,我是&#34;检查反向对并附加到列表中,如果情况并非如此&#34;正如你所说,你可以这样做,但我使用的是单循环。
bundle update rmagick
现有答案的优势在于,IMO更具可读性。这里不需要深入了解标准库。没有跟踪任何复杂的事情。对于初学者而言,唯一可能不熟悉的概念是x=[[0, 1], [0, 4], [1, 0], [1, 4], [4, 0], [4, 1]]
out = []
for pair in x:
if pair[::-1] not in out:
out.append(pair)
print out
还原该对。
但性能为O(n ** 2),因此如果性能问题和/或列表很大,请不要使用。