Étant donné un dictionnaire comme celui-ci:
my_map = {'a': 1, 'b': 2}
Comment inverser cette carte pour obtenir:
inv_map = {1: 'a', 2: 'b'}
Étant donné un dictionnaire comme celui-ci:
my_map = {'a': 1, 'b': 2}
Comment inverser cette carte pour obtenir:
inv_map = {1: 'a', 2: 'b'}
Réponses:
Pour Python 2.7.x
inv_map = {v: k for k, v in my_map.iteritems()}
Pour 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
. Il n'y a aucune garantie que cela restera ainsi, donc n'écrivez pas de code en vous basant sur Dict
le même comportement que OrderedDict
.
En supposant que les valeurs du dict sont uniques:
dict((v, k) for k, v in my_map.iteritems())
iteritems()
sortira, donc on peut supposer qu'une clé arbitraire sera attribuée pour une valeur non unique, d'une manière qui sera apparemment reproductible dans certaines conditions, mais pas en général.
iteritems()
méthode et que cette approche ne fonctionnera pas; utilisez items()
-le à la place comme indiqué dans la réponse acceptée. De plus, une compréhension du dictionnaire rendrait cela plus joli que d'appeler dict
.
Si les valeurs de my_map
ne sont pas uniques:
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, [])
renvoie la liste déjà ajoutée s'il y en a une, donc l'affectation ne se réinitialise pas dans une liste vide. setdefault
serait encore plus joli, cependant.
inv_map.setdefault(v, set()).add(k)
.
my_map.items()
au lieu de my_map.iteritems()
.
Pour ce faire tout en préservant le type de votre mappage (en supposant qu'il s'agit d'une dict
ou d'une dict
sous - classe):
def inverse_mapping(f):
return f.__class__(map(reversed, f.items()))
Essaye ça:
inv_map = dict(zip(my_map.values(), my_map.keys()))
(Notez que les documents Python sur les vues de dictionnaire garantissent explicitement cela .keys()
et .values()
ont leurs éléments dans le même ordre, ce qui permet à l'approche ci-dessus de fonctionner.)
Alternativement:
inv_map = dict((my_map[k], k) for k in my_map)
ou en utilisant les compréhensions dict de python 3.0
inv_map = {my_map[k] : k for k in my_map}
Une autre façon, plus fonctionnelle:
my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))
filter
et map
devrait mourir et être inclus dans des listes de compréhension, ne pas développer plus de variantes".
dict
avec d'autres types de mappage tels que collections.OrderedDict
oucollections.defaultdict
Cela étend la réponse de Robert , s'appliquant lorsque les valeurs du dict ne sont pas uniques.
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'implémentation est limitée en ce sens que vous ne pouvez pas l'utiliser reversed
deux fois et récupérer l'original. Il n'est pas symétrique en tant que tel. Il est testé avec Python 2.6. Voici un cas d'utilisation de la façon dont j'utilise pour imprimer le dict résultant.
Si vous préférez utiliser un set
plutôt qu'un list
, et il pourrait exister des applications non ordonnées pour lesquelles cela a du sens, au lieu de setdefault(v, []).append(k)
, utiliser setdefault(v, set()).add(k)
.
revdict.setdefault(v, set()).add(k)
set
. C'est le type intrinsèque qui s'applique ici. Et si je veux trouver toutes les clés où les valeurs ne sont pas 1
ou 2
? Ensuite, je peux simplement faire d.keys() - inv_d[1] - inv_d[2]
(en Python 3)
Nous pouvons également inverser un dictionnaire avec des clés en double en utilisant 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']}
Voir ici :
Cette technique est plus simple et plus rapide qu'une technique équivalente utilisant
dict.setdefault()
.
Par exemple, vous disposez du dictionnaire suivant:
dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}
Et vous voulez l'obtenir sous une telle forme inversée:
inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}
Première solution . Pour inverser les paires clé-valeur dans votre dictionnaire, utilisez une for
approche -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)
Deuxième solution . Utilisez une approche de compréhension de dictionnaire pour l'inversion:
# Use this code to invert dictionaries that have unique values
inverted_dict = {value: key for key, value in dict.items()}
Troisième solution . Utilisation de l'inversion de l' approche d' inversion (repose sur la deuxième solution):
# 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 réservé et ne doit pas être utilisé pour les noms de variables
my_map
est
dictio()
? Vous vouliez dire dict()
?
Combinaison de la liste et de la compréhension du dictionnaire. Peut gérer les clés en double
{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}
Si les valeurs ne sont pas uniques et que vous êtes un peu 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())
)
Surtout pour un dict large, notez que cette solution est beaucoup moins efficace que la réponse Python inverser / inverser un mappage car il boucle items()
plusieurs fois.
-1
car cela répond toujours à la question, juste mon opinion.
En plus des autres fonctions suggérées ci-dessus, si vous aimez les lambdas:
invert = lambda mydict: {v:k for k, v in mydict.items()}
Ou, vous pouvez aussi le faire de cette façon:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )
Je pense que la meilleure façon de le faire est de définir une classe. Voici une implémentation d'un "dictionnaire symétrique":
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
Les méthodes de suppression et d'itération sont assez faciles à mettre en œuvre si elles sont nécessaires.
Cette implémentation est bien plus efficace que d'inverser un dictionnaire entier (ce qui semble être la solution la plus populaire sur cette page). Sans oublier, vous pouvez ajouter ou supprimer des valeurs de votre SymDict autant que vous le souhaitez, et votre dictionnaire inverse restera toujours valide - ce n'est pas vrai si vous inversez simplement le dictionnaire entier une fois.
dictresize
, mais cette approche refuse à Python cette possibilité.
Cela gère les valeurs non uniques et conserve une grande partie de l'apparence du boîtier unique.
inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}
Pour Python 3.x, remplacez itervalues
par values
.
La fonction est symétrique pour les valeurs de type list; Les tuples sont convertis en listes lors de l'exécution de reverse_dict (reverse_dict (dictionnaire))
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
Étant donné que les dictionnaires nécessitent une clé unique dans le dictionnaire contrairement aux valeurs, nous devons ajouter les valeurs inversées dans une liste de tri à inclure dans les nouvelles clés spécifiques.
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
Solution fonctionnelle rapide pour les cartes non bijectives (valeurs non uniques):
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 théorie, cela devrait être plus rapide que d'ajouter un à un à l'ensemble (ou de l'ajouter à la liste) comme dans la solution impérative .
Malheureusement, les valeurs doivent être triables, le tri est requis par groupby.
n
éléments du dict original, votre approche présente une O(n log n)
complexité temporelle en raison de la nécessité de trier les éléments du dict, tandis que l'approche impérative naïve a une O(n)
complexité temporelle. Pour autant que je sache, votre approche peut être plus rapide jusqu'à des absurdement grands dict
en pratique , mais ce n'est certainement pas plus rapide en théorie.
Je le ferais de cette façon en python 2.
inv_map = {my_map[x] : x for x in my_map}
dict.items
(ou iteritems
en Python 2) est plus efficace que d'extraire chaque valeur séparément lors de l'itération des clés.
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})
Cela fournira la sortie comme: {1: ['a', 'd'], 2: ['b'], 3: ['c']}
dict.items
(ou iteritems
en Python 2) est plus efficace que d'extraire chaque valeur séparément lors de l'itération des clés. De plus, vous n'avez ajouté aucune explication à une réponse qui en double d'autres.
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
ce code fait ceci:
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']}
Pas quelque chose de complètement différent, juste une recette un peu réécrite de Cookbook. Il est en outre optimisé en conservant la setdefault
méthode, au lieu de le faire à chaque fois passer par l'instance:
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)
Conçu pour être exécuté sous CPython 3.x, pour 2.x remplacer mapping.items()
parmapping.iteritems()
Sur ma machine tourne un peu plus vite que les autres exemples ici
dict
puis convertir à la classe souhaitée à la fin (plutôt que de commencer par une classe du bon type) me semble que cela entraîne un impact sur les performances entièrement évitable, ici.
J'ai écrit cela à l'aide du cycle 'for' et de la méthode '.get ()' et j'ai changé le nom 'map' du dictionnaire en 'map1' parce que 'map' est une fonction.
def dict_invert(map1):
inv_map = {} # new dictionary
for key in map1.keys():
inv_map[map1.get(key)] = key
return inv_map
Si les valeurs ne sont pas uniques ET peuvent être un hachage (une 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)
Et avec une récursivité si vous avez besoin de creuser plus profondément, alors juste une 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"}
en {'b': ['foo'], 'a': ['foo'], 'r': ['foo']}
et déclenche une exception si une valeur dans myDict
n'est pas un itérable. Je ne sais pas quel comportement vous essayez d'implémenter ici, mais ce que vous avez réellement implémenté est quelque chose que personne ne voudra.
my_map.items()
marche aussi