사전 매핑 반전 / 반전


답변:


923

Python 2.7.x의 경우

inv_map = {v: k for k, v in my_map.iteritems()}

Python 3+의 경우 :

inv_map = {v: k for k, v in my_map.items()}

4
최근 파이썬 2.7.x 버전 my_map.items()으로 잘 작동
발렌틴

29
값이 고르지 않으면 작동하지 않는 것을 제외하고 작동합니다. 이 경우 일부 항목을
잃게됩니다


2
예, 구현 세부 사항입니다. The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon. 그런 식으로 유지된다는 보장은 없으므로 Dict와 같은 동작 에 의존하는 코드를 작성하지 마십시오 OrderedDict.
Mattias

9
@Mattias, 이것은 Python 3.6에 해당됩니다. 버전 3.7의 경우 주문 보존은 mail.python.org/pipermail/python-dev/2017-December/151283.html 입니다. BDFL은 그렇게 말했다.
interDist

174

dict의 값이 고유하다고 가정하면 :

dict((v, k) for k, v in my_map.iteritems())

22
값도 해시 가능해야합니다
John La Rooy

30
@ Buttons840 : 값이 고유하지 않으면 어쨌든 사전의 고유 한 반전이 없거나 다른 말로하면 반전이 의미가 없습니다.
Wrzlprmft

2
@ Buttons840 마지막 키만 값으로 나타납니다. iteritems()출력 될 순서에 대한 보증은 없을 것이므로, 임의의 키가 고유하지 않은 값에 대해 어떤 조건에서는 명백히 재현 될 수있는 방식으로 할당되지만 일반적으로는 그렇지 않은 것으로 가정 될 수 있습니다.
Evgeni Sergeev

2
물론 파이썬 3에는 더 이상 iteritems()메소드 가 없으며이 접근법은 작동하지 않습니다. items()수락 된 답변에 표시된대로 대신 사용 하십시오. 또한 사전 이해력은이를 호출하는 것보다 더 예쁘게 만듭니다 dict.
Mark Amery

5
@Wrzlprmft 고유하지 않은 값의 경우 역에 대한 자연적인 정의가 있습니다. 모든 값은 해당 키 세트로 매핑됩니다.
Leo

135

의 값 my_map이 고유하지 않은 경우 :

inv_map = {}
for k, v in my_map.iteritems():
    inv_map[v] = inv_map.get(v, [])
    inv_map[v].append(k)

56
... 또는 그냥 inv_map.setdefault (v, []). append (k). 예전에는 불독 한 팬보이 였지만 너무 여러 번 망쳐 져서 실제로 명시 적이 암시 적보다 낫다는 결론을 내 렸습니다.
alsuren

이 답변은 멀티 맵에 대해 올바르지 않습니다. 여기에 추가하면 값이 매번 빈 목록으로 재설정되므로 쓸모가 없습니다. set_default를 사용해야합니다.
Yaroslav Bulatov

1
@YaroslavBulatov 아니오, 여기에 표시된 코드가 손상되지 않았습니다 inv_map.get(v, []). 이미 추가 된 목록이 있으면 목록을 반환하므로 할당이 빈 목록으로 재설정되지 않습니다. setdefault그래도 여전히 더 예쁘다.
Mark Amery

10
여기에 세트가 더 의미가 있습니다. 키는 (아마도) 해시 가능하며 순서가 없습니다. inv_map.setdefault(v, set()).add(k).
Artyer

1
python3에서는 my_map.items()대신 사용하십시오 my_map.iteritems().
apitsch

42

맵핑 유형을 유지하면서 ( dict또는 dict서브 클래스 인 경우) 이를 수행하려면 다음을 수행하십시오 .

def inverse_mapping(f):
    return f.__class__(map(reversed, f.items()))

4
영리 할 수도 있지만 원래 사전에서 둘 이상의 키가 동일한 값을 갖는 경우 작동하지 않습니다.
Rafael_Espericueta 2016 년

1
@Rafael_Espericueta 반복되는 값을 가진지도는 되돌릴 수 없기 때문에이 질문에 대한 가능한 대답은 사실입니다.
Mark Amery

2
@Mark_Amery 좀 더 일반적으로, 그것은 돌이킬 수 없습니다. 예 : D = {1 : [1, 2], 2 : [2, 3], 3 : [1]}, Dinv = {1 : [1, 3], 2 : [1, 2], 3 : [2]}. D는 예를 들어 {parent : children}의 사전이고 Dinv는 {child : parents} 사전입니다.
Rafael_Espericueta

36

이 시도:

