Comment trier un dictionnaire par valeur?


3422

J'ai un dictionnaire de valeurs lues dans deux champs d'une base de données: un champ de chaîne et un champ numérique. Le champ de chaîne est unique, c'est donc la clé du dictionnaire.

Je peux trier sur les clés, mais comment trier en fonction des valeurs?

Remarque: J'ai lu la question Stack Overflow ici Comment puis-je trier une liste de dictionnaires par valeur du dictionnaire? et je pourrais probablement changer mon code pour avoir une liste de dictionnaires, mais comme je n'ai pas vraiment besoin d'une liste de dictionnaires, je voulais savoir s'il existe une solution plus simple pour trier par ordre croissant ou décroissant.


9
La structure des données du dictionnaire n'a pas d'ordre inhérent. Vous pouvez le parcourir, mais rien ne garantit que l'itération suivra un ordre particulier. C'est par conception, donc votre meilleur pari est probablement d'utiliser une structure de données différente pour la représentation.
Daishiman

135
"sorted ()" peut fonctionner sur les dictionnaires (et retourne une liste de clés triées), donc je pense qu'il en est conscient. Sans connaître son programme, il est absurde de dire à quelqu'un qu'il utilise la mauvaise structure de données. Si vous avez besoin de recherches rapides dans 90% des cas, un dict est probablement ce que vous voulez.
bobpaul

Les trois sorties (clés, valeurs, les deux) pour le tri des dictionnaires sont couvertes ici dans un style clair et concis: stackoverflow.com/questions/16772071/sort-dict-by-value-python
JStrahl

2
@Daishiman La classe de base n'est peut-être pas commandée mais OrderedDict l' est bien sûr.
Taylor Edmiston

1
Dans Python 3.6+, les dictionnaires préservent l'ordre d'insertion. Ce n'est bien sûr pas la même chose que la possibilité de les trier par valeur, mais d'un autre côté, il n'est plus valable de dire que "la structure des données du dictionnaire n'a pas d'ordre inhérent".
Konrad Kocik

Réponses:


4960

Python 3.6+

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
{k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}

Python plus ancien

Il n'est pas possible de trier un dictionnaire, seulement pour obtenir une représentation d'un dictionnaire qui est trié. Les dictionnaires sont intrinsèquement sans ordre, mais d'autres types, tels que les listes et les tuples, ne le sont pas. Vous avez donc besoin d'un type de données ordonné pour représenter les valeurs triées, qui sera une liste, probablement une liste de tuples.

Par exemple,

import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(1))

sorted_x sera une liste de tuples triés par le deuxième élément de chaque tuple. dict(sorted_x) == x.

Et pour ceux qui souhaitent trier sur des clés au lieu de valeurs:

import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(0))

En Python3 puisque le déballage n'est pas autorisé [1] nous pouvons utiliser

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=lambda kv: kv[1])

Si vous souhaitez que la sortie soit un dict, vous pouvez utiliser collections.OrderedDict:

import collections

sorted_dict = collections.OrderedDict(sorted_x)

43
pour les timings sur divers dictionnaires de tri par schémas de valeurs: writeonly.wordpress.com/2008/08/30/…
Gregg Lind

164
sorted_x.reverse()vous donnera un ordre décroissant (par le deuxième élément de tuple)
saidimu apale

414
saidimu: Puisque nous utilisons déjà sorted(), il est beaucoup plus efficace de passer l' reverse=Trueargument.
rmh

121
Dans python3 je un lambda: sorted(d.items(), key=lambda x: x[1]). Est-ce que cela fonctionnera en python 2.x?
Keyo

84
OrderedDict ajouté aux collections en 2.7. Exemple de tri affiché sur: docs.python.org/library/…
monkut

1264

Aussi simple que: sorted(dict1, key=dict1.get)

Eh bien, il est en fait possible de faire un "tri par valeurs de dictionnaire". Récemment, j'ai dû le faire dans un code Golf (Stack Overflow question Code golf: tableau de fréquence des mots ). Bref, le problème était du genre: compte tenu d'un texte, comptez la fréquence de chaque mot rencontré et affichez une liste des premiers mots, triée par fréquence décroissante.

Si vous construisez un dictionnaire avec les mots comme clés et le nombre d'occurrences de chaque mot comme valeur, simplifié ici comme:

from collections import defaultdict
d = defaultdict(int)
for w in text.split():
    d[w] += 1

vous pouvez alors obtenir une liste des mots, classés par fréquence d'utilisation avec sorted(d, key=d.get)- le tri se répète sur les clés du dictionnaire, en utilisant le nombre d'occurrences de mots comme clé de tri.

for w in sorted(d, key=d.get, reverse=True):
    print(w, d[w])

