Bei einem Wörterbuch wie diesem:
my_map = {'a': 1, 'b': 2}
Wie kann man diese Karte umkehren, um zu erhalten:
inv_map = {1: 'a', 2: 'b'}
Bei einem Wörterbuch wie diesem:
my_map = {'a': 1, 'b': 2}
Wie kann man diese Karte umkehren, um zu erhalten:
inv_map = {1: 'a', 2: 'b'}
Antworten:
Für Python 2.7.x.
inv_map = {v: k for k, v in my_map.iteritems()}
Für 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
. Es gibt keine Garantie dafür, dass dies auch so bleibt. Schreiben Sie also keinen Code, der auf Dict
dem gleichen Verhalten wie basiert OrderedDict
.
Angenommen, die Werte im Diktat sind eindeutig:
dict((v, k) for k, v in my_map.iteritems())
iteritems()
Ausgabe, die ausgegeben werden soll. Daher kann davon ausgegangen werden, dass ein beliebiger Schlüssel für einen nicht eindeutigen Wert zugewiesen wird, der unter bestimmten Bedingungen anscheinend reproduzierbar ist, im Allgemeinen jedoch nicht.
iteritems()
Methode mehr gibt und dieser Ansatz nicht funktioniert. Verwenden Sie items()
es stattdessen wie in der akzeptierten Antwort gezeigt. Ein Wörterbuchverständnis würde dies auch schöner machen als das Aufrufen dict
.
Wenn die Werte in my_map
nicht eindeutig sind:
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, [])
gibt die bereits hinzugefügte Liste zurück, falls vorhanden, sodass die Zuordnung nicht auf eine leere Liste zurückgesetzt wird. setdefault
wäre aber immer noch schöner.
inv_map.setdefault(v, set()).add(k)
.
my_map.items()
anstelle von my_map.iteritems()
.
So behalten Sie den Typ Ihres Mappings bei (vorausgesetzt, es handelt sich um eine dict
oder eine dict
Unterklasse):
def inverse_mapping(f):
return f.__class__(map(reversed, f.items()))
Versuche dies:
inv_map = dict(zip(my_map.values(), my_map.keys()))
(Beachten Sie, dass die Python-Dokumente in Wörterbuchansichten dies ausdrücklich garantieren .keys()
und .values()
ihre Elemente in derselben Reihenfolge haben, sodass der oben beschriebene Ansatz funktioniert.)
Alternative:
inv_map = dict((my_map[k], k) for k in my_map)
oder mit dem Diktatverständnis von Python 3.0
inv_map = {my_map[k] : k for k in my_map}
Ein anderer, funktionalerer Weg:
my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))
filter
und map
sollte sterben und in Listenverständnisse subsumiert werden, nicht mehr Varianten wachsen lassen".
dict
mit anderen Mapping-Typen wie collections.OrderedDict
oder collections.defaultdict
Dies erweitert die Antwort von Robert und gilt für den Fall, dass die Werte im Diktat nicht eindeutig sind.
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
Die Implementierung ist insofern eingeschränkt, als Sie nicht reversed
zweimal verwenden und das Original zurückerhalten können. Es ist als solches nicht symmetrisch. Es wird mit Python 2.6 getestet. Hier ist ein Anwendungsfall, wie ich das resultierende Diktat drucke.
Wenn Sie lieber a set
als a verwenden list
möchten und möglicherweise ungeordnete Anwendungen vorhanden sind, für die dies sinnvoll ist, anstatt sie zu setdefault(v, []).append(k)
verwenden setdefault(v, set()).add(k)
.
revdict.setdefault(v, set()).add(k)
set
. Es ist der intrinsische Typ, der hier gilt. Was ist, wenn ich alle Schlüssel finden möchte, bei denen die Werte nicht 1
oder sind 2
? Dann kann ich einfach d.keys() - inv_d[1] - inv_d[2]
(in Python 3)
Wir können ein Wörterbuch mit doppelten Schlüsseln auch umkehren, indem wir 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']}
Siehe hier :
Diese Technik ist einfacher und schneller als eine gleichwertige Technik
dict.setdefault()
.
Zum Beispiel haben Sie das folgende Wörterbuch:
dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}
Und du willst es in einer so umgekehrten Form bekommen:
inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}
Erste Lösung . Verwenden Sie zum Invertieren von Schlüssel-Wert- Paaren in Ihrem Wörterbuch einen for
-loop-Ansatz:
# 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)
Zweite Lösung . Verwenden Sie einen Wörterbuchverständnisansatz für die Inversion:
# Use this code to invert dictionaries that have unique values
inverted_dict = {value: key for key, value in dict.items()}
Dritte Lösung . Verwenden Sie das Zurücksetzen des Inversionsansatzes (basiert auf der zweiten Lösung):
# 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
ist reserviert und sollte nicht für Variablennamen verwendet werden
my_map
ist
dictio()
? Meinten Sie dict()
?
Kombination aus Listen- und Wörterbuchverständnis. Kann doppelte Schlüssel verarbeiten
{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}
Wenn die Werte nicht eindeutig sind und Sie ein wenig Hardcore sind:
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())
)
Beachten Sie insbesondere bei einem großen Diktat, dass diese Lösung weitaus weniger effizient ist als die Antwort Python, die ein Mapping umkehrt / umkehrt, da es items()
mehrere Schleifen durchläuft.
-1
weil es immer noch die Frage beantwortet, nur meine Meinung.
Zusätzlich zu den anderen oben vorgeschlagenen Funktionen, wenn Sie Lambdas mögen:
invert = lambda mydict: {v:k for k, v in mydict.items()}
Oder Sie könnten es auch so machen:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )
Ich denke, der beste Weg, dies zu tun, besteht darin, eine Klasse zu definieren. Hier ist eine Implementierung eines "symmetrischen Wörterbuchs":
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
Lösch- und Iterationsmethoden sind bei Bedarf einfach zu implementieren.
Diese Implementierung ist weitaus effizienter als das Invertieren eines gesamten Wörterbuchs (was auf dieser Seite die beliebteste Lösung zu sein scheint). Ganz zu schweigen davon, dass Sie Ihrem SymDict beliebig viele Werte hinzufügen oder daraus entfernen können, und Ihr inverses Wörterbuch bleibt immer gültig - dies gilt nicht, wenn Sie das gesamte Wörterbuch einfach einmal umkehren.
dictresize
, aber dieser Ansatz verweigert Python diese Möglichkeit.
Dies behandelt nicht eindeutige Werte und behält einen Großteil des Aussehens des eindeutigen Gehäuses bei.
inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}
Für Python 3.x, ersetzen itervalues
mit values
.
Die Funktion ist für Werte der Typliste symmetrisch. Tupel werden in Listen verdeckt, wenn reverse_dict (reverse_dict (Wörterbuch)) ausgeführt wird.
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
Da Wörterbücher im Gegensatz zu Werten einen eindeutigen Schlüssel im Wörterbuch benötigen, müssen wir die umgekehrten Werte an eine Sortierliste anhängen, die in den neuen spezifischen Schlüsseln enthalten sein soll.
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
Schnelle funktionale Lösung für nicht-bijektive Karten (Werte nicht eindeutig):
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
)
}
Theoretisch sollte dies schneller sein als das Hinzufügen zur Menge (oder das Anhängen an die Liste) nacheinander, wie in der imperativen Lösung .
Leider müssen die Werte sortierbar sein, die Sortierung wird von groupby benötigt.
n
Elemente im ursprünglichen Diktat ist Ihr Ansatz O(n log n)
zeitlich komplex, da die Elemente des Diktats sortiert werden müssen, während der naive imperative Ansatz O(n)
zeitlich komplex ist. Soweit ich weiß, ist Ihr Ansatz vielleicht schneller, bis er dict
in der Praxis absurd groß ist , aber theoretisch ist er sicherlich nicht schneller.
Ich würde es so in Python 2 machen.
inv_map = {my_map[x] : x for x in my_map}
dict.items
(oder iteritems
in Python 2) ist effizienter als das separate Extrahieren jedes Werts beim Iterieren von Schlüsseln.
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})
Dies liefert folgende Ausgabe: {1: ['a', 'd'], 2: ['b'], 3: ['c']}
dict.items
(oder iteritems
in Python 2) ist effizienter als das separate Extrahieren jedes Werts beim Iterieren von Schlüsseln. Außerdem haben Sie einer Antwort, die andere dupliziert, keine Erklärung hinzugefügt.
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
Dieser Code gefällt so:
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']}
Nicht etwas ganz anderes, nur ein bisschen umgeschriebenes Rezept aus dem Kochbuch. Es wird weiter optimiert, indem die setdefault
Methode beibehalten wird, anstatt sie jedes Mal durch die Instanz zu bringen:
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)
Entwarf, unter CPython 3.x ausgeführt zu werden, für 2.x zu ersetzen mapping.items()
durchmapping.iteritems()
Auf meinem Computer läuft etwas schneller als andere Beispiele hier
dict
zu erstellen und am Ende in die gewünschte Klasse zu konvertieren (anstatt mit einer Klasse des richtigen Typs zu beginnen), scheint mir hier einen völlig vermeidbaren Leistungseinbruch zu verursachen.
Ich habe dies mit Hilfe von Zyklus 'für' und Methode '.get ()' geschrieben und den Namen 'Karte' des Wörterbuchs in 'Karte1' geändert, weil 'Karte' eine Funktion ist.
def dict_invert(map1):
inv_map = {} # new dictionary
for key in map1.keys():
inv_map[map1.get(key)] = key
return inv_map
Wenn die Werte nicht eindeutig sind UND ein Hash sein kann (eine Dimension):
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)
Und mit einer Rekursion, wenn Sie tiefer graben müssen als nur eine Dimension:
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"}
zu {'b': ['foo'], 'a': ['foo'], 'r': ['foo']}
und löst eine Ausnahme aus, wenn ein Wert in myDict
nicht iterierbar ist. Ich bin mir nicht sicher, welches Verhalten Sie hier implementieren wollten, aber was Sie tatsächlich implementiert haben, wird so ziemlich niemand wollen.
my_map.items()
funktioniert das auch