Python обратный / инвертировать отображение

данный словарь, как так:

my_map = { 'a': 1, 'b':2 }

как можно инвертировать эту карту сделать:

inv_map = { 1: 'a', 2: 'b' }

ПРИМЕЧАНИЕ: map изменено на my_map чтобы избежать конфликтов со встроенной функцией,map. Некоторые замечания могут быть затронуты ниже.

29 ответов


Для 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()}

предполагая, что значения в dict уникальны:

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

Если значения в my_map не уникальны:

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

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

попробуйте это:

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

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

другой, более функциональный, так:

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

это расширяет ответ Python reverse / инвертировать отображение, применяясь, когда значения в 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. здесь это вариант использования того, как я использую для печати результирующего Дикта.

если вы используете set чем list и есть приложения, для которых это имеет смысл, а не setdefault(v, []).append(k) используйте setdefault(v, set()).add(k).


добавление моих 2 центов pythonic way:

inv_map = dict(map(reversed, my_map.items()))

пример:

In [7]: my_map
Out[7]: {1: 'one', 2: 'two', 3: 'three'}

In [8]: inv_map = dict(map(reversed, my_map.items()))

In [9]: inv_map
Out[9]: {'one': 1, 'three': 3, 'two': 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 reverse / инвертировать отображение потому что он перебирает items() несколько раз.


мы также можем изменить словарь с дубликатами ключей, используя defaultdict:

from collections import Counter, defaultdict

def invert_dict(d):
    d_inv = defaultdict(list)
    for k, v in c.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().


сочетание списка и понимания словаря. Может обрабатывать дубликаты ключей

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

в дополнение к другим функциям, предложенным выше, если вам нравится lambdas:

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

или, вы могли бы сделать это так:

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

Это обрабатывает не уникальные значения и сохраняет большую часть внешнего вида уникального случая.

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

Для Python 3.х, заменить itervalues С значения. Я не могу взять на себя ответственность за это... это было предложено Icon Jack.


Я думаю, что лучший способ сделать это, чтобы определить класс. Вот реализация "симметричного словаря":

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 столько, сколько хотите, и ваш обратный словарь будет всегда оставайтесь в силе - это не так, если вы просто перевернете весь словарь один раз.


С помощью zip

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

попробуйте это для python 2.7 / 3.x

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

Я бы сделал это таким образом в python 2.

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

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']}


  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']}

функция симметрична для значений типа 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

поскольку словари требуют одного уникального ключа в словаре в отличие от значений, мы должны добавить обратные значения в список сортировки, который будет включен в новые конкретные ключи.

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

не что-то совершенно другое, просто немного переписать рецепт из Поваренной книги. Это futhermore оптимизирован путем сохранения 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()

на моей машине работает немного быстрее, чем другие примеры здесь


Если значения не уникальны и могут быть хэшем (одно измерение):

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)

обратный словарь:

dict_ = {"k0":"v0", "k1":"v1", "k2":"v1"}
inversed_dict_ = {val: key for key, val in dict_.items()}

print(inversed_dict_["v1"])

быстрое функциональное решение для небиективных отображений (значения не уникальны):

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

теоретически это должно быть быстрее, чем добавление к множеству (или добавление к списку) один за другим, как в необходимо решение.

к сожалению, значения должны быть сортируемыми, сортировка требуется groupby.


Я написал это с помощью цикла " for " и method.get ()", и я изменил имя "карта" словаря на "map1", потому что "карта" - это функция.

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

для всех видов словарей, независимо от того, имеют ли они уникальные значения для использования в качестве ключей, вы можете создать список ключей для каждого значения

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

Это не лучшее решение, но оно работает. Скажем, словарь, который мы хотим перевернуть, это:

dictionary = {'a': 1, 'b': 2, 'c': 3}, затем:

dictionary = {'a': 1, 'b': 2, 'c': 3}
reverse_dictionary = {}
for index, val in enumerate(list(dictionary.values())):
    reverse_dictionary[val] = list(dictionary.keys())[index]

вывод reverse_dictionary должен быть {1:' a', 2:' b', 3:'c'}


если элементы не уникальны, попробуйте следующее:

     dict={}
     dict1={}
     num=int(raw_input(" how many numbers in dict?--> "))
     for i in range (0,num):
         key=raw_input(" enter key --> ")
         value=raw_input("enter value --> ")
         dict[key]=value
     keys=dict.keys()
     values=dict.values()
     for b in range (0,num):
         keys[b],values[b]=values[b],keys[b]
         dict1[keys[b]]=values[b]
     print keys
     print values
     print dict1