J'écris cette explication détaillée pour illustrer ce que les gens entendent souvent par "Je peux facilement trier un dictionnaire par clé, mais comment trier par valeur" - et je pense que le message d'origine essayait de résoudre un tel problème. Et la solution est de faire une sorte de liste des clés, basée sur les valeurs, comme indiqué ci-dessus.


32
Ceci est également bon, mais key=operator.itemgetter(1)devrait être plus évolutif pour l'efficacité quekey=d.get
smci

3
@raylu J'observe un comportement "ne fonctionne pas" en utilisant itemgetter: ----- from operator import itemgetter d = {"a":7, "b":1, "c":5, "d":3} sorted_keys = sorted(d, key=itemgetter, reverse=True) for key in sorted_keys: print "%s: %d" % (key, d[key]) ----- -> b: 1 c: 5 a: 7 d: 3 Les résultats changent chaque fois que j'exécute le code: bizarre . (désolé, impossible d'afficher le code correctement)
bli

12
@bli sorted_keys = sorted(d.items(), key=itemgetter(1), reverse=True)et for key, val in sorted_keys: print "%s: %d" % (key, val)- itemgetter crée une fonction lorsqu'elle est appelée, vous ne l'utilisez pas directement comme dans votre exemple. Et une simple itération sur un dict utilise les clés sans les valeurs
Izkata

18
Je viens du futur pour vous en parler collections.Counter, qui a une most_commonméthode qui pourrait vous intéresser :)
Evoli

2
@Eevee - il y a cela, le risque d'être mal compris dans les courts commentaires non f2f. Amuzing tidbit: a collections.Counterété ajouté en 2.7, qui est sorti il ​​y a presque exactement 7 ans! (Je ne le savais pas alors - en plus je l'aurais évité dans le code-golf à des fins de brièveté, qui est la seule obsession du jeu)
Nas Banov

859

Vous pouvez utiliser:

sorted(d.items(), key=lambda x: x[1])

Cela triera le dictionnaire selon les valeurs de chaque entrée du dictionnaire du plus petit au plus grand.

Pour le trier par ordre décroissant, ajoutez simplement reverse=True:

sorted(d.items(), key=lambda x: x[1], reverse=True)

Contribution:

d = {'one':1,'three':3,'five':5,'two':2,'four':4}
a = sorted(d.items(), key=lambda x: x[1])    
print(a)

Production:

