목록을 집합으로 변환하면 요소 순서가 변경됩니다.


119

최근에 나는 내가 변환하고 때 눈치 listset요소의 순서를 변경하고 문자로 정렬됩니다.

이 예를 고려하십시오.

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. 초기 주문을 잃지 않고 설정 작업 (특히 차액 설정)을 수행하려면 어떻게해야합니까?

8
특히 집합 작업을 수행하는 경우 초기 주문을 잃고 싶지 않습니까? "순서"는 파이썬뿐만 아니라 수학에서도 집합에 대한 의미없는 개념입니다.
Karl Knechtel 2012 년

131
@KarlKnechtel-예 "순서는 수학에서 집합에 대한 무의미한 개념입니다."하지만 실제 문제가 있습니다. :)
d.putto

CPython 3.6 이상에서 unique = list(dict.fromkeys([1, 2, 1]).keys()). dicts는 지금 삽입 순서를 보존하기 때문에 작동합니다 .
Boris

답변:


106
  1. A 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 3.7부터 삽입 순서를 유지하는 Python 사전의 키를 사용할 수 있습니다.

    >>> 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 도 사용할 수 있습니다 . 참고 a.keys() - b.keys()A와 차 집합을 반환 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)])

3
개체 비용은 16 바이트가 아닙니다. 기본 OrderedSet () 만있는 경우. :(

2
@Sean 아니, 그들은하지 않습니다. None언어 보장 싱글 톤입니다. CPython에서 실제 비용은 포인터 일뿐입니다 (비용은 항상 존재하지만 dict의 경우 거의 고려할 수 None있고 다른 싱글 톤 또는 공유 참조 "무료"). 따라서 최신 컴퓨터에서는 기계어가 8 바이트 일 가능성이 높습니다. . 그러나 네, 세트만큼 공간 효율적이지 않습니다.
juanpa.arrivillaga

2
CPython 3.6 이상 dict.fromkeys([1, 2, 1]).keys()에서는 일반 dicts도 순서를 유지하기 때문에 할 수 있습니다.
Boris

@Boris 이것은 Python 3.7부터 시작하는 언어 사양의 일부일뿐입니다. CPython 구현은 이미 버전 3.6에서 삽입 순서를 유지하지만 다른 Python 구현이 뒤 따르지 않을 수 있는 구현 세부 사항으로 간주됩니다 .
Sven Marnach

@ Sven 나는 CPython을 말했다. 나는 이것을 어디에나 게시한다. 나는 "CPython 3.6 또는 Python 3.7로 시작하는 다른 구현"을 작성하는 것에 지쳐있다. 중요하지 않습니다. 모두가 CPython을 사용하고 있습니다
Boris

52

Python 3.6에서는 set()이제 순서를 유지 해야 하지만 Python 2 및 3에 대한 또 다른 솔루션이 있습니다.

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

8
주문 보존에 관한 두 가지 참고 사항 : Python 3.6에서만, 심지어 거기에서도 구현 세부 사항으로 간주되므로 의존하지 마십시오. 그 외에는 x.index호출 될 때마다 선형 검색이 수행 되기 때문에 코드가 매우 비효율적 입니다. 2 차 복잡도에 문제가 없으면 set처음부터 a를 사용할 이유가 없습니다 .
Thijs van Dien

27
@ThijsvanDien이 잘못, set()파이썬 3.6, 심지어 구현 세부로, 당신있는 거 생각에 정렬되지 않은 dictS
Chris_Rands

8
때문에 때때로 그렇게 표시하지만 @ThijsvanDien 아니 그들은, 분류하지 않는 int자신에 자주의 해시 stackoverflow.com/questions/45581901/...
Chris_Rands

3
시도 x=[1,2,-1,20,6,210]하고 그것을 설정합니다. Python 3.6에서 테스트 된 순서가 전혀 없음을 알 수 있습니다.
GabrielChu

3
이 답변에 너무 많은 찬성표가있는 이유를 이해할 수 없으며 게재 순서를 유지하지 않으며 집합도 반환하지 않습니다.
Igor Rodriguez

20

첫 번째 질문에 대한 답으로 집합은 집합 작업에 최적화 된 데이터 구조입니다. 수학적 집합과 마찬가지로 요소의 특정 순서를 적용하거나 유지하지 않습니다. 집합의 추상 개념은 순서를 적용하지 않으므로 구현이 필요하지 않습니다. 목록에서 집합을 만들 때 Python은 집합 작업을 효율적으로 수행 할 수있는 집합에 대해 사용하는 내부 구현의 요구에 따라 요소의 순서를 자유롭게 변경할 수 있습니다.


9

아래 기능으로 중복 제거 및 순서 유지

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

이 링크를 확인


좋은 것, 내 솔루션보다 훨씬 낫다 :)
Tiger-222

