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()}
Если значения в my_map
не уникальны:
inv_map = {}
for k, v in my_map.iteritems():
inv_map[v] = inv_map.get(v, [])
inv_map[v].append(k)
попробуйте это:
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}
это расширяет ответ 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 столько, сколько хотите, и ваш обратный словарь будет всегда оставайтесь в силе - это не так, если вы просто перевернете весь словарь один раз.
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