[('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)]

D'après ce que j'ai vu ( docs.python.org/2/library/… ), il existe une classe appelée OrderedDict qui peut être triée et conserver l'ordre tout en restant un dictionnaire. À partir des exemples de code, vous pouvez utiliser lambda pour le trier, mais je ne l'ai pas essayé personnellement: P
UsAndRufus

53
Je préfère key=lambda (k, v): vpersonnellement
Claudiu

35
@Claudiu J'aime aussi cette (k, v)syntaxe, mais elle n'est pas disponible dans Python 3 où la décompression des paramètres de tuple a été supprimée.
Bob Stein

2
dict(sorted(d.items(), key=lambda x: x[1])).
Dr_Hope

3
Non. Créer un dict à partir d'une liste triée de tuples tue à nouveau l'ordre ...
Jean-François Fabre

233

Les dictés ne peuvent pas être triés, mais vous pouvez créer une liste triée à partir d'eux.

Une liste triée de valeurs dict:

sorted(d.values())

Une liste de paires (clé, valeur), triées par valeur:

from operator import itemgetter
sorted(d.items(), key=itemgetter(1))

Dans quel ordre sont placées les clés de même valeur? J'ai trié la liste par clés d'abord, puis par valeurs, mais l'ordre des clés avec la même valeur ne reste pas.
SabreWolfy

1
Les dictés peuvent maintenant être triés, en commençant par CPython 3.6 et toutes les autres implémentations de Python commençant par 3.7
Boris

161

Dans Python 2.7 récent, nous avons le nouveau type OrderedDict , qui se souvient de l'ordre dans lequel les éléments ont été ajoutés.

>>> d = {"third": 3, "first": 1, "fourth": 4, "second": 2}

>>> for k, v in d.items():
...     print "%s: %s" % (k, v)
...
second: 2
fourth: 4
third: 3
first: 1

>>> d
{'second': 2, 'fourth': 4, 'third': 3, 'first': 1}

Pour créer un nouveau dictionnaire ordonné à partir de l'original, triez par les valeurs:

>>> from collections import OrderedDict
>>> d_sorted_by_value = OrderedDict(sorted(d.items(), key=lambda x: x[1]))

Le OrderedDict se comporte comme un dicton normal:

>>> for k, v in d_sorted_by_value.items():
...     print "%s: %s" % (k, v)
...
first: 1
second: 2
third: 3
fourth: 4

>>> d_sorted_by_value
OrderedDict([('first': 1), ('second': 2), ('third': 3), ('fourth': 4)])

5
Ce n'est pas de cela qu'il s'agit - il ne s'agit pas de maintenir l'ordre des clés mais de "trier par valeur"
Nas Banov

10
@Nas Banov: ce n'est PAS un tri par clé. c'est le tri dans l'ordre, on crée les articles. dans notre cas, nous trions par valeur. malheureusement, le dict de 3 éléments a malheureusement été choisi, de sorte que l'ordre était le même, lorsqu'il était trié par valeur et par clé, j'ai donc développé l'échantillon de dict.
mykhal

sorted(d.items(), key=lambda x: x[1])Pouvez-vous expliquer ce xque cela signifie, pourquoi cela peut prendre x[1]pour lambda? Pourquoi ça ne peut pas être x[0]? Merci beaucoup!
JZAU

1
@Boern d.items()renvoie un conteneur semblable à une liste de (key, value)tuples. [0]accède au premier élément du tuple - la clé - et [1]accède au deuxième élément - la valeur.
BallpointBen

2
Remarque: Depuis la version 3.6 (en tant que détail d'implémentation CPython / PyPy) et la version 3.7 (en tant que garantie du langage Python), plain dictest également inséré dans l'ordre, vous pouvez donc simplement le remplacer OrderedDictpar dictdu code fonctionnant sur Python moderne. OrderedDictn'est plus vraiment nécessaire, sauf si vous devez réorganiser l'ordre d'un existant dict(avec move_to_end/ popitem) ou avoir besoin de comparaisons d'égalité pour être sensible à l'ordre. Il utilise beaucoup plus de mémoire que plain dict, donc si vous le pouvez, dictc'est la voie à suivre.
ShadowRanger

104

MISE À JOUR: 5 DÉCEMBRE 2015 en utilisant Python 3.5

Bien que j'ai trouvé la réponse acceptée utile, j'ai également été surprise qu'elle n'ait pas été mise à jour pour référencer OrderedDict à partir du module de collections de bibliothèques standard comme une alternative viable et moderne - conçue pour résoudre exactement ce type de problème.

from operator import itemgetter
from collections import OrderedDict

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = OrderedDict(sorted(x.items(), key=itemgetter(1)))
# OrderedDict([(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)])

La documentation officielle OrderedDict propose également un exemple très similaire, mais en utilisant un lambda pour la fonction de tri:

# regular unsorted dictionary
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

# dictionary sorted by value
OrderedDict(sorted(d.items(), key=lambda t: t[1]))
# OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

86

À peu près la même chose que la réponse de Hank Gay :

sorted([(value,key) for (key,value) in mydict.items()])

Ou légèrement optimisé comme suggéré par John Fouhy:

sorted((value,key) for (key,value) in mydict.items())

9
..et comme avec la réponse de Hank Gay, vous n'avez pas besoin des crochets. sorted () prendra volontiers tout itérable, comme une expression de générateur.
John Fouhy

Vous devrez peut-être encore échanger les éléments de tuple (valeur, clé) pour vous retrouver avec (clé, valeur). Une autre compréhension de la liste est alors nécessaire. [(key, value) for (value, key) in sorted_list_of_tuples]
saidimu apale

non, il vaut mieux laisser des crochets, car il sortedfaudra quand même reconstruire la liste, et la reconstruction à partir de gencomp sera plus rapide. Bon pour le codegolfing, mauvais pour la vitesse. Gardez la ([])version laide .
Jean-François Fabre

75

Il peut souvent être très pratique d'utiliser namedtuple . Par exemple, vous avez un dictionnaire de «nom» en tant que clés et de «score» en tant que valeurs et vous souhaitez trier sur «score»:

import collections
Player = collections.namedtuple('Player', 'score name')
d = {'John':5, 'Alex':10, 'Richard': 7}

tri avec le score le plus bas en premier:

worst = sorted(Player(v,k) for (k,v) in d.items())

tri avec le score le plus élevé en premier:

best = sorted([Player(v,k) for (k,v) in d.items()], reverse=True)

Maintenant, vous pouvez obtenir le nom et le score de, disons le deuxième meilleur joueur (index = 1) très Pythoniquement comme ceci:

player = best[1]
player.name
    'Richard'
player.score
    7

Comment pourrais-je le reconvertir en dictionnaire?
rowana

as_list = [Joueur (v, k) pour (k, v) dans d.items ()] as_dict = dict ((p.nom, p.score) pour p dans as_list)
Remi

72

Depuis Python 3.6 le dict intégré sera commandé

Bonne nouvelle, le cas d'utilisation d'origine de l'OP de mappage de paires récupérées dans une base de données avec des ID de chaîne uniques en tant que clés et des valeurs numériques en tant que valeurs dans un dict Python v3.6 + intégré devrait désormais respecter l'ordre d'insertion.

Si, par exemple, les expressions de table à deux colonnes résultant d'une requête de base de données:

SELECT a_key, a_value FROM a_table ORDER BY a_value;

serait stocké dans deux tuples Python, k_seq et v_seq (alignés par index numérique et avec la même longueur bien sûr), puis:

k_seq = ('foo', 'bar', 'baz')
v_seq = (0, 1, 42)
ordered_map = dict(zip(k_seq, v_seq))

Autoriser à sortir plus tard en tant que:

for k, v in ordered_map.items():
    print(k, v)

donnant dans ce cas (pour le nouveau dict intégré Python 3.6+!):

foo 0
bar 1
baz 42

dans le même ordre par valeur de v.

Où dans l'installation Python 3.5 sur ma machine, il donne actuellement:

bar 1
foo 0
baz 42

Détails:

Comme proposé en 2012 par Raymond Hettinger (cf. mail sur python-dev avec le sujet "Dictionnaires plus compacts avec une itération plus rapide" ) et maintenant (en 2016) annoncé dans un mail de Victor Stinner à python-dev avec le sujet "Python 3.6 dict devient compact et obtient une version privée, et les mots clés deviennent ordonnés "en raison de la correction / implémentation du problème 27350 " Compact et ordonné dict " dans Python 3.6, nous allons maintenant pouvoir utiliser un dict intégré pour maintenir l'ordre d'insertion !!

Espérons que cela conduira à une implémentation OrderedDict en couche mince dans un premier temps. Comme l'a indiqué @ JimFasarakis-Hilliard, certains verront également des cas d'utilisation pour le type OrderedDict à l'avenir. Je pense que la communauté Python dans son ensemble inspectera attentivement si cela résistera à l'épreuve du temps et quelles seront les prochaines étapes.

Il est temps de repenser nos habitudes de codage pour ne pas rater les possibilités ouvertes par une commande stable de:

  • Arguments de mots clés et
  • (intermédiaire) stockage de dict

Le premier parce qu'il facilite la répartition dans l'implémentation des fonctions et méthodes dans certains cas.

La seconde car elle encourage à utiliser plus facilement dicts comme stockage intermédiaire dans les pipelines de traitement.

Raymond Hettinger a aimablement fourni de la documentation expliquant " The Tech Behind Python 3.6 Dictionaries " - de sa présentation du San Francisco Python Meetup Group 2016-DEC-08.

Et peut-être que certaines pages de questions et réponses décorées de Stack Overflow recevront des variantes de ces informations et de nombreuses réponses de haute qualité nécessiteront également une mise à jour par version.

Caveat Emptor (mais voir également la mise à jour ci-dessous 2017-12-15):

Comme le note @ajcr à juste titre: "L'aspect préservant l'ordre de cette nouvelle implémentation est considéré comme un détail d'implémentation et ne doit pas être invoqué." (à partir du whatsnew36 ) pas de cueillette, mais la citation a été coupée un peu pessimiste ;-). Il continue comme "(cela peut changer à l'avenir, mais il est souhaitable d'avoir cette nouvelle implémentation de dict dans le langage pour quelques versions avant de changer la spécification du langage pour rendre obligatoire la sémantique préservant l'ordre pour toutes les implémentations Python actuelles et futures; cela aussi aide à préserver la compatibilité descendante avec les anciennes versions du langage où l'ordre d'itération aléatoire est toujours en vigueur, par exemple Python 3.5). "

Ainsi, comme dans certaines langues humaines (par exemple l'allemand), l'usage façonne la langue, et la volonté a maintenant été déclarée ... dans whatsnew36 .

Mise à jour 2017-12-15:

Dans un mail à la liste python-dev , Guido van Rossum a déclaré:

Faire en sorte. "Dict conserve l'ordre d'insertion" est la décision. Merci!

Ainsi, l'effet secondaire CPython de la version 3.6 de l'ordre d'insertion de dict fait maintenant partie de la spécification de langage (et non plus seulement un détail d'implémentation). Ce fil de discussion a également fait ressortir certains objectifs de conception distinctifs, collections.OrderedDictcomme l'a rappelé Raymond Hettinger lors de la discussion.


15
Il convient de souligner l' avertissement sur la page `` Quoi de neuf '' auquel vous avez lié: l'aspect préservant l'ordre de cette nouvelle implémentation est considéré comme un détail d'implémentation et ne doit pas être invoqué . Personne ne devrait supposer que le dicttype respectera l'ordre d'insertion dans son code. Cela ne fait pas partie de la définition du langage et l'implémentation pourrait changer dans toute version future. Continuez à utiliser OrderedDictpour garantir la commande.
Alex Riley

@ajcr merci pour l'avertissement, très apprécié - comme des smileys et peut-être ont été tissés dans ma réponse, ceux-ci devraient indiquer, le changement est massif mais bien sûr, uniquement disponible pour CPython (implémentation de référence) et PyPy. Pour quelque chose de complètement différent ... Je parle rarement des détails de non-implémentation lors du codage des instructions homme-machine. Si ça n'avait été que Jython ;-) ... Je n'aurais peut-être pas eu le courage de l'écrire.
Dilettant

OrderedDictne sera certainement pas abandonné; au lieu de cela, il deviendra un wrapper mince autour de l'implémentation actuelle de dict (vous pouvez donc ajouter qu'il deviendra plus compact aussi). L'ajout de cet extrait avec le ImportErrorn'est pas tout à fait la meilleure idée, car il induit les lecteurs en erreur qui OrderedDictne servent à rien.
Dimitris Fasarakis Hilliard