inv_map = dict(zip(my_map.values(), my_map.keys()))

(참고 사전 뷰에 대한 파이썬 문서가 있음을 명시 적으로 보장 .keys()하고 .values()작업에 대한 위의 접근을 허용하는 동일한 순서로, 자신의 요소를 가지고있다.)

또는

inv_map = dict((my_map[k], k) for k in my_map)

또는 python 3.0의 dict comprehensions 사용

inv_map = {my_map[k] : k for k in my_map}

1
이는 키가 고유 한 경우에만 작동합니다 (반전하려는 경우에는 거의 해당되지 않음).
gented

python.org/dev/peps/pep-0274 에 따르면 , 2.7 이상의 dict comprehension도 사용할 수 있습니다.
Kawu

24

더 기능적인 다른 방법 :

my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))

3
게시 해 주셔서 감사합니다. "PEP 279에 귀도 반 로섬 (Guido van Rossum)를 인용하기 - 나는 확실히이 바람직하지 않다 filtermap변형 더 성장하지 죽을와 지능형리스트에 포섭 될".
Brian M. Hunt

2
네, 그건 브라이언의 공평한 점입니다. 대화의 포인트로 추가했습니다. dict 독해 방법은 내가 상상할 수있는 대부분의 경우 더 읽기 쉽습니다. (그리고 아마도 더 빠를 것 같아요)
Brendan Maguire

3
다른 사람보다 덜 읽을 수 있지만,이 방법은 스왑 할 수있는의 이익을 가지고 있습니다 dict같은 다른 매핑 유형 collections.OrderedDict또는collections.defaultdict
윌 S

10

이것은 Robert 의 대답 따라 확장 되어 dict의 값이 고유하지 않은 경우에 적용됩니다.

class ReversibleDict(dict):

    def reversed(self):
        """
        Return a reversed dict, with common values in the original dict
        grouped into a list in the returned dict.

        Example:
        >>> d = ReversibleDict({'a': 3, 'c': 2, 'b': 2, 'e': 3, 'd': 1, 'f': 2})
        >>> d.reversed()
        {1: ['d'], 2: ['c', 'b', 'f'], 3: ['a', 'e']}
        """

        revdict = {}
        for k, v in self.iteritems():
            revdict.setdefault(v, []).append(k)
        return revdict

구현은 reversed두 번 사용할 수 없으며 원본을 다시 가져올 수 없다는 제한이 있습니다 . 대칭이 아닙니다. Python 2.6으로 테스트되었습니다. 다음 은 결과 dict을 인쇄하는 데 사용하는 유스 케이스입니다.

당신이 오히려를 사용하는 거라고 경우 set(A)보다 list,이 대신에, 의미가있는 정렬되지 않은 응용 프로그램이 존재할 수 setdefault(v, []).append(k), 사용 setdefault(v, set()).add(k).


이것은 또한리스트 대신 세트를 사용하기에 좋은 장소가 될 것이다.revdict.setdefault(v, set()).add(k)
mueslo

물론, 그러나 이것이 왜 타당한 지 이유 set입니다. 여기에 적용되는 내장형입니다. 어떤 값이 아닌 어디 모든 키를 찾으려면 12? 그럼 난 그냥 할 수 있습니다 d.keys() - inv_d[1] - inv_d[2](파이썬 3에서)
mueslo

9

다음을 사용하여 중복 키로 사전을 뒤집을 수도 있습니다 defaultdict.

from collections import Counter, defaultdict

def invert_dict(d):
    d_inv = defaultdict(list)
    for k, v in d.items():
        d_inv[v].append(k)
    return d_inv

text = 'aaa bbb ccc ddd aaa bbb ccc aaa' 
c = Counter(text.split()) # Counter({'aaa': 3, 'bbb': 2, 'ccc': 2, 'ddd': 1})
dict(invert_dict(c)) # {1: ['ddd'], 2: ['bbb', 'ccc'], 3: ['aaa']}  

여기를 참조 하십시오 :

이 기술은를 사용하는 동등한 기술보다 간단하고 빠릅니다 dict.setdefault().


6

예를 들어 다음과 같은 사전이 있습니다.

dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}

그리고 당신은 그런 거꾸로 된 형태로 그것을 얻고 싶습니다 :

inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}

첫 번째 해결책 . 사전에서 키-값 쌍을 반전 시키려면 for-loop 접근법을 사용하십시오 .

# Use this code to invert dictionaries that have non-unique values

inverted_dict = dict()
for key, value in dict.items():
    inverted_dict.setdefault(value, list()).append(key)

