Biorąc pod uwagę taki słownik:
my_map = {'a': 1, 'b': 2}
Jak można odwrócić tę mapę, aby uzyskać:
inv_map = {1: 'a', 2: 'b'}
Biorąc pod uwagę taki słownik:
my_map = {'a': 1, 'b': 2}
Jak można odwrócić tę mapę, aby uzyskać:
inv_map = {1: 'a', 2: 'b'}
Odpowiedzi:
Dla Python 2.7.x
inv_map = {v: k for k, v in my_map.iteritems()}
W przypadku 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
. Nie ma gwarancji, że tak pozostanie, więc nie pisz kodu polegającego na Dict
takim samym zachowaniu jak OrderedDict
.
Zakładając, że wartości w dykcie są unikalne:
dict((v, k) for k, v in my_map.iteritems())
iteritems()
zostanie wyprowadzona, więc można założyć, że arbitralny klucz zostanie przypisany do wartości nieunikalnej, w sposób, który będzie w oczywisty sposób odtwarzalny w pewnych warunkach, ale ogólnie nie.
iteritems()
metody i takie podejście nie zadziała; użyj items()
tam zamiast tego, jak pokazano w zaakceptowanej odpowiedzi. Również tłumaczenie ze słownika uczyniłoby to ładniejszym niż dzwonienie dict
.
Jeśli wartości w my_map
nie są unikalne:
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, [])
zwraca listę, która już została dodana, jeśli taka istnieje, więc przypisanie nie resetuje się do pustej listy. setdefault
byłby jednak ładniejszy.
inv_map.setdefault(v, set()).add(k)
.
my_map.items()
zamiast my_map.iteritems()
.
Aby to zrobić, zachowując typ mapowania (zakładając, że jest dict
to dict
podklasa lub podklasa):
def inverse_mapping(f):
return f.__class__(map(reversed, f.items()))
Spróbuj tego:
inv_map = dict(zip(my_map.values(), my_map.keys()))
(Zauważ, że dokumenty Pythona dotyczące widoków słownika wyraźnie to gwarantują .keys()
i .values()
mają ich elementy w tej samej kolejności, co pozwala na działanie powyższego podejścia.)
Alternatywnie:
inv_map = dict((my_map[k], k) for k in my_map)
lub używając rozumienia słownika w Pythonie 3.0
inv_map = {my_map[k] : k for k in my_map}
Kolejny, bardziej funkcjonalny sposób:
my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))
filter
i map
powinien umrzeć i zostać pochłonięty listami, a nie wyhodować więcej wariantów”.
dict
inne typy mapowania, takie jak collections.OrderedDict
lubcollections.defaultdict
To rozszerza się na odpowiedź Roberta , mającą zastosowanie do sytuacji, gdy wartości w dykcie nie są unikalne.
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
Implementacja jest ograniczona, ponieważ nie można użyć reversed
dwa razy i odzyskać oryginał. To nie jest symetryczne jako takie. Jest testowany z Python 2.6. Oto przykład użycia tego, w jaki sposób drukuję wynikowy dykt.
Jeśli wolisz użyć set
niż a list
, a mogą istnieć nieuporządkowane aplikacje, dla których ma to sens, zamiast setdefault(v, []).append(k)
używać setdefault(v, set()).add(k)
.
revdict.setdefault(v, set()).add(k)
set
. Obowiązuje tutaj typ wewnętrzny. Co jeśli chcę znaleźć wszystkie klucze, których wartości nie są 1
lub 2
? Potem mogę po prostu zrobić d.keys() - inv_d[1] - inv_d[2]
(w Pythonie 3)
Możemy również odwrócić słownik za pomocą duplikatów kluczy, używając 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']}
Zobacz tutaj :
Ta technika jest prostsza i szybsza niż technika równoważna
dict.setdefault()
.
Na przykład masz następujący słownik:
dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}
I chcesz uzyskać to w takiej odwróconej formie:
inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}
Pierwsze rozwiązanie . Aby odwrócić pary klucz-wartość w słowniku, użyj for
metody -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)
Drugie rozwiązanie . Użyj inwersji ze słownikiem :
# Use this code to invert dictionaries that have unique values
inverted_dict = {value: key for key, value in dict.items()}
Trzecie rozwiązanie . Użyj odwrócenia metody inwersji (polega na drugim rozwiązaniu):
# 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
jest zarezerwowany i nie należy go używać do nazw zmiennych
my_map
jest
dictio()
? Miałeś na myśli dict()
?
Połączenie rozumienia listy i słownika. Może obsługiwać duplikaty kluczy
{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}
Jeśli wartości nie są unikalne, a ty jesteś trochę hardkorowy:
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())
)
Zwłaszcza w przypadku dużego nagrania zauważ, że to rozwiązanie jest o wiele mniej wydajne niż odpowiedź Python odwracający / odwracający mapowanie, ponieważ zapętla się items()
wielokrotnie.
-1
ponieważ nadal odpowiada na pytanie, tylko moja opinia.
Oprócz innych funkcji sugerowanych powyżej, jeśli lubisz lambdas:
invert = lambda mydict: {v:k for k, v in mydict.items()}
Możesz też zrobić to w ten sposób:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )
Myślę, że najlepszym sposobem na to jest zdefiniowanie klasy. Oto implementacja „słownika symetrycznego”:
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
Metody usuwania i iteracji są wystarczająco łatwe do wdrożenia, jeśli są potrzebne.
Ta implementacja jest znacznie wydajniejsza niż odwracanie całego słownika (który wydaje się być najpopularniejszym rozwiązaniem na tej stronie). Nie wspominając o tym, że możesz dodawać lub usuwać wartości z SymDict tyle, ile chcesz, a słownik odwrotny zawsze pozostanie ważny - nie jest to prawdą, jeśli po prostu raz odwrócisz cały słownik.
dictresize
, ale takie podejście zaprzecza Pythonowi na taką możliwość.
To obsługuje nie unikalne wartości i zachowuje wiele z wyglądu unikalnej skrzynki.
inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}
Dla Pythona 3.x, wymienić itervalues
z values
.
Funkcja jest symetryczna dla wartości listy typów; Krotki są pokrywane do list podczas wykonywania reverse_dict (reverse_dict (słownik))
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
Ponieważ słowniki wymagają jednego unikalnego klucza w słowniku, w przeciwieństwie do wartości, musimy dołączyć odwrócone wartości do listy sortowania, która ma zostać uwzględniona w nowych określonych kluczach.
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
Szybkie funkcjonalne rozwiązanie dla map niebiotywnych (wartości nie unikalne):
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
)
}
Teoretycznie powinno to być szybsze niż dodawanie do zestawu (lub dołączanie do listy) jeden po drugim, jak w rozwiązaniu imperatywnym .
Niestety wartości muszą być sortowalne, sortowanie jest wymagane przez groupby.
n
elementy oryginalnego dyktanda, twoje podejście ma O(n log n)
złożoność czasową z powodu potrzeby sortowania elementów tego dykta, podczas gdy naiwne imperatywne podejście ma O(n)
złożoność czasową. Z tego co wiem, twoje podejście może być szybsze aż do absurdalnie dużych dict
w praktyce , ale z pewnością nie jest szybsze w teorii.
Zrobiłbym tak w Pythonie 2.
inv_map = {my_map[x] : x for x in my_map}
dict.items
(lub iteritems
w Pythonie 2) jest bardziej wydajne niż wyodrębnianie każdej wartości osobno podczas iteracji kluczy.
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})
Zapewni to wynik w postaci: {1: ['a', 'd'], 2: ['b'], 3: ['c']}
dict.items
(lub iteritems
w Pythonie 2) jest bardziej wydajne niż wyodrębnianie każdej wartości osobno podczas iteracji kluczy. Ponadto nie dodałeś żadnego wyjaśnienia do odpowiedzi, która powiela inne osoby.
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
ten kod robi to tak:
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']}
Nie jest to coś zupełnie innego, tylko nieco przepisany przepis z książki kucharskiej. Jest dalej optymalizowany metodą przechowywania setdefault
, zamiast za każdym razem, gdy przechodzi przez instancję:
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)
Zaprojektowany do pracy pod CPython 3.x, dla wersji 2.x mapping.items()
zmapping.iteritems()
Na mojej maszynie działa trochę szybciej niż inne przykłady tutaj
dict
a następnie przekształcanie go w pożądaną klasę na końcu (zamiast zaczynać od klasy odpowiedniego typu) wydaje mi się, że powoduje tutaj całkowicie niemożliwe do uniknięcia uderzenie wydajności.
Napisałem to za pomocą cyklu „for” i metody „.get ()” i zmieniłem nazwę „map” słownika na „map1”, ponieważ „map” jest funkcją.
def dict_invert(map1):
inv_map = {} # new dictionary
for key in map1.keys():
inv_map[map1.get(key)] = key
return inv_map
Jeśli wartości nie są unikalne ORAZ może być skrótem (jeden wymiar):
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)
I z rekurencją, jeśli chcesz kopać głębiej, to tylko jeden wymiar:
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"}
się {'b': ['foo'], 'a': ['foo'], 'r': ['foo']}
i podnosi wyjątek jeśli wartość myDict
nie jest iterable. Nie jestem pewien, jakie zachowanie próbujesz tutaj zastosować, ale to, co faktycznie wdrożyłeś, jest czymś, czego nikt nie będzie chciał.
my_map.items()
działa