@ JimFasarakis-Hilliard merci pour la rétroaction. Les «meilleures idées» m'ont fait sourire - l'avenir est souvent difficile à prévoir. Mais j'aime bien que votre suggestion vérifie les sources, l'essaye puis met à jour la réponse en conséquence. Merci encore.
Dilettant

8
@AlexRiley Cette mise en garde n'est plus exacte. Python3.7 garantit des dictionnaires ordonnés.
gerrit

42

J'ai eu le même problème et je l'ai résolu comme ceci:

WantedOutput = sorted(MyDict, key=lambda x : MyDict[x]) 

(Les personnes qui répondent "Il n'est pas possible de trier un dict" n'ont pas lu la question! En fait, "Je peux trier sur les clés, mais comment trier en fonction des valeurs?" Signifie clairement qu'il veut une liste de les clés triées selon la valeur de leurs valeurs.)

Veuillez noter que l'ordre n'est pas bien défini (les clés avec la même valeur seront dans un ordre arbitraire dans la liste de sortie).


1
Il vous manque la valeur du résultat
Dejell

Notez que vous êtes à la fois en train d'itérer le dictionnaire et de récupérer des valeurs par leur clé, donc en termes de performances, ce n'est pas une solution optimale.
Ron Klein

1
@Dejell: comme le dit le contributeur, il interprète la question comme "puis-je obtenir la liste des clés triées selon les valeurs". Nous n'avons pas besoin des valeurs dans le résultat, nous les avons dans le dictionnaire.
Max