두번째 해결책 . 반전을 위해 사전 이해 접근법을 사용하십시오 .

# Use this code to invert dictionaries that have unique values

inverted_dict = {value: key for key, value in dict.items()}

세 번째 해결책 . 사용 반전 되 돌리는 방법을 (두번째 솔루션에 의존)

# Use this code to invert dictionaries that have lists of values

dict = {value: key for key in inverted_dict for value in my_map[key]}

4
dict변수 이름에 예약되어 사용되어서는 안됩니다
crypdick

2
무엇을 우리에게 얘기를 잊었 my_map입니다
crypdick

dictio()? 당신은 의미 했습니까 dict()?
Georgy

5

목록과 사전 이해의 조합. 중복 키를 처리 할 수 ​​있습니다

{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}

1
stackoverflow.com/a/41861007/1709587 과 마찬가지로 , 이것은 몇 가지 추가 코드 줄로 O (n)에서 쉽게 해결되는 문제에 대한 O (n²) 솔루션입니다.
Mark Amery

2

값이 고유하지 않고 약간 하드 코어 한 경우 :

inv_map = dict(
    (v, [k for (k, xx) in filter(lambda (key, value): value == v, my_map.items())]) 
    for v in set(my_map.values())
)

특히 큰 사전의 경우이 솔루션은 여러 번 반복되므로 Python이 매핑을 반전 / 반전시키는 것보다 훨씬 효율적이지 않습니다 items().


7
이것은 읽을 수없는 평범한 코드이며 유지 관리 가능한 코드를 작성하지 않는 좋은 예입니다. -1여전히 질문에 대답 하지 않기 때문에 내 의견입니다.
Russ Bradberry

1

람다를 좋아한다면 위에서 제안한 다른 기능 외에도 :

invert = lambda mydict: {v:k for k, v in mydict.items()}

또는 다음과 같이 할 수도 있습니다.

invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )

2
-1; 당신이 한 모든 것은 페이지에서 다른 답변을 가져 와서 람다에 넣는 것입니다. 또한 변수에 람다를 할당하는 것은 PEP 8 위반입니다.
Mark Amery

1

이 작업을 수행하는 가장 좋은 방법은 클래스를 정의하는 것입니다. 다음은 "대칭 사전"의 구현입니다.

class SymDict:
    def __init__(self):
        self.aToB = {}
        self.bToA = {}

    def assocAB(self, a, b):
        # Stores and returns a tuple (a,b) of overwritten bindings
        currB = None
        if a in self.aToB: currB = self.bToA[a]
        currA = None
        if b in self.bToA: currA = self.aToB[b]

        self.aToB[a] = b
        self.bToA[b] = a
        return (currA, currB)

    def lookupA(self, a):
        if a in self.aToB:
            return self.aToB[a]
        return None

    def lookupB(self, b):
        if b in self.bToA:
            return self.bToA[b]
        return None

삭제 및 반복 방법은 필요한 경우 쉽게 구현할 수 있습니다.

이 구현은 전체 사전을 뒤집는 것보다 훨씬 효율적입니다 (이 페이지에서 가장 인기있는 솔루션 인 것 같습니다). 말할 것도없이 SymDict에서 원하는만큼 값을 추가하거나 제거 할 수 있으며, 역 사전은 항상 유효합니다. 전체 사전을 한 번만 뒤집는 경우에는 해당되지 않습니다.


이 아이디어가 마음에 들지만 계산을 향상시키기 위해 추가 메모리를 사용한다는 점에 유의하는 것이 좋습니다. 더 행복한 매체는 미러를 캐싱하거나 느리게 계산할 수있다. 사전보기 및 사용자 정의 연산자와 같이 구문 적으로 더 매력적으로 만들 수 있다는 점도 주목할 가치가 있습니다.
Brian M. Hunt

@ BrianM.Hunt 그것은 메모리를 교환하지만 많이는 아닙니다. 각 객체에 대해 두 개의 포인터 세트 만 저장합니다. 객체가 단일 정수보다 훨씬 큰 경우 큰 차이가 없습니다. 반면에 작은 물체가 거대한 테이블을 가지고 있다면 그 제안을 고려해야 할 수도 있습니다.
NcAdams

그리고 여기에 더 많은 일이있을 것이라는 데 동의합니다. 나중에 이것을 완전히 작동하는 데이터 유형으로
바꿀 수 있습니다

