Question Comment trier un dictionnaire par valeur?


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

Je peux trier sur les touches, mais comment puis-je trier en fonction des valeurs?

Note: J'ai lu la question Stack Overflow Comment trier une liste de dictionnaires par valeurs du dictionnaire en Python? et probablement pourrait changer mon code pour avoir une liste de dictionnaires, mais puisque je n'ai pas vraiment besoin d'une liste de dictionnaires je voulais savoir s'il y a une solution plus simple.


2932
2018-03-05 00:49


origine


Réponses:


Il n'est pas possible de trier un dictionnaire, uniquement pour obtenir une représentation d'un dictionnaire 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 second élément dans chaque tuple. dict(sorted_x) == x.

Et pour ceux qui souhaitent trier sur les clés au lieu des 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] on peut utiliser

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

3550
2018-03-05 00:59



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

Eh bien, il est réellement possible de faire un "tri par les valeurs du dictionnaire". Récemment, j'ai dû le faire dans un Code Golf (Stack Overflow question Code golf: Graphique de la fréquence des mots). Abrégé, le problème était du genre: à partir d'un texte, comptez combien de fois chaque mot est rencontré et affichez une liste des mots les plus importants, triés 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

alors vous pouvez obtenir une liste des mots, classés par fréquence d'utilisation avec sorted(d, key=d.get) - le tri itére sur les clés du dictionnaire, en utilisant le nombre d'occurrences de mots comme une 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 PO 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.


967
2017-07-05 08:01



Vous pouvez utiliser:

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

Cela va trier le dictionnaire par les valeurs de chaque entrée dans le dictionnaire du plus petit au plus grand.


601
2018-02-13 16:33



Les dictons ne peuvent pas être triés, mais vous pouvez en créer une liste triée.

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))

165
2018-03-05 01:05



En Python 2.7 récent, nous avons le nouveau OrderedDict type, 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, en le triant 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 dict 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)])

128
2017-07-05 02:50



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

Bien que la réponse acceptée ait été utile, j'ai également été surpris qu'elle n'ait pas été mise à jour OrderedDict de la bibliothèque standard des collections module comme une alternative viable et moderne - conçu 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)])

L'officiel OrderedDict La documentation offre 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)])

75
2017-12-05 09:46



Il peut souvent être très pratique à utiliser namedtuple. Par exemple, vous avez un dictionnaire de 'nom' comme clés et 'score' comme valeurs et vous voulez trier sur 'score':

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

trier avec le plus bas score en premier:

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

trier avec le plus haut score 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

64
2017-08-30 00:30



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


    trié ([(valeur, clé) pour (clé, valeur) dans mydict.items ()])

Ou optimisé un peu comme suggéré par John Fouhy;


    trié ((valeur, clé) pour (clé, valeur) dans mydict.items ())


57
2018-03-05 01:06



À partir de Python 3.6 le dict intégré sera commandé

Bonne nouvelle, le cas d'utilisation original de paires de mappage extraites d'une base de données avec des ID de chaîne uniques comme clés et des valeurs numériques comme valeurs dans un dict Python v3.6 + intégré devrait maintenant respecter l'ordre d'insertion.

Si dis les deux expressions de table de colonne résultant d'une requête de base de données comme:

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 de cours), alors:

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

Autoriser à sortir plus tard comme:

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

cédant dans ce cas (pour la nouvelle dictée intégrée Python 3.6+):

foo 0
bar 1
baz 42

dans le même ordre par valeur de v.

Où dans l'installation de 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 sujet "Dictionnaires plus compacts avec une itération plus rapide") et maintenant (en 2016) annoncé dans un mail par Victor Stinner à python-dev avec sujet "Python 3.6 dict devient compact et obtient une version privée, et les mots-clés deviennent commandés" en raison de la correction / mise en œuvre du problème 27350 "Dict compact et ordonné" en 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 de OrderedDict en couche mince dans un premier temps. Comme l'a indiqué @ JimFasarakis-Hilliard, certains voient des cas d'utilisation pour le type OrderedDict également dans le futur. Je pense que la communauté Python dans son ensemble va inspecter soigneusement, si cela va résister à l'épreuve du temps, et quelles seront les prochaines étapes.

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

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

La première parce qu'elle facilite l'envoi dans l'implémentation de fonctions et de 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 une documentation expliquant "The Tech Behind Python 3.6 Dictionnaires"- de sa présentation de San Francisco Python Meetup Group 2016-DEC-08.

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

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

Comme le note justement @ajcr: "L'aspect préservation de l'ordre de cette nouvelle implémentation est considéré comme un détail d'implémentation et ne doit pas être utilisé." (du whatsnew36) pas de prélèvement de nit, mais la citation a été coupée un peu pessimiste ;-). Il continue comme "(ceci peut changer dans le futur, mais il est souhaitable d'avoir cette nouvelle implémentation de dict dans la langue pour quelques versions avant de changer la spécification de langage pour mandater la sémantique préservant l'ordre pour toutes les implémentations Python actuelles et futures. aide à préserver la rétrocompatibilité 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 envoyer à la liste python-dev, Guido van Rossum a déclaré:

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

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


48
2017-09-10 10:05



Dictionnaire donné

e = {1:39, 4:34, 7:110, 2:87}

Tri

sred = sorted(e.items(), key=lambda value: value[1])

Résultat

[(4, 34), (1, 39), (2, 87), (7, 110)]

Vous pouvez utiliser une fonction lambda pour trier les valeurs par valeur et les stocker traitées dans une variable, dans ce cas sred avec e le dictionnaire original.

J'espère que cela pourra aider!


41
2018-01-25 14:54