36

Si les valeurs sont numériques, vous pouvez également utiliser Counterdes collections .

from collections import Counter

x = {'hello': 1, 'python': 5, 'world': 3}
c = Counter(x)
print(c.most_common())

>> [('python', 5), ('world', 3), ('hello', 1)]    

qu'en est-il si votre dictionnaire est >>> x = {'bonjour': 1, 'python': 5, 'monde': 300}
James Sapam

@yopy Counter({'hello':1, 'python':5, 'world':300}).most_common()donne [('world', 300), ('python', 5), ('hello', 1)]. Cela fonctionne en fait pour tout type de valeur triable (bien que de nombreuses autres opérations Counter nécessitent des valeurs comparables à des entiers).
lvc

34

En Python 2.7, faites simplement:

from collections import OrderedDict
# regular unsorted dictionary
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

# dictionary sorted by key
OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])

# dictionary sorted by value
OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

copier-coller depuis: http://docs.python.org/dev/library/collections.html#ordereddict-examples-and-recipes

Prendre plaisir ;-)


25

Voici le code:

import operator
origin_list = [
    {"name": "foo", "rank": 0, "rofl": 20000},
    {"name": "Silly", "rank": 15, "rofl": 1000},
    {"name": "Baa", "rank": 300, "rofl": 20},
    {"name": "Zoo", "rank": 10, "rofl": 200},
    {"name": "Penguin", "rank": -1, "rofl": 10000}
]
print ">> Original >>"
for foo in origin_list:
    print foo

print "\n>> Rofl sort >>"
for foo in sorted(origin_list, key=operator.itemgetter("rofl")):
    print foo

print "\n>> Rank sort >>"
for foo in sorted(origin_list, key=operator.itemgetter("rank")):
    print foo

Voici les résultats:

Original

{'name': 'foo', 'rank': 0, 'rofl': 20000}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Baa', 'rank': 300, 'rofl': 20}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Penguin', 'rank': -1, 'rofl': 10000}

