Dado un diccionario como este:
my_map = {'a': 1, 'b': 2}
¿Cómo se puede invertir este mapa para obtener:
inv_map = {1: 'a', 2: 'b'}
Dado un diccionario como este:
my_map = {'a': 1, 'b': 2}
¿Cómo se puede invertir este mapa para obtener:
inv_map = {1: 'a', 2: 'b'}
Respuestas:
Para Python 2.7.x
inv_map = {v: k for k, v in my_map.iteritems()}
Para 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
. No hay garantía de que se mantenga así, así que no escriba código confiando en Dict
tener el mismo comportamiento que OrderedDict
.
Suponiendo que los valores en el dict son únicos:
dict((v, k) for k, v in my_map.iteritems())
iteritems()
saldrá, por lo que se puede suponer que se asignará una clave arbitraria para un valor no único, de una manera que aparentemente será reproducible en algunas condiciones, pero en general no.
iteritems()
método y este enfoque no funcionará; use en items()
su lugar como se muestra en la respuesta aceptada. Además, una comprensión del diccionario lo haría más bonito que llamar dict
.
Si los valores en my_map
no son únicos:
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, [])
devuelve la lista ya agregada si hay una, por lo que la asignación no se restablece a una lista vacía. setdefault
sin embargo, aún sería más bonita.
inv_map.setdefault(v, set()).add(k)
.
my_map.items()
lugar de my_map.iteritems()
.
Para hacer esto mientras se preserva el tipo de su mapeo (suponiendo que sea una dict
o una dict
subclase):
def inverse_mapping(f):
return f.__class__(map(reversed, f.items()))
Prueba esto:
inv_map = dict(zip(my_map.values(), my_map.keys()))
(Tenga en cuenta que los documentos de Python en las vistas de diccionario garantizan explícitamente que .keys()
y.values()
tienen sus elementos en el mismo orden, lo que permite que funcione el enfoque anterior).
Alternativamente:
inv_map = dict((my_map[k], k) for k in my_map)
o usando las comprensiones dict de python 3.0
inv_map = {my_map[k] : k for k in my_map}
Otra forma más funcional:
my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))
filter
y map
debería morir y ser incluido en las comprensiones de la lista, no crecer más variantes".
dict
con otros tipos de mapeo como collections.OrderedDict
ocollections.defaultdict
Esto amplía la respuesta de Robert , aplicando cuando los valores en el dict no son únicos.
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
La implementación es limitada, ya que no se puede usar reversed
dos veces y recuperar el original. No es simétrico como tal. Se prueba con Python 2.6. Aquí hay un caso de uso de cómo estoy usando para imprimir el dict resultante.
Si prefiere usar a set
que a list
, y podría existir aplicaciones desordenadas para las que esto tiene sentido, en lugar desetdefault(v, []).append(k)
usar setdefault(v, set()).add(k)
.
revdict.setdefault(v, set()).add(k)
set
. Es el tipo intrínseco que se aplica aquí. ¿Qué sucede si quiero encontrar todas las claves donde los valores no son 1
o 2
? Entonces solo puedo hacer d.keys() - inv_d[1] - inv_d[2]
(en Python 3)
También podemos revertir un diccionario con claves duplicadas 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']}
Mira aquí :
Esta técnica es más simple y más rápida que una técnica equivalente
dict.setdefault()
.
Por ejemplo, tiene el siguiente diccionario:
dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}
Y quieres obtenerlo en una forma tan invertida:
inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}
La primera solución . Para invertir pares clave-valor en su diccionario, utilice un for
enfoque de bucle:
# 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)
Segunda solución . Utilice un enfoque de comprensión del diccionario para la inversión:
# Use this code to invert dictionaries that have unique values
inverted_dict = {value: key for key, value in dict.items()}
Tercera solución . Utilice revertir el enfoque de inversión (se basa en la segunda solución):
# 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
está reservado y no debe usarse para nombres de variables
my_map
es
dictio()
? Quiso decir dict()
?
Combinación de lista y comprensión de diccionario. Puede manejar claves duplicadas
{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}
Si los valores no son únicos y eres un poco duro:
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())
)
Especialmente para un dict grande, tenga en cuenta que esta solución es mucho menos eficiente que la respuesta Python invertir / invertir una asignación porque se repite items()
varias veces.
-1
porque todavía responde la pregunta, solo mi opinión.
Además de las otras funciones sugeridas anteriormente, si le gustan las lambdas:
invert = lambda mydict: {v:k for k, v in mydict.items()}
O también puedes hacerlo de esta manera:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )
Creo que la mejor manera de hacer esto es definir una clase. Aquí hay una implementación de un "diccionario simétrico":
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
Los métodos de eliminación e iteración son lo suficientemente fáciles de implementar si son necesarios.
Esta implementación es mucho más eficiente que invertir un diccionario completo (que parece ser la solución más popular en esta página). Sin mencionar que puede agregar o eliminar valores de su SymDict tanto como desee, y su diccionario inverso siempre será válido; esto no es cierto si simplemente invierte todo el diccionario una vez.
dictresize
, pero este enfoque niega a Python esa posibilidad.
Esto maneja valores no únicos y conserva gran parte del aspecto del caso único.
inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}
Para Python 3.x, reemplace itervalues
con values
.
La función es simétrica para los valores de la lista de tipos; Las tuplas se convierten en listas cuando se realiza reverse_dict (reverse_dict (diccionario))
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
Dado que los diccionarios requieren una clave única dentro del diccionario a diferencia de los valores, tenemos que agregar los valores invertidos en una lista de clasificación para incluirlos en las nuevas claves específicas.
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
Solución funcional rápida para mapas no biyectivos (valores no únicos):
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
)
}
En teoría, esto debería ser más rápido que agregar al conjunto (o agregar a la lista) uno por uno, como en la solución imperativa .
Desafortunadamente, los valores tienen que ser ordenables, la clasificación es requerida por groupby
n
elementos en el dict original, su enfoque tiene O(n log n)
complejidad de tiempo debido a la necesidad de clasificar los elementos del dict, mientras que el enfoque imperativo ingenuo tiene O(n)
complejidad de tiempo. Por lo que sé, su enfoque puede ser más rápido hasta absurdamente grande dict
en la práctica , pero ciertamente no es más rápido en teoría.
Lo haría de esa manera en Python 2.
inv_map = {my_map[x] : x for x in my_map}
dict.items
(o iteritems
en Python 2) es más eficiente que extraer cada valor por separado mientras se repiten las claves.
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})
Esto proporcionará resultados como: {1: ['a', 'd'], 2: ['b'], 3: ['c']}
dict.items
(o iteritems
en Python 2) es más eficiente que extraer cada valor por separado mientras se repiten las claves. Además, no ha agregado ninguna explicación a una respuesta que duplica a otras.
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
este código hace así:
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']}
No es algo completamente diferente, solo una receta un poco reescrita de Cookbook. Además, está optimizado por el setdefault
método de retención , en lugar de hacerlo cada vez a través de la instancia:
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)
Diseñado para ejecutarse en CPython 3.x, para 2.x reemplazar mapping.items()
conmapping.iteritems()
En mi máquina funciona un poco más rápido que otros ejemplos aquí
dict
ay luego convertirlo a la clase deseada al final (en lugar de comenzar con una clase del tipo correcto) me parece que incurre en un éxito de rendimiento completamente evitable, aquí.
Escribí esto con la ayuda del ciclo 'for' y el método '.get ()' y cambié el nombre 'map' del diccionario a 'map1' porque 'map' es una función.
def dict_invert(map1):
inv_map = {} # new dictionary
for key in map1.keys():
inv_map[map1.get(key)] = key
return inv_map
Si los valores no son únicos Y pueden ser un hash (una dimensión):
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)
Y con una recursión si necesita profundizar, solo una dimensión:
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"}
a {'b': ['foo'], 'a': ['foo'], 'r': ['foo']}
y levanta una excepción si cualquier valor en myDict
no es un iterable. No estoy seguro de qué comportamiento estaba tratando de implementar aquí, pero lo que realmente ha implementado es algo que nadie querrá.
my_map.items()
funciona