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

Приведенный словарь выглядит так:

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

Как можно инвертировать эту карту, чтобы получить:

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

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

41 ответ

Решение

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

или используя pyt 3.0 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()))

Мы также можем перевернуть словарь с дубликатами ключей, используя 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(),

Это расширяет ответ Python на обратное / обратное отображение, применяя его к случаям, когда значения в 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 чем list и есть приложения, для которых это имеет смысл, а не setdefault(v, []).append(k) использовать setdefault(v, set()).add(k),

Много ответов, но ничего не нашел чистого, если речь идет о словаре с неуникальными значениями .

Решение было бы:

      from collections import defaultdict

inv_map = defaultdict(list) 
for k, v in my_map.items(): 
    inv_map[v].append(k)

Пример:

Если исходный диктат my_map = {'c': 1, 'd': 5, 'a': 5, 'b': 10}

тогда выполнение приведенного выше кода даст:

      {5: ['a', 'd'], 1: ['c'], 10: ['b']}

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

      some_dict = {"1":{"a","b","c"},
        "2":{"d","e","f"},
        "3":{"g","h","i"}}

Обратное хотел бы:

      some_dict = {vi: k  for k, v in some_dict.items() for vi in v}

Результат такой:

      {'c': '1',
 'b': '1',
 'a': '1',
 'f': '2',
 'd': '2',
 'e': '2',
 'g': '3',
 'h': '3',
 'i': '3'}

Например, у вас есть следующий словарь:

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

И вы хотите получить это в такой перевернутой форме:

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

Первое решение Для инвертирования пар ключ-значение в вашем словаре используйте forпетлевой подход:

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

inverted_dict = dictio()
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]}

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

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

Добавляя мои 2 цента питонического пути:

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 на обратное / обратное отображение, потому что оно зацикливается на items() многократно.

Я обнаружил, что эта версия более чем на 10% быстрее принятой версии словаря с 10000 ключами.

d = {i: str(i) for i in range(10000)}

new_d = dict(zip(d.values(), d.keys()))

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

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

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

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

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

Или вы можете сделать это тоже так:

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

Используя zip

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

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

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

Я знаю, что на этот вопрос уже есть много хороших ответов, но я хотел поделиться этим очень изящным решением, которое также заботится о повторяющихся значениях:

      def dict_reverser(d):
    seen = set()
    return {v: k for k, v in d.items() if v not in seen or seen.add(v)}

Это опирается на тот факт, что set.add всегда возвращается None в Python.

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

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

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

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

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

Вот еще один способ сделать это.

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

inv_map= {}
for key in my_map.keys() :
    val = my_map[key]
    inv_map[val] = key

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

      def dict_invert(d):
    '''
    d: dict
    Returns an inverted dictionary 
    '''
    # Your code here
    inv_d = {}
    for k, v in d.items():
        if v not in inv_d.keys():
            inv_d[v] = [k]
        else:
            inv_d[v].append(k)
        inv_d[v].sort()
        print(f"{inv_d[v]} are the values")
        
    return inv_d
      dict([(value, key) for key, value in d.items()])
  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']}

Лямбда-решение для текущих версий python 3.x:

d1 = dict(alice='apples', bob='bananas')
d2 = dict(map(lambda key: (d1[key], key), d1.keys()))
print(d2)

Результат:

{'apples': 'alice', 'bananas': 'bob'}

Это решение не проверяет дубликаты.

Некоторые замечания:

  • Лямбда-конструкция может получить доступ к d1 из внешней области видимости, поэтому мы передаем только текущий ключ. Он возвращает кортеж.
  • Конструктор dict() принимает список кортежей. Он также принимает результат карты, поэтому мы можем пропустить преобразование в список.
  • Это решение не имеет явного forпетля. Он также избегает использованияlist comprehension для тех, кто плохо разбирается в математике;-)

Согласно моему комментарию к вопросу. Я думаю, что самый простой и один вкладыш, который работает как для Python2, так и для Python 3, будет

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

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

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

print(inversed_dict_["v1"])
Другие вопросы по тегам