Rofl

{'name': 'Baa', 'rank': 300, 'rofl': 20}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Penguin', 'rank': -1, 'rofl': 10000}
{'name': 'foo', 'rank': 0, 'rofl': 20000}

Rang

{'name': 'Penguin', 'rank': -1, 'rofl': 10000}
{'name': 'foo', 'rank': 0, 'rofl': 20000}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Baa', 'rank': 300, 'rofl': 20}

25

Essayez l'approche suivante. Définissons un dictionnaire appelé mydict avec les données suivantes:

mydict = {'carl':40,
          'alan':2,
          'bob':1,
          'danny':3}

Si l'on voulait trier le dictionnaire par clés, on pourrait faire quelque chose comme:

for key in sorted(mydict.iterkeys()):
    print "%s: %s" % (key, mydict[key])

Cela devrait renvoyer la sortie suivante:

alan: 2
bob: 1
carl: 40
danny: 3

D'un autre côté, si l'on voulait trier un dictionnaire par valeur (comme cela est demandé dans la question), on pourrait faire ce qui suit:

for key, value in sorted(mydict.iteritems(), key=lambda (k,v): (v,k)):
    print "%s: %s" % (key, value)

Le résultat de cette commande (tri du dictionnaire par valeur) doit retourner ce qui suit:

bob: 1
alan: 2
danny: 3
carl: 40

Impressionnant! for key, value in sorted(mydict.iteritems(), key=lambda (k,v): v["score"]):vous permet de trier par une sous
Andomar

cela me donne une erreur de syntaxe près de lambda en Python3
Suleman Elahi

21

À partir de Python 3.6, les dictobjets sont désormais classés par ordre d'insertion. C'est officiellement dans les spécifications de Python 3.7.

>>> words = {"python": 2, "blah": 4, "alice": 3}
>>> dict(sorted(words.items(), key=lambda x: x[1]))
{'python': 2, 'alice': 3, 'blah': 4}

Avant cela, vous deviez utiliser OrderedDict.

La documentation de Python 3.7 dit:

Modifié dans la version 3.7: l'ordre du dictionnaire est garanti comme étant l'ordre d'insertion. Ce comportement était le détail d'implémentation de CPython de 3.6.


fonctionne très bien! dict(sorted(words.items(), key=lambda x: x[1], reverse=True))pour DESC
vizyourdata

20

Vous pouvez créer un "index inversé", également

from collections import defaultdict
inverse= defaultdict( list )
for k, v in originalDict.items():
    inverse[v].append( k )

Maintenant, votre inverse a les valeurs; chaque valeur a une liste de clés applicables.

for k in sorted(inverse):
    print k, inverse[k]

19

Vous pouvez utiliser les collections.Counter . Notez que cela fonctionnera pour les valeurs numériques et non numériques.

>>> x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
>>> from collections import Counter
>>> #To sort in reverse order
>>> Counter(x).most_common()
[(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)]
>>> #To sort in ascending order
>>> Counter(x).most_common()[::-1]
[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]
>>> #To get a dictionary sorted by values
>>> from collections import OrderedDict
>>> OrderedDict(Counter(x).most_common()[::-1])
OrderedDict([(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)])

7
En quoi est-ce différent de la réponse d' Ivan Sas ?
Peter Mortensen

15

Vous pouvez utiliser un skip dict qui est un dictionnaire trié en permanence par valeur.

>>> data = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
>>> SkipDict(data)
{0: 0.0, 2: 1.0, 1: 2.0, 4: 3.0, 3: 4.0}

Si vous utilisez keys(), values()ou items()alors vous itérerez dans l'ordre trié par valeur.

Il est implémenté en utilisant la structure de données de la liste de saut.


pouvons-nous changer l'ordre de tri, en ce moment, il est en train de monter, mais je veux décroître.
Suleman Elahi

afaik vous devrez annuler vos valeurs afin d'inverser l'ordre
malthe

wow, je ne pense pas comme ça ... merci.
Suleman Elahi

14

Vous pouvez également utiliser une fonction personnalisée qui peut être transmise à la clé.

def dict_val(x):
    return x[1]
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=dict_val)

12
from django.utils.datastructures import SortedDict

def sortedDictByKey(self,data):
    """Sorted dictionary order by key"""
    sortedDict = SortedDict()
    if data:
        if isinstance(data, dict):
            sortedKey = sorted(data.keys())
            for k in sortedKey:
                sortedDict[k] = data[k]
    return sortedDict

2
la question était: trier par valeur, pas par clés ... J'aime voir une fonction. Vous pouvez importer des collections et bien sûr utiliser triées (data.values ​​())
Remi

