Приведенный словарь выглядит так:
my_map = {'a': 1, 'b': 2}
Как можно инвертировать эту карту, чтобы получить:
inv_map = {1: 'a', 2: 'b'}
Приведенный словарь выглядит так:
my_map = {'a': 1, 'b': 2}
Как можно инвертировать эту карту, чтобы получить:
inv_map = {1: 'a', 2: 'b'}
Ответы:
Для 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()}
The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon
, Нет гарантии, что так будет и дальше, поэтому не пишите код, основанный на Dict
том же поведении, что и OrderedDict
.
Предполагая, что значения в dict являются уникальными:
dict((v, k) for k, v in my_map.iteritems())
iteritems()
будет выводиться, поэтому можно предположить, что для неуникального значения будет назначен произвольный ключ способом, который, по-видимому, будет воспроизводим при некоторых условиях, но в общем случае нет.
iteritems()
метода, и этот подход не будет работать; используйте items()
вместо этого, как показано в принятом ответе. Кроме того, понимание словаря сделало бы это красивее, чем вызов dict
.
Если значения в 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.get(v, [])
возвращает уже добавленный список, если он есть, поэтому назначение не сбрасывается в пустой список. setdefault
все равно будет красивее.
inv_map.setdefault(v, set()).add(k)
,
my_map.items()
вместо my_map.iteritems()
.
Чтобы сделать это при сохранении типа вашего отображения (при условии, что это - dict
или dict
подкласс):
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
inv_map = {my_map[k] : k for k in my_map}
Другой, более функциональный способ:
my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))
filter
и map
должен умереть и быть включенным в составление списка, а не увеличивать количество вариантов».
dict
с другими типами отображения, такими как collections.OrderedDict
илиcollections.defaultdict
Это расширяет ответ Роберта , применяемый к случаям, когда значения в 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)
.
revdict.setdefault(v, set()).add(k)
set
. Это внутренний тип, который применяется здесь. Что делать, если я хочу найти все ключи, значения которых отсутствуют 1
или 2
? Тогда я могу просто сделать d.keys() - inv_d[1] - inv_d[2]
(в Python 3)
Мы также можем перевернуть словарь с дубликатами ключей, используя 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()
.
Например, у вас есть следующий словарь:
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)
Второе решение . Используйте словарный подход для инверсии:
# 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]}
dict
зарезервировано и не должно использоваться для имен переменных
my_map
такое
dictio()
? Вы имели в виду dict()
?
Сочетание списка и словаря. Может обрабатывать дубликаты ключей
{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}
Если значения не уникальны, и вы немного хардкор:
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()
несколько раз.
-1
потому что это все еще отвечает на вопрос, только мое мнение.
В дополнение к другим функциям, предложенным выше, если вам нравятся лямбды:
invert = lambda mydict: {v:k for k, v in mydict.items()}
Или вы можете сделать это тоже так:
invert = lambda mydict: dict( zip(mydict.values(), mydict.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 столько раз, сколько захотите, и ваш обратный словарь всегда останется действительным - это не так, если вы просто полностью измените весь словарь один раз.
dictresize
, но этот подход лишает Python такой возможности.
Это обрабатывает неуникальные значения и сохраняет большую часть внешнего вида уникального случая.
inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}
Для Python 3.x замените itervalues
на values
.
Функция симметрична для значений списка типов; Кортежи включаются в списки при выполнении reverse_dict (reverse_dict (словарь))
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
Быстрое функциональное решение для небиективных карт (значения не уникальны):
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
)
}
Теоретически это должно быть быстрее, чем добавление к набору (или добавление к списку) один за другим, как в императивном решении .
К сожалению, значения должны быть сортируемыми, сортировка требуется по группам.
n
элементы в исходном дикте, ваш подход имеет O(n log n)
временную сложность из-за необходимости сортировки элементов диктанта, тогда как наивный императивный подход имеет O(n)
временную сложность. Насколько я знаю, ваш подход может быть быстрее вплоть до абсурдно больших dict
на практике , но в теории он, конечно, не быстрее.
Я бы сделал это таким образом в Python 2.
inv_map = {my_map[x] : x for x in my_map}
dict.items
(или iteritems
в Python 2) более эффективна, чем извлечение каждого значения отдельно при итерации ключей.
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
(или iteritems
в Python 2) более эффективна, чем извлечение каждого значения отдельно при итерации ключей. Кроме того, вы не добавили объяснения в ответ, который дублирует других.
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']}
Не что-то совершенно другое, просто немного переписанный рецепт из поваренной книги. Более того, он оптимизируется путем сохранения 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()
На моей машине работает чуть быстрее, чем на других примерах здесь
dict
и последующее преобразование в требуемый класс в конце (вместо того, чтобы начинать с класса правильного типа) выглядит для меня так, как будто это приводит к совершенно предотвращаемому падению производительности.
Я написал это с помощью цикла «for» и метода «.get ()» и изменил название «map» в словаре на «map1», потому что «map» - это функция.
def dict_invert(map1):
inv_map = {} # new dictionary
for key in map1.keys():
inv_map[map1.get(key)] = key
return inv_map
Если значения не уникальны, И может быть хешем (одно измерение):
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)
{"foo": "bar"}
в {'b': ['foo'], 'a': ['foo'], 'r': ['foo']}
и вызывает исключение , если какое - либо значение в myDict
не итератор. Я не уверен, какое поведение вы пытались реализовать здесь, но то, что вы на самом деле реализовали, это то, чего никто не хочет.
my_map.items()
работает