将列表转换为集合会改变元素的顺序

最近我注意到,当我将list转换为set时,元素的顺序发生了变化,并按字符排序。

想想这个例子:

x=[1,2,20,6,210]
print(x)
# [1, 2, 20, 6, 210] # the order is same as initial order


set(x)
# set([1, 2, 20, 210, 6]) # in the set(x) output order is sorted

我的问题是

  1. 为什么会这样?
  2. 如何才能在不丢失初始顺序的情况下进行设置操作(特别是设置差异)?
232257 次浏览
  1. A < A href="https://docs.python.org/library/stdtypes.html#set-types-set-frozenset" rel="noreferrer">set是一个无序的数据结构,因此它不保留插入顺序。

  2. 这取决于你的要求。如果你有一个普通的列表,并且想要删除一些元素,同时保持列表的顺序,你可以使用列表推导式来做到这一点:

    >>> a = [1, 2, 20, 6, 210]
    >>> b = set([6, 20, 1])
    >>> [x for x in a if x not in b]
    [2, 210]
    

    如果你需要一个同时支持快速成员关系测试插入顺序的保留的数据结构,你可以使用Python字典的键,从Python 3.7开始保证保持插入顺序:

    >>> a = dict.fromkeys([1, 2, 20, 6, 210])
    >>> b = dict.fromkeys([6, 20, 1])
    >>> dict.fromkeys(x for x in a if x not in b)
    {2: None, 210: None}
    

    b在这里并不需要排序——你也可以使用set。注意,a.keys() - b.keys()将设置的差值返回为set,因此它不会保留插入顺序。

    在较旧版本的Python中,可以使用collections.OrderedDict:

    >>> a = collections.OrderedDict.fromkeys([1, 2, 20, 6, 210])
    >>> b = collections.OrderedDict.fromkeys([6, 20, 1])
    >>> collections.OrderedDict.fromkeys(x for x in a if x not in b)
    OrderedDict([(2, None), (210, None)])
    

回答你的第一个问题,集合是为集合操作优化的数据结构。像数学集合一样,它不强制或维护元素的任何特定顺序。集合的抽象概念并不强制执行顺序,因此不需要实现。当你从列表中创建一个set时,Python可以根据它用于set的内部实现的需要自由地改变元素的顺序,这能够有效地执行set操作。

正如在其他答案中指出的那样,集合是不保留元素顺序的数据结构(和数学概念)

然而,通过使用集合和字典的组合,你可以实现任何你想要的-尝试使用这些片段:

# save the element order in a dict:
x_dict = dict(x,y for y, x in enumerate(my_list) )
x_set = set(my_list)
#perform desired set operations
...
#retrieve ordered list from the set:
new_list = [None] * len(new_set)
for element in new_set:
new_list[x_dict[element]] = element

基于Sven的回答,我发现使用集合。OrderedDict像这样帮助我完成你想要的,并允许我添加更多的项目到dict:

import collections


x=[1,2,20,6,210]
z=collections.OrderedDict.fromkeys(x)
z
OrderedDict([(1, None), (2, None), (20, None), (6, None), (210, None)])

如果你想添加项目,但仍然把它当作一个集合,你可以这样做:

z['nextitem']=None

你可以在dict上执行类似z.keys()的操作并获得集合:

z.keys()
[1, 2, 20, 6, 210]

这里有一个简单的方法:

x=[1,2,20,6,210]
print sorted(set(x))

在Python 3.6中,set()现在应该保持顺序,但是有另一个解决方案的Python 2和3:

>>> x = [1, 2, 20, 6, 210]
>>> sorted(set(x), key=x.index)
[1, 2, 20, 6, 210]

上面的最高分概念的实现,将它带回一个列表:

def SetOfListInOrder(incominglist):
from collections import OrderedDict
outtemp = OrderedDict()
for item in incominglist:
outtemp[item] = None
return(list(outtemp))

在Python 3.6和Python 2.7上测试(简要)。

如果你在你的两个初始列表中有少量的元素,你想要对其进行集差操作,而不是使用collections.OrderedDict,这会使实现变得复杂并使其可读性较差,你可以使用:

# initial lists on which you want to do set difference
>>> nums = [1,2,2,3,3,4,4,5]
>>> evens = [2,4,4,6]
>>> evens_set = set(evens)
>>> result = []
>>> for n in nums:
...   if not n in evens_set and not n in result:
...     result.append(n)
...
>>> result
[1, 3, 5]


它的时间复杂度不是很好,但它很简洁,易于阅读。

删除重复和保存顺序以下功能

def unique(sequence):
seen = set()
return [x for x in sequence if not (x in seen or seen.add(x))]

如何在Python中保留顺序的同时从列表中删除重复项

