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