辞書マッピングの逆転/反転


回答:


924

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
最近のPython 2.7.35のバージョンでmy_map.items()同様に動作
バレンティン

30
これは、値に単一性がないと機能しないことを除いて機能します。その場合、いくつかのエントリが
失われ


2
はい、実装の詳細として。The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon。そのままである保証はないのでDict、と同じ動作に依存するコードを記述しないでくださいOrderedDict
マティアス2018

9
@Mattias、これはPython 3.6に当てはまります。バージョン3.7の場合、順序保持は公式です:mail.python.org/pipermail/python-dev/2017-December/151283.html。BDFLはそう言った。
interDist 2018年

174

辞書の値が一意であると仮定します:

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

22
値もハッシュ可能でなければなりません
John La Rooy、2012年

30
@ Buttons840:値が一意でない場合、いずれにしても辞書の一意の反転はありません。つまり、反転は意味がありません。
Wrzlprmft 2014年

2
@ Buttons840最後のキーのみが値に表示されます。iteritems()出力される順序はおそらく保証されないため、特定の条件下で明らかに再現可能な方法で、任意のキーが一意でない値に割り当てられると想定されることがありますが、一般的にはそうではありません。
Evgeni Sergeev

2
もちろん、Python 3ではiteritems()メソッドがなくなり、このアプローチは機能しないことに注意してください。items()受け入れられた答えに示されているように代わりにそこで使用してください。また、辞書を理解すると、を呼び出すよりも見栄えがよくなりdictます。
Mark Amery

5
@Wrzlprmft非一意の値の場合の逆の自然な定義があります。すべての値は、それにつながる一連のキーにマップされます。
レオ

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)。私はdefaultdictのファンボーイでしたが、それから何度も何度も悩まされ、実際には明示的が暗黙的よりも優れていると結論付けました。
alsuren

この回答はマルチマップでは正しくありません。値は毎回空のリストにリセットされるため、ここに追加しても意味がありません。set_defaultを使用する必要があります
Yaroslav Bulatov

1
@YaroslavBulatovいいえ、ここに示されているコードは壊れていません- inv_map.get(v, [])既に追加されているリストがあればそれを返します。したがって、割り当ては空のリストにリセットされません。setdefaultただし、まだきれいです。
Mark Amery

10
セットはここでもっと意味があります。キーは(おそらく)ハッシュ可能であり、順序はありません。inv_map.setdefault(v, set()).add(k)
Artyer 2017

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

1
@Rafael_Espericueta値が繰り返されているマップは反転できないため、この質問への可能な回答にはそれが当てはまります。
マークアメリー

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()))

ディクショナリビューのPythonドキュメントでは、これらの要素が同じ順序であることが明示的に保証され.keys().values()いるため、上記のアプローチが機能することに注意しください。)

または:

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

またはpython 3.0のdict内包表記を使用して

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

1
これは、キーが一意である場合にのみ機能することに注意してください(キーを逆にしたい場合は、ほとんどの場合当てはまりません)。
断続的

python.org/dev/peps/pep-0274によると、2.7以上でも dict内包表記が利用可能です。
カウ2018年

24

別の、より機能的な方法:

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

3
投稿いただきありがとうございます。PEP 279でGuido Van Rossumを引用すると、これが望ましいfiltermapは思えません。
ブライアンM.ハント

2
ええ、それはブライアンの公正なポイントです。会話のポイントとして追加しただけです。辞書を理解する方法は、私が想像するほとんどの人にとって読みやすくなっています。(そしておそらく私も推測するともっと速いでしょう)
ブレンダンマグワイア

3
他よりも読みにくくなる可能性がありますが、この方法にはdictcollections.OrderedDictor などの他のマッピングタイプと交換できるという利点がありますcollections.defaultdict
Will 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おり、2回使用して元の状態に戻すことはできません。このように対称ではありません。Python 2.6でテストされています。これが、結果のディクテーションを印刷するための使用例です。

setよりもを使用する場合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](Python 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)

2番目のソリューション。反転には辞書内包アプローチを使用します。

# Use this code to invert dictionaries that have unique values

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

3番目のソリューション反転アプローチを元に戻す(2番目のソリューションに依存):

# 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()
ジョージー

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²)溶液です。
マークアメリー

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())
)