8

수학에서, 거기 세트명령 세트 (osets).

  • set : 고유 요소의 정렬되지 않은 컨테이너 (구현 됨)
  • oset : 고유 요소의 정렬 된 컨테이너 (NotImplemented)

Python에서는 세트 만 직접 구현됩니다. 일반 사전 키 ( 3.7+ )로 osets를 에뮬레이트 할 수 있습니다 .

주어진

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]

딕셔너리 키에 대한 세트와 유사한 작업.

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)이 두 가지 더 매력적인 수학적 데이터 구조 라는 사실을 알게되면 기뻐할 수 있습니다 .

  • list : 복제를 허용하는 정렬 된 요소 컨테이너 (구현 됨)
  • mset : 복제를 허용하는 요소의 정렬되지 않은 컨테이너 (NotImplemented) *

요약

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

* 다중 집합은 collections.Counter()딕셔너리와 유사한 다중도 (개수) 매핑을 사용하여 간접적으로 에뮬레이션 할 수 있습니다 .


4

다른 답변에서 알 수 있듯이 세트는 요소 순서를 유지하지 않는 데이터 구조 (및 수학적 개념)입니다.

그러나 세트와 사전의 조합을 사용하면 원하는대로 얻을 수 있습니다. 다음 스 니펫을 사용해보세요.

# 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

1

Sven의 답변을 바탕으로 collections.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]

당신이해야 할 list(z.keys())목록의 출력을 얻을 수 있습니다.
jxn

파이썬 3에서는 그렇습니다. 내가 지정 했어야했지만 Python 2에는 없습니다.
jimh

0

위의 최고 점수 개념을 구현하여 목록으로 되돌립니다.

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에서 (간단히) 테스트되었습니다.


0

두 개의 초기 목록에 차이 연산을 설정하려는 요소가 적은 경우 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]

시간의 복잡성은 그다지 좋지 않지만 깔끔하고 읽기 쉽습니다.


0

사람들이 이론 과학의 정의에 대해 농담을하기 위해 항상 '현실 세계 문제'를 사용하는 것은 흥미 롭습니다.

세트에 순서가 있으면 먼저 다음 문제를 파악해야합니다. 목록에 중복 요소가있는 경우 집합으로 전환 할 때 순서는 무엇입니까? 두 세트를 합치는 순서는 무엇입니까? 동일한 요소에서 순서가 다른 두 세트를 교차하면 순서는 무엇입니까?

또한 set는 세트 작업에 매우 좋은 특정 키를 검색하는 데 훨씬 더 빠릅니다 (이것이 목록이 아닌 세트가 필요한 이유입니다).

색인에 대해 정말로 관심이 있다면 목록으로 유지하십시오. 여전히 많은 목록의 요소에 대해 집합 작업을 수행하려면 가장 간단한 방법은 원래 목록에있는 키의 모든 인덱스를 포함하는 목록 값과 함께 집합의 동일한 키를 사용하여 각 목록에 대한 사전을 만드는 것입니다.

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}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.