Dato un dizionario del genere:
my_map = {'a': 1, 'b': 2}
Come si può invertire questa mappa per ottenere:
inv_map = {1: 'a', 2: 'b'}
Dato un dizionario del genere:
my_map = {'a': 1, 'b': 2}
Come si può invertire questa mappa per ottenere:
inv_map = {1: 'a', 2: 'b'}
Risposte:
Per Python 2.7.x
inv_map = {v: k for k, v in my_map.iteritems()}
Per 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
. Non vi è alcuna garanzia che rimarrà tale, quindi non scrivere codice facendo affidamento sullo Dict
stesso comportamento di OrderedDict
.
Supponendo che i valori nel dict siano univoci:
dict((v, k) for k, v in my_map.iteritems())
iteritems()
verrà emesso, quindi si può presumere che una chiave arbitraria verrà assegnata per un valore non univoco, in un modo che apparentemente sarà riproducibile in alcune condizioni, ma non così in generale.
iteritems()
metodo e questo approccio non funzionerà; usalo items()
invece come mostrato nella risposta accettata. Inoltre, una comprensione del dizionario renderebbe questo più bello del chiamare dict
.
Se i valori in my_map
non sono univoci:
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, [])
restituisce l'elenco già aggiunto se ce n'è uno, quindi l'assegnazione non viene ripristinata su un elenco vuoto. setdefault
sarebbe comunque più bello, comunque.
inv_map.setdefault(v, set()).add(k)
.
my_map.items()
invece di my_map.iteritems()
.
Per fare ciò preservando il tipo di mappatura (supponendo che si tratti di una dict
o una dict
sottoclasse):
def inverse_mapping(f):
return f.__class__(map(reversed, f.items()))
Prova questo:
inv_map = dict(zip(my_map.values(), my_map.keys()))
(Si noti che i documenti Python nelle viste del dizionario garantiscono esplicitamente che .keys()
e .values()
hanno i loro elementi nello stesso ordine, il che consente al metodo sopra di funzionare).
In alternativa:
inv_map = dict((my_map[k], k) for k in my_map)
o usando la comprensione del dict di python 3.0
inv_map = {my_map[k] : k for k in my_map}
Un altro modo più funzionale:
my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))
filter
e map
dovrei morire ed essere riassunto nella comprensione dell'elenco, non far crescere altre varianti".
dict
con altri tipi di mappatura come collections.OrderedDict
ocollections.defaultdict
Questo si espande sulla risposta di Robert , applicando a quando i valori nel dict non sono unici.
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
L'implementazione è limitata in quanto non è possibile utilizzare reversed
due volte e recuperare l'originale. Non è simmetrico in quanto tale. È testato con Python 2.6. Ecco un caso d'uso di come sto usando per stampare il dict risultante.
Se preferisci usare a piuttosto set
che a list
, e potrebbero esistere applicazioni non ordinate per cui ha senso, invece di setdefault(v, []).append(k)
, usare setdefault(v, set()).add(k)
.
revdict.setdefault(v, set()).add(k)
set
. È il tipo intrinseco che si applica qui. Cosa succede se desidero trovare tutte le chiavi in cui i valori non sono 1
o 2
? Allora posso solo fare d.keys() - inv_d[1] - inv_d[2]
(in Python 3)
Possiamo anche invertire un dizionario con chiavi duplicate usando 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']}
Vedi qui :
Questa tecnica è più semplice e veloce di una tecnica equivalente che utilizza
dict.setdefault()
.
Ad esempio, hai il seguente dizionario:
dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}
E vuoi prenderlo in una forma così invertita:
inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}
Prima soluzione . Per invertire le coppie chiave-valore nel dizionario utilizzare un for
approccio -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)
Seconda soluzione . Utilizzare un approccio di comprensione del dizionario per l'inversione:
# Use this code to invert dictionaries that have unique values
inverted_dict = {value: key for key, value in dict.items()}
Terza soluzione . Utilizzare il ripristino dell'approccio di inversione (si basa sulla seconda soluzione):
# 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
è riservato e non dovrebbe essere usato per i nomi delle variabili
my_map
è
dictio()
? Volevi dire dict()
?
Combinazione di elenco e comprensione del dizionario. Può gestire chiavi duplicate
{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}
Se i valori non sono unici e sei un po 'hardcore:
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())
)
Soprattutto per un dict di grandi dimensioni, si noti che questa soluzione è molto meno efficiente della risposta Python inverte / inverte una mappatura perché scorre items()
più volte.
-1
perché risponde ancora alla domanda, solo la mia opinione.
Oltre alle altre funzioni suggerite sopra, se ti piacciono le lambda:
invert = lambda mydict: {v:k for k, v in mydict.items()}
Oppure potresti farlo anche in questo modo:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )
Penso che il modo migliore per farlo sia definire una classe. Ecco un'implementazione di un "dizionario simmetrico":
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
I metodi di eliminazione e iterazione sono abbastanza facili da implementare se necessari.
Questa implementazione è molto più efficiente dell'inversione di un intero dizionario (che sembra essere la soluzione più popolare in questa pagina). Per non parlare, puoi aggiungere o rimuovere valori dal tuo SymDict quanto vuoi e il tuo dizionario inverso rimarrà sempre valido - questo non è vero se si inverte semplicemente l'intero dizionario una volta.
dictresize
, ma questo approccio nega a Python questa possibilità.
Gestisce valori non univoci e mantiene gran parte dell'aspetto del caso unico.
inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}
Per Python 3.x, sostituire itervalues
con values
.
La funzione è simmetrica per i valori dell'elenco dei tipi; Le tuple vengono convertite in elenchi quando si esegue reverse_dict (reverse_dict (dizionario))
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
Poiché i dizionari richiedono una chiave univoca all'interno del dizionario a differenza dei valori, dobbiamo aggiungere i valori invertiti in un elenco di ordinamento da includere nelle nuove chiavi specifiche.
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
Soluzione funzionale veloce per mappe non biiettive (valori non univoci):
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
)
}
In teoria questo dovrebbe essere più veloce dell'aggiunta al set (o aggiunta alla lista) uno alla volta come nella soluzione imperativa .
Purtroppo i valori devono essere ordinabili, l'ordinamento è richiesto da groupby.
n
elementi del dict originale, il tuo approccio ha una O(n log n)
complessità temporale dovuta alla necessità di ordinare gli articoli del dict, mentre l'approccio ingenuo imperativo ha una O(n)
complessità temporale. Per quanto ne so, il tuo approccio potrebbe essere più veloce fino a quando dict
in pratica è assurdamente grande , ma di certo non è più veloce in teoria.
Lo farei così in Python 2.
inv_map = {my_map[x] : x for x in my_map}
dict.items
(o iteritems
in Python 2) è più efficiente dell'estrazione di ciascun valore separatamente durante l'iterazione delle chiavi.
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})
Ciò fornirà un output come: {1: ['a', 'd'], 2: ['b'], 3: ['c']}
dict.items
(o iteritems
in Python 2) è più efficiente dell'estrazione di ciascun valore separatamente durante l'iterazione delle chiavi. Inoltre, non hai aggiunto alcuna spiegazione a una risposta che duplica gli altri.
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
questo codice fa così:
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']}
Non qualcosa di completamente diverso, solo una ricetta un po 'riscritta da Cookbook. Inoltre è ottimizzato mantenendo il setdefault
metodo, anziché ogni volta che lo ottiene attraverso l'istanza:
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)
Progettato per essere eseguito con CPython 3.x, per 2.x sostituirlo mapping.items()
conmapping.iteritems()
Sulla mia macchina funziona un po 'più veloce rispetto ad altri esempi qui
dict
e poi convertirlo alla classe desiderata alla fine (piuttosto che iniziare con una classe del tipo giusto) mi sembra che incorre in un colpo di prestazione del tutto evitabile, qui.
Ho scritto questo con l'aiuto del ciclo 'for' e del metodo '.get ()' e ho cambiato il nome 'map' del dizionario in 'map1' perché 'map' è una funzione.
def dict_invert(map1):
inv_map = {} # new dictionary
for key in map1.keys():
inv_map[map1.get(key)] = key
return inv_map
Se i valori non sono univoci AND può essere un hash (una dimensione):
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)
E con una ricorsione se hai bisogno di scavare più a fondo allora solo una dimensione:
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"}
per {'b': ['foo'], 'a': ['foo'], 'r': ['foo']}
e solleva un'eccezione se qualsiasi valore myDict
non è un iterabile. Non sono sicuro del comportamento che stavi cercando di implementare qui, ma ciò che hai effettivamente implementato è qualcosa che praticamente nessuno vorrebbe.
my_map.items()
funziona anche