Вопрос: Как отсортировать словарь по значению?


У меня есть словарь значений, считанных из двух полей в базе данных: поле строки и числовое поле. Строковое поле уникально, поэтому это ключ словаря.

Я могу сортировать по ключам, но как я могу сортировать на основе значений?

Примечание. Я прочитал вопрос о переполнении стека Как отсортировать список словарей по значениям словаря в Python? и, возможно, я могу изменить свой код, чтобы иметь список словарей, но поскольку мне действительно не нужен список словарей, я хотел бы знать, есть ли более простое решение.


2847


источник


Ответы:


Невозможно отсортировать словарь, только чтобы получить представление отсортированного словаря. Словари по своей природе являются бесхозными, но другие типы, такие как списки и кортежи, не являются. Поэтому вам нужен упорядоченный тип данных для представления отсортированных значений, которые будут списком, возможно, списком кортежей.

Например,

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

sorted_xбудет список кортежей, отсортированных по второму элементу в каждом кортеже. dict(sorted_x) == x,

И для тех, кто хочет сортировать ключи вместо значений:

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

3456



Так просто как: sorted(dict1, key=dict1.get)

Ну, на самом деле можно сделать «сортировку по значениям словаря». Недавно мне пришлось сделать это в поле Code Golf (Stack Overflow question) Код гольфа: частотная диаграмма слов ). Сокращенный, проблема была такой: учитывая текст, подсчитывайте, как часто встречается каждое слово, и отображает список верхних слов, отсортированных по уменьшению частоты.

Если вы строите словарь со словами в виде ключей и количеством вхождений каждого слова в качестве значения, упрощается здесь как:

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

то вы можете получить список слов, упорядоченных по частоте использования с sorted(d, key=d.get)- сортировка выполняется по клавишам словаря, используя количество вхождений слов в качестве ключа сортировки.

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

Я пишу это подробное объяснение, чтобы проиллюстрировать, что люди часто подразумевают под «Я могу легко сортировать словарь по ключевым словам, но как я сортирую по значению» - и я думаю, что ОП пытался решить такую ​​проблему. И решение состоит в том, чтобы сделать список ключей, основанный на значениях, как показано выше.


954



Вы можете использовать:

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

Это сортирует словарь по значениям каждой записи в словаре от наименьшего до самого большого.


584



Диктов нельзя сортировать, но вы можете создать из них отсортированный список.

Сортированный список значений dict:

sorted(d.values())

Список пар (ключ, значение), отсортированный по значению:

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

159



В недавнем Python 2.7 у нас есть новый OrderedDict тип, который запоминает порядок, в котором элементы были добавлены.

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

Чтобы создать новый упорядоченный словарь из оригинала, сортировка по значениям:

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

OrderedDict ведет себя как обычный dict:

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

120



ОБНОВЛЕНИЕ: 5 ДЕКАБРЯ 2015 г. с использованием Python 3.5

В то время как я нашел приемлемый ответ полезным, я также был удивлен, что он не был обновлен до ссылки OrderedDict из стандартной библиотеки коллекции модуль как жизнеспособная, современная альтернатива - предназначенная для решения именно такого рода проблем.

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

Официальный OrderedDict документация предлагает очень похожий пример, но с использованием лямбда для функции сортировки:

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

70



Часто бывает очень удобно использовать namedtuple , Например, у вас есть словарь «имя» в качестве ключей и «оценка» в качестве значений, и вы хотите сортировать по «оценке»:

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

сначала сортировка с минимальным счетом:

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

сначала сортировка с наивысшим балл:

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

Теперь вы можете получить имя и оценку, скажем, второго лучшего игрока (index = 1) очень Pythonically следующим образом:

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

59



Pretty much the same as Hank Gay's answer;


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

Or optimized a bit as suggested by John Fouhy;


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


52



As of Python 3.6 the built-in dict will be ordered

Good news, so the OP's original use case of mapping pairs retrieved from a database with unique string ids as keys and numeric values as values into a built-in Python v3.6+ dict, should now respect the insert order.

If say the resulting two column table expressions from a database query like:

SELECT a_key, a_value FROM a_table ORDER BY a_value;

would be stored in two Python tuples, k_seq and v_seq (aligned by numerical index and with the same length of course), then:

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

Allow to output later as:

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

yielding in this case (for the new Python 3.6+ built-in dict!):

foo 0
bar 1
baz 42

in the same ordering per value of v.

Where in the Python 3.5 install on my machine it currently yields:

bar 1
foo 0
baz 42

Details:

As proposed in 2012 by Raymond Hettinger (cf. mail on python-dev with subject "More compact dictionaries with faster iteration") and now (in 2016) announced in a mail by Victor Stinner to python-dev with subject "Python 3.6 dict becomes compact and gets a private version; and keywords become ordered" due to the fix/implementation of issue 27350 "Compact and ordered dict" in Python 3.6 we will now be able, to use a built-in dict to maintain insert order!!

Hopefully this will lead to a thin layer OrderedDict implementation as a first step. As @JimFasarakis-Hilliard indicated, some see use cases for the OrderedDict type also in the future. I think the Python community at large will carefully inspect, if this will stand the test of time, and what the next steps will be.

Time to rethink our coding habits to not miss the possibilities opened by stable ordering of:

  • Keyword arguments and
  • (intermediate) dict storage

The first because it eases dispatch in the implementation of functions and methods in some cases.

The second as it encourages to more easily use dicts as intermediate storage in processing pipelines.

Raymond Hettinger kindly provided documentation explaining "The Tech Behind Python 3.6 Dictionaries" - from his San Francisco Python Meetup Group presentation 2016-DEC-08.

And maybe quite some Stack Overflow high decorated question and answer pages will receive variants of this information and many high quality answers will require a per version update too.

Caveat Emptor (but also see below update 2017-12-15):

As @ajcr rightfully notes: "The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon." (from the whatsnew36) not nit picking, but the citation was cut a bit pessimistic ;-). It continues as " (this may change in the future, but it is desired to have this new dict implementation in the language for a few releases before changing the language spec to mandate order-preserving semantics for all current and future Python implementations; this also helps preserve backwards-compatibility with older versions of the language where random iteration order is still in effect, e.g. Python 3.5)."

So as in some human languages (e.g. German), usage shapes the language, and the will now has been declared ... in whatsnew36.

Update 2017-12-15:

In a mail to the python-dev list, Guido van Rossum declared:

Make it so. "Dict keeps insertion order" is the ruling. Thanks!

So, the version 3.6 CPython side-effect of dict insertion ordering is now becoming part of the language spec (and not anymore only an implementation detail). That mail thread also surfaced some distinguishing design goals for collections.OrderedDict as reminded by Raymond Hettinger during discussion.


42



Given dictionary

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

Sorting

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

Result

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

You can use a lambda function to sort things up by value and store them processed inside a variable, in this case sred with e the original dictionary.

Hope that helps!


35



I had the same problem, and I solved it like this:

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

(People who answer "It is not possible to sort a dict" did not read the question! In fact, "I can sort on the keys, but how can I sort based on the values?" clearly means that he wants a list of the keys sorted according to the value of their values.)

Please notice that the order is not well defined (keys with the same value will be in an arbitrary order in the output list).


34