10

Comme l'a souligné Dilettant , Python 3.6 gardera désormais l'ordre ! Je pensais partager une fonction que j'ai écrite qui facilite le tri d'un itérable (tuple, list, dict). Dans ce dernier cas, vous pouvez trier sur des clés ou des valeurs, et il peut prendre en compte la comparaison numérique.Uniquement pour> = 3,6!

Lorsque vous essayez d'utiliser trié sur un itérable qui contient par exemple des chaînes ainsi que des entiers, sorted () échoue. Bien sûr, vous pouvez forcer la comparaison de chaînes avec str (). Cependant, dans certains cas, vous souhaitez effectuer une comparaison numérique réelle12est plus petit que 20(ce qui n'est pas le cas dans la comparaison de chaînes). J'ai donc trouvé ce qui suit. Lorsque vous souhaitez une comparaison numérique explicite, vous pouvez utiliser l'indicateurnum_as_num qui tentera de faire un tri numérique explicite en essayant de convertir toutes les valeurs en flottants. Si cela réussit, il fera un tri numérique, sinon il aura recours à la comparaison de chaînes.

Commentaires pour amélioration ou demandes push bienvenues.

def sort_iterable(iterable, sort_on=None, reverse=False, num_as_num=False):
    def _sort(i):
      # sort by 0 = keys, 1 values, None for lists and tuples
      try:
        if num_as_num:
          if i is None:
            _sorted = sorted(iterable, key=lambda v: float(v), reverse=reverse)
          else:
            _sorted = dict(sorted(iterable.items(), key=lambda v: float(v[i]), reverse=reverse))
        else:
          raise TypeError
      except (TypeError, ValueError):
        if i is None:
          _sorted = sorted(iterable, key=lambda v: str(v), reverse=reverse)
        else:
          _sorted = dict(sorted(iterable.items(), key=lambda v: str(v[i]), reverse=reverse))

      return _sorted

    if isinstance(iterable, list):
      sorted_list = _sort(None)
      return sorted_list
    elif isinstance(iterable, tuple):
      sorted_list = tuple(_sort(None))
      return sorted_list
    elif isinstance(iterable, dict):
      if sort_on == 'keys':
        sorted_dict = _sort(0)
        return sorted_dict
      elif sort_on == 'values':
        sorted_dict = _sort(1)
        return sorted_dict
      elif sort_on is not None:
        raise ValueError(f"Unexpected value {sort_on} for sort_on. When sorting a dict, use key or values")
    else:
      raise TypeError(f"Unexpected type {type(iterable)} for iterable. Expected a list, tuple, or dict")

9

Voici une solution utilisant zip sur d.values()etd.keys() . Quelques lignes sur ce lien (sur les objets de la vue Dictionnaire) sont:

Cela permet la création de paires (valeur, clé) à l'aide de zip (): pairs = zip (d.values ​​(), d.keys ()).

Nous pouvons donc faire ce qui suit:

d = {'key1': 874.7, 'key2': 5, 'key3': 8.1}

d_sorted = sorted(zip(d.values(), d.keys()))

print d_sorted 
# prints: [(5, 'key2'), (8.1, 'key3'), (874.7, 'key1')]

9

Bien sûr, rappelez-vous, vous devez utiliser OrderedDictcar les dictionnaires Python réguliers ne conservent pas l'ordre d'origine.

from collections import OrderedDict
a = OrderedDict(sorted(originalDict.items(), key=lambda x: x[1]))

Si vous n'avez pas Python 2.7 ou supérieur, le mieux que vous puissiez faire est d'itérer sur les valeurs dans une fonction de générateur. (Il y a un OrderedDictpour 2.4 et 2.6 ici , mais

a) Je ne sais pas si ça marche

et

b) Vous devez bien sûr le télécharger et l'installer. Si vous ne disposez pas d'un accès administratif, je crains que l'option ne soit supprimée.)


def gen(originalDict):
    for x, y in sorted(zip(originalDict.keys(), originalDict.values()), key=lambda z: z[1]):
        yield (x, y)
    #Yields as a tuple with (key, value). You can iterate with conditional clauses to get what you want. 

for bleh, meh in gen(myDict):
    if bleh == "foo":
        print(myDict[bleh])

Vous pouvez également imprimer chaque valeur

for bleh, meh in gen(myDict):
    print(bleh, meh)

N'oubliez pas de supprimer les parenthèses après l'impression si vous n'utilisez pas Python 3.0 ou supérieur


les dictionnaires Python classiques ne conservent pas l'ordre d'origine - comme dans Python 3.7, ils le font.
gerrit

7

Utilisez ValueSortedDict à partir de dict :