2
"이 구현은 전체 사전을 뒤집는 것보다 훨씬 효율적입니다" -음, 왜? 이 접근 방식이 상당한 성능 이점을 가질 수있는 적절한 방법은 없습니다. 이 방법으로 여전히 두 개의 사전이 있습니다. 파이썬이 기본 C 데이터 구조에 얼마나 많은 버킷을 할당하고 역 맵을 만들 수 있는지 사전에 알기 때문에 사전에 이해력으로 dict를 뒤집는 것보다 느릴 것으로 예상 됩니다. 전화하지 않고dictresize 접근법은 파이썬에게 그 가능성을 부인합니다.
Mark Amery

1

고유하지 않은 값을 처리하고 고유 한 경우의 모양을 유지합니다.

inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}

파이썬 3.x를 들어, 교체 itervalues와 함께 values.


3
이 솔루션은 하나의 라이너로 매우 우아하며 고유하지 않은 값을 관리합니다. 그러나 O (n2)의 복잡도는 수십 개의 요소에 대해서는 괜찮을 것이지만 초기 사전에 수십만 개의 요소가있는 경우 실제로 사용하기에는 너무 느립니다. 기본 dict에 기반한 솔루션이 이보다 빠릅니다.
gabuzo

Gabuzo가 옳습니다. 이 버전은 (아마도) 일부 버전보다 명확하지만 대용량 데이터에는 적합하지 않습니다.
Ersatz Kwisatz

0

함수는 list 유형의 값에 대해 대칭입니다. reverse_dict (reverse_dict (dictionary))를 수행 할 때 튜플이 목록에 포함됩니다.

def reverse_dict(dictionary):
    reverse_dict = {}
    for key, value in dictionary.iteritems():
        if not isinstance(value, (list, tuple)):
            value = [value]
        for val in value:
            reverse_dict[val] = reverse_dict.get(val, [])
            reverse_dict[val].append(key)
    for key, value in reverse_dict.iteritems():
        if len(value) == 1:
            reverse_dict[key] = value[0]
    return reverse_dict

0

사전은 값과 달리 사전 내에 하나의 고유 키를 필요로하기 때문에 새로운 특정 키에 포함되도록 정렬 된 값을 역순으로 추가해야합니다.

def r_maping(dictionary):
    List_z=[]
    Map= {}
    for z, x in dictionary.iteritems(): #iterate through the keys and values
        Map.setdefault(x,List_z).append(z) #Setdefault is the same as dict[key]=default."The method returns the key value available in the dictionary and if given key is not available then it will return provided default value. Afterward, we will append into the default list our new values for the specific key.
    return Map

0

Bi-jective 맵을위한 빠른 기능 솔루션 (값이 고유하지 않음) :

from itertools import imap, groupby

def fst(s):
    return s[0]

def snd(s):
    return s[1]

def inverseDict(d):
    """
    input d: a -> b
    output : b -> set(a)
    """
    return {
        v : set(imap(fst, kv_iter))
        for (v, kv_iter) in groupby(
            sorted(d.iteritems(),
                   key=snd),
            key=snd
        )
    }

이론적으로 이것은 명령형 솔루션 과 같이 세트에 하나씩 추가하거나 목록에 추가하는 것보다 빠릅니다 .

불행히도 값은 정렬 가능해야하며 정렬은 그룹별로 필요합니다.


1
"이론적으로 이것은 세트에 하나씩 추가 (또는리스트에 추가)하는 것보다 빠를 것" -아니오. n원래 dict의 요소가 주어지면 O(n log n)dict의 항목을 정렬해야하기 때문에 접근 방식에 시간이 복잡하고 순진한 접근 방식에는 O(n)시간이 복잡합니다. 모두를 위해 나는 당신의 접근 방식이 더 빨리 터무니없이 큰까지있을 수 있습니다 알고 dict에서의 연습 하지만, 그것은 확실히 이론에 있지 빠릅니다.
Mark Amery

0

파이썬 2.7 / 3.x에서 이것을 시도하십시오

inv_map={};
for i in my_map:
    inv_map[my_map[i]]=i    
print inv_map

-1

파이썬 2에서 그렇게 할 것입니다.

inv_map = {my_map[x] : x for x in my_map}

키 를 반복하는 동안 dict.items(또는 iteritemsPython 2에서) 키-값 쌍을 동시에 반복하는 것이 각 값을 개별적으로 추출하는 것보다 효율적입니다.
jpp

-1
def invertDictionary(d):
    myDict = {}
  for i in d:
     value = d.get(i)
     myDict.setdefault(value,[]).append(i)   
 return myDict
 print invertDictionary({'a':1, 'b':2, 'c':3 , 'd' : 1})

{1 : [ 'a', 'd'], 2 : [ 'b'], 3 : [ 'c']}와 같이 출력을 제공합니다.