在数学中,有命令集(偏移量)。

  • :唯一元素的无序容器(已实现)
  • oset:唯一元素的有序容器

在Python中,只有集合是直接实现的。我们可以用常规字典键(3.7 +)来模拟偏移量。

鉴于

a = [1, 2, 20, 6, 210, 2, 1]
b = {2, 6}

代码

oset = dict.fromkeys(a).keys()
# dict_keys([1, 2, 20, 6, 210])

演示

删除复制,保留插入顺序。

list(oset)
# [1, 2, 20, 6, 210]

字典键上类似set的操作。

oset - b
# {1, 20, 210}


oset | b
# {1, 2, 5, 6, 20, 210}


oset & b
# {2, 6}


oset ^ b
# {1, 5, 20, 210}

细节

注意:无序结构并不排除有序元素。相反,维持秩序并不能得到保证。例子:

assert {1, 2, 3} == {2, 3, 1}                    # sets (order is ignored)

assert [1, 2, 3] != [2, 3, 1]                    # lists (order is guaranteed)

你可能会很高兴地发现列表多重集 (mset)是两个更迷人的数学数据结构:

  • 列表:允许复制的有序元素容器(已实现)
  • mset:允许复制的无序元素容器(NotImplemented

总结

Container | Ordered | Unique | Implemented
----------|---------|--------|------------
set       |    n    |    y   |     y
oset      |    y    |    y   |     n
list      |    y    |    n   |     y
mset      |    n    |    n   |     n*

*一个多集可以间接地用collections.Counter()模拟,一个字典式的映射< A href="http://mathworld.wolfram.com/Multiplicity.html" rel="noreferrer"> multipl< / A > (counts)。

有趣的是,人们总是用“现实问题”来开理论科学定义的玩笑。

如果set有顺序,首先需要解决以下问题。 如果你的列表有重复的元素,当你把它变成一个集合时,顺序应该是什么?如果我们合并两个集合,顺序是什么?如果我们在相同的元素上相交两个不同顺序的集合是什么顺序?< / p >

另外,set在搜索特定键时要快得多,这在set操作中非常好(这就是为什么你需要set,而不是list)。

如果您真的关心索引,只需将其保存为列表即可。如果您仍然想对许多列表中的元素执行set操作,最简单的方法是为每个具有相同键的列表创建一个字典,并创建一个list值,其中包含原始列表中键的所有索引。

def indx_dic(l):
dic = {}
for i in range(len(l)):
if l[i] in dic:
dic.get(l[i]).append(i)
else:
dic[l[i]] = [i]
return(dic)


a = [1,2,3,4,5,1,3,2]
set_a  = set(a)
dic_a = indx_dic(a)


print(dic_a)
# {1: [0, 5], 2: [1, 7], 3: [2, 6], 4: [3], 5: [4]}
print(set_a)
# {1, 2, 3, 4, 5}

你可以用一行代码删除重复的值并保持插入的列表顺序,Python 3.8.2

mylist = ['b', 'b', 'a', 'd', 'd', 'c']




results = list({value:"" for value in mylist})


print(results)


>>> ['b', 'a', 'd', 'c']


results = list(dict.fromkeys(mylist))


print(results)


>>> ['b', 'a', 'd', 'c']

我们可以使用集合。计数器来实现:

# tested on python 3.7
>>> from collections import Counter
>>> lst = ["1", "2", "20", "6", "210"]


>>> for i in Counter(lst):
>>>     print(i, end=" ")
1 2 20 6 210


>>> for i in set(lst):
>>>     print(i, end=" ")
20 6 2 1 210

如果愿意,可以删除重复的值并保持插入的列表顺序

lst = [1,2,1,3]
new_lst = []


for num in lst :
if num not in new_lst :
new_lst.append(num)


# new_lst = [1,2,3]

如果你想要的是“order”,不要使用“sets”来删除重复,

使用集合进行搜索,即
X在列表中
花费O(n)时间
其中< br > X在set
花费O(1)时间*大多数情况

迟了,但你可以用熊猫,pd。转换列表,同时保持顺序:

import pandas as pd
x = pd.Series([1, 2, 20, 6, 210, 2, 1])
print(pd.unique(x))

< >强输出: 数组([1,2,20,6,210])

适用于字符串列表

x = pd.Series(['c', 'k', 'q', 'n', 'p','c', 'n'])
print(pd.unique(x))

< >强输出 ['c' 'k' 'q' 'n' 'p']

另一种更简单的方法是创建一个空列表,例如“unique_list”;添加原始列表中唯一的元素,例如:

unique_list=[]


for i in original_list:
if i not in unique_list:
unique_list.append(i)
else:
pass

这将为您提供所有独特的元素,并保持顺序。