from dicts.sorteddict import ValueSortedDict
d = {1: 2, 3: 4, 4:3, 2:1, 0:0}
sorted_dict = ValueSortedDict(d)
print sorted_dict.items() 

[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]

6

Cela fonctionne dans 3.1.x:

import operator
slovar_sorted=sorted(slovar.items(), key=operator.itemgetter(1), reverse=True)
print(slovar_sorted)

6

Je viens d'apprendre la compétence pertinente de Python pour tout le monde .

Vous pouvez utiliser une liste temporaire pour vous aider à trier le dictionnaire:

#Assume dictionary to be:
d = {'apple': 500.1, 'banana': 1500.2, 'orange': 1.0, 'pineapple': 789.0}

# create a temporary list
tmp = []

# iterate through the dictionary and append each tuple into the temporary list 
for key, value in d.items():
    tmptuple = (value, key)
    tmp.append(tmptuple)

# sort the list in ascending order
tmp = sorted(tmp)

print (tmp)

Si vous souhaitez trier la liste par ordre décroissant, changez simplement la ligne de tri d'origine en:

tmp = sorted(tmp, reverse=True)

En utilisant la compréhension de la liste, la seule ligne serait:

#Assuming the dictionary looks like
d = {'apple': 500.1, 'banana': 1500.2, 'orange': 1.0, 'pineapple': 789.0}
#One liner for sorting in ascending order
print (sorted([(v, k) for k, v in d.items()]))
#One liner for sorting in descending order
print (sorted([(v, k) for k, v in d.items()], reverse=True))

Exemple de sortie:

#Asending order
[(1.0, 'orange'), (500.1, 'apple'), (789.0, 'pineapple'), (1500.2, 'banana')]
#Descending order
[(1500.2, 'banana'), (789.0, 'pineapple'), (500.1, 'apple'), (1.0, 'orange')]

c'est le tri par clés, pas par valeurs.
Rubel

Si vous souhaitez l'imprimer dans le format initial, vous devez faire: imprimer ([(k, v) pour v, k en tri ([(v, k) pour k, v dans d.items ()])]). Le résultat est: [('orange', 1.0), ('apple', 500.1), ('pineapple', 789.0), ('banana', 1500.2)]. Avec [(k, v) pour v, k en tri ([(v, k) pour k, v dans d.items ()], reverse = True)] la sortie est: [('banane', 1500.2), ('ananas', 789.0), ('pomme', 500.1), ('orange', 1.0)]
Hermes Morales

5

Parcourez un dict et triez-le par ses valeurs dans l'ordre décroissant:

$ python --version
Python 3.2.2

$ cat sort_dict_by_val_desc.py 
dictionary = dict(siis = 1, sana = 2, joka = 3, tuli = 4, aina = 5)
for word in sorted(dictionary, key=dictionary.get, reverse=True):
  print(word, dictionary[word])

$ python sort_dict_by_val_desc.py 
aina 5
tuli 4
joka 3
sana 2
siis 1

5

Si vos valeurs sont des entiers et que vous utilisez Python 2.7 ou une version plus récente, vous pouvez utiliser à la collections.Counterplace de dict. La most_commonméthode vous donnera tous les éléments, triés par valeur.


5

Par souci d'exhaustivité, je publie une solution en utilisant heapq . Remarque, cette méthode fonctionnera pour les valeurs numériques et non numériques

>>> x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
>>> x_items = x.items()
>>> heapq.heapify(x_items)
>>> #To sort in reverse order
>>> heapq.nlargest(len(x_items),x_items, operator.itemgetter(1))
[(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)]
>>> #To sort in ascending order
>>> heapq.nsmallest(len(x_items),x_items, operator.itemgetter(1))
[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]

4

En raison de la nécessité de conserver une compatibilité descendante avec les anciennes versions de Python je pense que la solution OrderedDict est très imprudente. Vous voulez quelque chose qui fonctionne avec Python 2.7 et les versions antérieures.

Mais la solution de collecte mentionnée dans une autre réponse est absolument superbe, car vous recyclez une connexion entre la clé et la valeur, ce qui dans le cas des dictionnaires est extrêmement important.

Je ne suis pas d'accord avec le choix numéro un présenté dans une autre réponse, car il jette les clés.

J'ai utilisé la solution mentionnée ci-dessus (code indiqué ci-dessous) et conservé l'accès aux clés et aux valeurs et dans mon cas, l'ordre était sur les valeurs, mais l'importance était l'ordre des clés après avoir ordonné les valeurs.

from collections import Counter

x = {'hello':1, 'python':5, 'world':3}
c=Counter(x)
print c.most_common()


>> [('python', 5), ('world', 3), ('hello', 1)]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.