Python invierte/invierte un mapeo
dictionary mapping (30)
Añadiendo mis 2 centavos de forma pitónica:
inv_map = dict(map(reversed, my_map.items()))
Ejemplo:
In [7]: my_map
Out[7]: {1: ''one'', 2: ''two'', 3: ''three''}
In [8]: inv_map = dict(map(reversed, my_map.items()))
In [9]: inv_map
Out[9]: {''one'': 1, ''three'': 3, ''two'': 2}
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'' }
NOTA my_map
EDITOR: el map
cambió a my_map
para evitar conflictos con la función incorporada, map
. Algunos comentarios pueden verse afectados a continuación.
Además de las otras funciones sugeridas anteriormente, si te gustan las lambdas:
invert = lambda mydict: {v:k for k, v in mydict.items()}
O, también podrías hacerlo de esta manera:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )
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()}
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 bastante 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). Por no mencionar, puedes agregar o eliminar valores de SymDict todo lo que quieras, y tu diccionario inverso siempre será válido, esto no es cierto si simplemente inviertes todo el diccionario una vez.
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 dentro de 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
Escribí esto con la ayuda del ciclo ''para'' y el método ''.get ()'' y cambié el nombre ''mapa'' del diccionario a ''mapa1'' porque ''mapa'' 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
Esta no es la mejor solución, pero funciona. Digamos que el diccionario que queremos invertir es:
dictionary = {''a'': 1, ''b'': 2, ''c'': 3}, entonces:
dictionary = {''a'': 1, ''b'': 2, ''c'': 3}
reverse_dictionary = {}
for index, val in enumerate(list(dictionary.values())):
reverse_dictionary[val] = list(dictionary.keys())[index]
La salida de reverse_dictionary, debe ser {1: ''a'', 2: ''b'', 3: ''c''}
Esto expande la respuesta Python invierte / invierte una asignación , aplicándose a 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 está limitada en el sentido de que no se puede utilizar reversed
dos veces y recuperar el original. No es simétrico como tal. Se prueba con Python 2.6. Here hay un caso de uso de cómo estoy usando para imprimir el dictado resultante.
Si prefiere usar un set
que una list
, y hay aplicaciones para las que esto tiene sentido, en lugar de setdefault(v, []).append(k)
, use setdefault(v, set()).add(k)
.
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, reemplaza itervalues con valores . No puedo tomar crédito por esto ... fue sugerido por Icon Jack.
Invierte tu diccionario:
dict_ = {"k0":"v0", "k1":"v1", "k2":"v1"}
inversed_dict_ = {val: key for key, val in dict_.items()}
print(inversed_dict_["v1"])
La función es simétrica para valores de lista de tipos; Las tuplas se cubren 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
Lo haría así en python 2.
inv_map = {my_map[x] : x for x in my_map}
No es algo completamente diferente, solo una receta un poco reescrita de Cookbook. Se optimiza aún más al conservar el método setdefault
, en lugar de setdefault
por la instancia cada vez:
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 bajo CPython 3.x, para 2.x reemplazar mapping.items()
con mapping.iteritems()
En mi máquina corre un poco más rápido, que otros ejemplos aquí.
Otra forma, más funcional, de:
my_map = { ''a'': 1, ''b'':2 }
dict(map(reversed, my_map.items()))
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()}
Para todo tipo de diccionario, no importa si no tienen valores únicos para usar como claves, puede crear una lista de claves para cada valor
inv_map = {v: inv_map.get(v, []) + [k] for k,v in my_map.items()}
Prueba esto para python 2.7 / 3.x
inv_map={};
for i in my_map:
inv_map[my_map[i]]=i
print inv_map
Prueba esto:
inv_map = dict(zip(my_map.values(), my_map.keys()))
(Tenga en cuenta que los documentos de Python en las vistas del diccionario garantizan explícitamente que .keys()
y .values()
tienen sus elementos en el mismo orden, lo que permite que el enfoque anterior funcione).
Alternativamente:
inv_map = dict((my_map[k], k) for k in my_map)
o usando dictaduras de Python 3.0
inv_map = {my_map[k] : k for k in my_map}
Según mi comentario a la pregunta. Creo que el más sencillo y un trazador de líneas que funciona tanto para Python2 como para Python 3 será
dict(zip(inv_map.values(), inv_map.keys()))
Si los artículos no son únicos, prueba esto:
dict={}
dict1={}
num=int(raw_input(" how many numbers in dict?--> "))
for i in range (0,num):
key=raw_input(" enter key --> ")
value=raw_input("enter value --> ")
dict[key]=value
keys=dict.keys()
values=dict.values()
for b in range (0,num):
keys[b],values[b]=values[b],keys[b]
dict1[keys[b]]=values[b]
print keys
print values
print dict1
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)
Si los valores no son únicos Y puede 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 más, 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)
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 dictado grande, tenga en cuenta que esta solución es mucho menos eficiente que la respuesta que Python invierte / invierte en un mapeo porque se repite en los items()
varias veces.
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 agregarse a la lista) uno por uno, como en la solución imperativa .
Desafortunadamente, los valores tienen que ser ordenados, la clasificación es requerida por groupby.
Suponiendo que los valores en el dict son únicos:
dict((v, k) for k, v in my_map.iteritems())
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 c.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'']}
Ver here
Esta técnica es más simple y más rápida que una técnica equivalente utilizando
dict.setdefault()
.
Usando zip
inv_map = dict(zip(my_map.values(), my_map.keys()))
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 de esta manera:
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'']}
def inverse_mapping(f):
return f.__class__(map(reversed, f.items()))
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á una salida como: {1: [''a'', ''d''], 2: [''b''], 3: [''c'']}