特に大規模な辞書の場合、このソリューションは、Pythonitems()複数回ループするため、回答のPythonのマッピングよりもはるかに効率が悪いことに注意してください。


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の値は好きなだけ追加または削除できます。逆辞書は常に有効なままです。これは、単に辞書全体を1度反転するだけでは当てはまりません。


私はこのアイデアが好きですが、計算を改善するために追加のメモリをトレードオフすることに注意してください。幸福な媒体は、ミラーをキャッシングまたは遅延計算している可能性があります。また、辞書ビューやカスタムオペレーターなどを使用すると、構文的に魅力的になる可能性があることにも注意してください。
ブライアンM.ハント

@ BrianM.Huntメモリのトレードオフですが、多くはありません。各オブジェクトへのポインタの2つのセットのみを保存します。オブジェクトが単一の整数よりもはるかに大きい場合、これは大きな違いにはなりません。一方、小さなオブジェクトの巨大なテーブルがある場合は、それらの提案を考慮する必要があるかもしれません...
NcAdams '28

そして、私は同意します、ここで行うべきことは
まだあり

2
「この実装は、辞書全体を逆にするよりもはるかに効率的です」 -ええと、なぜですか?このアプローチでパフォーマンスが大幅に向上する可能性のある方法はありません。この方法では、まだ2つの辞書があります。どちらかと言えば、これは、たとえば、内包するディクテーションを反転するより遅くなると思います。ディクテーションを反転すると、Pythonは、基礎となるCデータ構造に割り当てるバケット数を前もって適切に把握し、逆マップを作成できるためです。を呼び出すことdictresizeはありませんが、このアプローチではPythonの可能性が否定されます。
Mark Amery

1

これにより、一意でない値が処理され、一意のケースの外観の多くが保持されます。

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

Pythonの3.xの、交換してくださいitervaluesvalues


3
このソリューションは、ワンライナーとして非常にエレガントで、一意でない値のケースを管理します。ただし、O(n2)は複雑であるため、数十の要素では問題ありませんが、初期の辞書に数十万の要素がある場合、実際の使用には遅すぎます。デフォルトの辞書に基づくソリューションは、これよりもはるかに高速です。
gabuzo 2017年

ガブゾウはまったく正しい。このバージョンは一部の(おそらく)より明確ですが、大きなデータには適していません。
Ersatz Kwisatz 2017年

0

リスト型の値の関数は対称です。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

辞書は値とは異なり、辞書内に1つの一意のキーを必要とするため、新しい特定のキーに含めるには、逆の値を並べ替えのリストに追加する必要があります。

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

非単射マップ(値が一意ではない)の高速機能ソリューション:

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つずつ追加する(またはリストに追加する)よりも高速です。

残念ながら、値はソート可能でなければなりません。ソートはgroupbyで必要です。


1
「理論的には、これは1つずつセットに追加する(またはリストに追加する)よりも速くなるはずです」 -いいえ。n元のdictの要素を考えるとO(n log n)、dictのアイテムをソートする必要があるため、アプローチはO(n)時間的に複雑ですが、単純な命令型のアプローチは時間的に複雑です。すべての人にとって、あなたのアプローチは実際には途方もなく大きいdicts まで速くなる可能性があることを知っていますが、理論的にはそれは決して速くありません。
Mark Amery

0

これをpython 2.7 / 3.xで試してください

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

-1

私はそれをPython 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 '25

-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
一般に、コードに意図されていること、および他の人を紹介することなく問題を解決する理由の説明が含まれている場合、回答ははるかに役立ちます。
トムアランダ2017

1
それはとても良いですが、多くの説明されていない決定(たとえば、なぜキーが小文字なのか)
Liudvikas Akelis

-2

完全に異なるものではなく、クックブックのレシピを少し書き直しただけです。setdefaultインスタンスを通過するたびにではなく、retaining メソッドによってさらに最適化されます。

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)

2.xのために置き換え、CPythonの3.xの下で実行するように設計されmapping.items()mapping.iteritems()

私のマシンでは、他の例よりも少し速く実行されます


1
結果をaとしてビルドdictし、最後に(適切なタイプのクラスから開始するのではなく)目的のクラスに変換すると、ここでは完全に回避可能なパフォーマンスヒットが発生するように見えます。
マークアメリー

-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)

そして、再帰を使用して、1つだけの次元を掘り下げる必要がある場合:

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。ここで実装しようとした動作はわかりませんが、実際に実装したのは、だれもが望んでいないことです。
マークアメリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.