키 를 반복하는 동안 dict.items(또는 iteritemsPython 2에서) 키-값 쌍을 동시에 반복하는 것이 각 값을 개별적으로 추출하는 것보다 효율적입니다. 또한 다른 사람을 복제하는 답변에 설명을 추가하지 않았습니다.
jpp

-1
  def reverse_dictionary(input_dict):
      out = {}
      for v in input_dict.values():  
          for value in v:
              if value not in out:
                  out[value.lower()] = []

      for i in input_dict:
          for j in out:
              if j in map (lambda x : x.lower(),input_dict[i]):
                  out[j].append(i.lower())
                  out[j].sort()
      return out

이 코드는 다음과 같습니다.

r = reverse_dictionary({'Accurate': ['exact', 'precise'], 'exact': ['precise'], 'astute': ['Smart', 'clever'], 'smart': ['clever', 'bright', 'talented']})

print(r)

{'precise': ['accurate', 'exact'], 'clever': ['astute', 'smart'], 'talented': ['smart'], 'bright': ['smart'], 'exact': ['accurate'], 'smart': ['astute']}

1
일반적으로 코드의 목적과 다른 언어를 도입하지 않고 문제를 해결하는 이유에 대한 설명이 포함되어 있으면 답변이 훨씬 유용합니다.
Tom Aranda

1
그것은 아주 좋은,하지만 설명 할 수없는 의사 결정의 많은입니다 (예를 들어, 키에 대한 이유를 소문자?)
Liudvikas Akelis

-2

완전히 다른 것이 아니라 Cookbook의 약간 다시 작성된 요리법. setdefault인스턴스를 통해 전달할 때마다 유지 방법을 사용하여 더욱 최적화되었습니다 .

def inverse(mapping):
    '''
    A function to inverse mapping, collecting keys with simillar values
    in list. Careful to retain original type and to be fast.
    >> d = dict(a=1, b=2, c=1, d=3, e=2, f=1, g=5, h=2)
    >> inverse(d)
    {1: ['f', 'c', 'a'], 2: ['h', 'b', 'e'], 3: ['d'], 5: ['g']}
    '''
    res = {}
    setdef = res.setdefault
    for key, value in mapping.items():
        setdef(value, []).append(key)
    return res if mapping.__class__==dict else mapping.__class__(res)

CPython 3.x에서 실행되도록 설계되었으며 2.x mapping.items()mapping.iteritems()

내 컴퓨터에서 다른 예제보다 약간 빠르게 실행됩니다.


1
결과를 a로 빌드 한 dict다음 끝에 올바른 유형의 클래스로 시작하지 않고 원하는 클래스로 변환하면 완전히 피할 수있는 성능 저하가 발생하는 것처럼 보입니다.
Mark Amery

-2

나는 사이클 'for'와 메소드 '.get ()'의 도움으로 이것을 작성했으며 'map'은 함수이기 때문에 사전의 이름 'map'을 'map1'로 변경했습니다.

def dict_invert(map1):
    inv_map = {} # new dictionary
    for key in map1.keys():
        inv_map[map1.get(key)] = key
    return inv_map

-2

값이 고유하지 않고 해시 (1 차원) 일 수있는 경우 :

for k, v in myDict.items():
    if len(v) > 1:
        for item in v:
            invDict[item] = invDict.get(item, [])
            invDict[item].append(k)
    else:
        invDict[v] = invDict.get(v, [])
        invDict[v].append(k)

그리고 한 차원 만 더 깊이 파고 들어야한다면 재귀를 사용하십시오.

def digList(lst):
    temp = []
    for item in lst:
        if type(item) is list:
            temp.append(digList(item))
        else:
            temp.append(item)
    return set(temp)

for k, v in myDict.items():
    if type(v) is list:
        items = digList(v)
        for item in items:
            invDict[item] = invDict.get(item, [])
            invDict[item].append(k)
    else:
        invDict[v] = invDict.get(v, [])
        invDict[v].append(k)

defaultdict를 사용하여 솔루션을 향상시킬 수 있습니다. 모든 invDict [item] = invDict.get (item, []) 행을 제거합니다
gabuzo

첫 번째 접근 방식은 여기에 변환 {"foo": "bar"}{'b': ['foo'], 'a': ['foo'], 'r': ['foo']}하고 어떤 값이있는 경우 예외가 발생 myDict하지 않은 반복 가능한 것입니다. 여기서 구현하려는 동작이 확실하지 않지만 실제로 구현 한 것은 아무도 원하지 않는 것입니다.
Mark Amery
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.