Как отсортировать словарь по значению?
У меня есть словарь значений, прочитанных из двух полей в базе данных: строковое поле и числовое поле. Строковое поле уникально, так что это ключ словаря.
Я могу сортировать по ключам, но как я могу сортировать по значениям?
Примечание: я прочитал вопрос переполнения стека. Как отсортировать список словарей по значениям словаря в Python? и, возможно, мог бы изменить свой код, чтобы иметь список словарей, но так как мне не нужен список словарей, я хотел знать, есть ли более простое решение.
42 ответа
Сортировать словарь невозможно, только чтобы получить представление отсортированного словаря. Словари по своей природе беспорядочные, но другие типы, такие как списки и кортежи, - нет. Таким образом, вам нужен упорядоченный тип данных для представления отсортированных значений, который будет списком - вероятно, списком кортежей.
Например,
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))
В Python3, поскольку распаковка запрещена [1], мы можем использовать
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_by_value = sorted(x.items(), key=lambda kv: kv[1])
Так просто как: sorted(dict1, key=dict1.get)
Ну, на самом деле можно сделать "сортировку по значениям словаря". Недавно мне пришлось сделать это в Code Golf (вопрос о переполнении стека Code golf: Word частотная диаграмма). Сокращенно, проблема была в следующем: по тексту посчитайте, как часто встречается каждое слово, и отобразите список главных слов, отсортированных по убыванию частоты.
Если вы создаете словарь со словами в качестве ключей и числом вхождений каждого слова в качестве значения, то здесь это упрощается как:
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]
Я пишу это подробное объяснение, чтобы проиллюстрировать, что люди часто имеют в виду, "я могу легко отсортировать словарь по ключу, но как я могу отсортировать по значению" - и я думаю, что ОП пытался решить такую проблему. И решение состоит в том, чтобы сделать вид списка ключей, основанный на значениях, как показано выше.
Вы можете использовать:
sorted(d.items(), key=lambda x: x[1])
Это позволит отсортировать словарь по значениям каждой записи в словаре от наименьшего к наибольшему.
Дики не могут быть отсортированы, но вы можете построить отсортированный список из них.
Сортированный список значений dict:
sorted(d.values())
Список пар (ключ, значение), отсортированных по значению:
from operator import itemgetter
sorted(d.items(), key=itemgetter(1))
В недавнем 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)])
ОБНОВЛЕНИЕ: 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)])
Почти так же, как ответ Хэнка Гея;
отсортировано ([(значение, ключ) для (ключ, значение) в mydict.items()])
Или немного оптимизирован, как предложено Джоном Фухи;
отсортировано ((значение, ключ) для (ключ, значение) в mydict.items())
Начиная с Python 3.6 встроенный dict будет заказан
Хорошие новости, поэтому исходный сценарий использования OP для отображения пар, извлеченных из базы данных с уникальными строковыми идентификаторами в качестве ключей и числовыми значениями в качестве значений во встроенном Python v3.6+ dict, теперь должен соответствовать порядку вставки.
Если, скажем, результирующие выражения таблицы в два столбца из запроса к базе данных, как:
SELECT a_key, a_value FROM a_table ORDER BY a_value;
будет храниться в двух кортежах Python, k_seq и v_seq (выровненных по числовому индексу и, конечно же, с одинаковой длиной), затем:
k_seq = ('foo', 'bar', 'baz')
v_seq = (0, 1, 42)
ordered_map = dict(zip(k_seq, v_seq))
Разрешить вывод позже как:
for k, v in ordered_map.items():
print(k, v)
уступая в этом случае (для нового Python 3.6+ встроенный dict!):
foo 0
bar 1
baz 42
в том же порядке на значение v.
Где в Python 3.5 установить на моей машине это в настоящее время дает:
bar 1
foo 0
baz 42
Подробности:
Как было предложено в 2012 году Рэймондом Хеттингером (см. Почту на python-dev с темой "Более компактные словари с более быстрой итерацией"), а теперь (в 2016 году) объявлено в письме Виктора Стиннера на почту python-dev с темой "Python 3.6 dict становится сжимается и получает приватную версию, а ключевые слова упорядочиваются " из-за исправления / реализации проблемы 27350 " Сжать и упорядочить dict" в Python 3.6, теперь мы сможем использовать встроенный dict для поддержания порядка вставки!!
Надеемся, что это приведет к тонкому слою реализации OrderedDict в качестве первого шага. Как указывало @JimFasarakis-Hilliard, некоторые видят варианты использования для типа OrderedDict и в будущем. Я думаю, что сообщество Python в целом тщательно проверит, выдержит ли это испытание временем, и какими будут следующие шаги.
Время переосмыслить наши привычки кодирования, чтобы не упустить возможности, открываемые стабильным упорядочением:
- Ключевое слово аргументы и
- промежуточное хранение
Первый, потому что в некоторых случаях он облегчает выполнение функций и методов.
Второе, так как это способствует более легкому использованию dict
как промежуточное хранилище в технологических трубопроводах.
Рэймонд Хеттингер любезно предоставил документацию, объясняющую " Технология позади словарей Python 3.6" - из своей презентации в Сан-Франциско Python Meetup Group 2016-DEC-08.
И, может быть, довольно много страниц с вопросами и ответами в Stack Overflow получат варианты этой информации, и многие высококачественные ответы также потребуют обновления для каждой версии.
Предостережение Emptor (но также см. Ниже обновление 2017-12-15):
Как справедливо отмечает @ajcr: "Сохраняющий порядок аспект этой новой реализации считается деталью реализации и на нее не следует полагаться". (из whatsnew36) не гнида, а цитата была вырезана немного пессимистично;-). Это продолжается как " (это может измениться в будущем, но желательно иметь эту новую реализацию dict в языке в течение нескольких выпусков, прежде чем изменить спецификацию языка, чтобы предписывать семантику сохранения порядка для всех текущих и будущих реализаций Python; это также помогает сохранить обратную совместимость со старыми версиями языка, где все еще действует случайный порядок итераций, например, Python 3.5)."
Как и в некоторых человеческих языках (например, в немецком), использование определяет язык, и завещание было объявлено... в whatsnew36.
Обновление 2017-12-15:
В письме к списку python-dev Гвидо ван Россум заявил:
Сделай это так. "Dict сохраняет порядок ввода" - это решение. Спасибо!
Таким образом, побочный эффект CPython версии 3.6 от порядка вставки dict теперь становится частью языковой спецификации (а не только детали реализации). Эта ветка также выявила отличительные цели дизайна для collections.OrderedDict
как напомнил Рэймонд Хеттингер во время обсуждения.
Часто может быть очень удобно использовать 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), очень Pythonly, как это:
player = best[1]
player.name
'Richard'
player.score
7
Начиная с Python 3.6, dict
объекты теперь упорядочены по порядку вставки. Это официально в спецификациях 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}
До этого вам пришлось использовать OrderedDict
,
Документация Python 3.7 гласит:
Изменено в версии 3.7: Порядок словаря гарантированно является порядком вставки. Такое поведение было деталью реализации CPython от 3.6.
У меня была такая же проблема, и я решил ее так:
WantedOutput = sorted(MyDict, key=lambda x : MyDict[x])
(Люди, которые отвечают "Невозможно отсортировать диктовку", не читали вопрос! На самом деле, "Я могу сортировать по ключам, но как я могу сортировать по значениям?" Ясно означает, что он хочет получить список ключи отсортированы по значению их значений.)
Обратите внимание, что порядок не определен правильно (ключи с одинаковым значением будут находиться в произвольном порядке в списке вывода).
Если значения являются числовыми, вы также можете использовать Counter из коллекций.
from collections import Counter
x={'hello':1,'python':5, 'world':3}
c=Counter(x)
print c.most_common()
>> [('python', 5), ('world', 3), ('hello', 1)]
В Python 2.7 просто выполните:
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)])
скопируйте и вставьте из: http://docs.python.org/dev/library/collections.html
Наслаждаться;-)
Данный словарь
e = {1:39, 4:34, 7:110, 2:87}
Сортировка
sred = sorted(e.items(), key=lambda value: value[1])
Результат
[(4, 34), (1, 39), (2, 87), (7, 110)]
Вы можете использовать лямбда-функцию, чтобы отсортировать вещи по значению и сохранить их обработанными внутри переменной, в данном случае это будет исходный словарь.
Надеюсь, это поможет!
Это код:
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
Вот результаты:
оригинал
{'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}
Ранг
{'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}
Попробуйте следующий подход. Давайте определим словарь mydict со следующими данными:
mydict = {'carl':40,
'alan':2,
'bob':1,
'danny':3}
Если кто-то хочет отсортировать словарь по ключам, можно сделать что-то вроде:
for key in sorted(mydict.iterkeys()):
print "%s: %s" % (key, mydict[key])
Это должно вернуть следующий вывод:
alan: 2
bob: 1
carl: 40
danny: 3
С другой стороны, если кто-то хочет отсортировать словарь по значению (как это задается в вопросе), можно сделать следующее:
for key, value in sorted(mydict.iteritems(), key=lambda (k,v): (v,k)):
print "%s: %s" % (key, value)
Результат этой команды (сортировка словаря по значению) должен вернуть следующее:
bob: 1
alan: 2
danny: 3
carl: 40
Вы можете создать "инвертированный индекс", также
from collections import defaultdict
inverse= defaultdict( list )
for k, v in originalDict.items():
inverse[v].append( k )
Теперь ваш обратный имеет значения; у каждого значения есть список применимых ключей.
for k in sorted(inverse):
print k, inverse[k]
Вы можете использовать коллекции. Счетчик. Обратите внимание, что это будет работать как для числовых, так и для не числовых значений.
>>> 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)])
Технически, словари не являются последовательностями, и поэтому не могут быть отсортированы. Вы можете сделать что-то вроде
sorted(a_dictionary.values())
при условии, что производительность не так уж и велика.
Из-за требований сохранить обратную совместимость со старыми версиями Python, я думаю, что решение OrderedDict очень неразумно. Вы хотите что-то, что работает с Python 2.7 и более ранними версиями.
Но решение коллекций, упомянутое в другом ответе, абсолютно превосходно, потому что вы переобучаете связь между ключом и значением, что в случае словарей чрезвычайно важно.
Я не согласен с выбором номер один, представленным в другом ответе, потому что он выбрасывает ключи.
Я использовал решение, упомянутое выше (код, показанный ниже), и сохранил доступ к ключам и значениям, и в моем случае упорядочение было по значениям, но важным было упорядочение ключей после упорядочения значений.
from collections import Counter
x = {'hello':1, 'python':5, 'world':3}
c=Counter(x)
print c.most_common()
>> [('python', 5), ('world', 3), ('hello', 1)]
Вы также можете использовать пользовательскую функцию, которую можно передать клавише.
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)
Еще один способ сделать это - использовать функцию labmda.
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=lambda t: t[1])
Вы можете использовать пропускающий словарь, который является словарем, который постоянно сортируется по значению.
>>> 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}
Если вы используете keys()
, values()
или же items()
тогда вы будете перебирать в отсортированном порядке по значению.
Это реализовано с использованием структуры данных списка пропуска.
Конечно, помните, вам нужно использовать OrderedDict
потому что обычные словари Python не сохраняют первоначальный порядок.
from collections import OrderedDict
a = OrderedDict(sorted(originalDict.items(), key = lambda x: x[1]))
Если у вас нет Python 2.7 или выше, лучшее, что вы можете сделать, - это перебирать значения в функции генератора. (Здесь есть OrderedDict для 2.4 и 2.6, но
a) I don't know about how well it works
а также
b) You have to download and install it of course. If you do not have administrative access, then I'm afraid the option's out.)
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])
Вы также можете распечатать каждое значение
for bleh, meh in gen(myDict):
print(bleh,meh)
Пожалуйста, не забудьте удалить скобки после печати, если вы не используете Python 3.0 или выше
Только что выучил соответствующий навык у Python для всех.
Вы можете использовать временный список для сортировки словаря:
#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)
Если вы хотите отсортировать список в порядке убывания, просто измените исходную строку сортировки на:
tmp = sorted(tmp, reverse=True)
Используя понимание списка, один вкладыш будет:
#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))
Пример вывода:
#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')]
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
Вот решение с использованием zip на d.values()
а такжеd.keys()
, Несколько строк по этой ссылке (в объектах словаря):
Это позволяет создавать пары (значение, ключ) с помощью zip(): pair = zip(d.values (), d.keys()).
Таким образом, мы можем сделать следующее:
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')]
Это возвращает список пар ключ-значение в словаре, отсортированных по значению от самого высокого до самого низкого:
sorted(d.items(), key=lambda x: x[1], reverse=True)
Для словаря, отсортированного по ключу, используйте следующее:
sorted(d.items(), reverse=True)
Возвращаемым является список кортежей, потому что сами словари не могут быть отсортированы.
Это может быть как распечатано, так и отправлено в дальнейшие вычисления.
Как указывает Dilettant, Python 3.6 теперь будет поддерживать порядок! Я думал, что поделюсь функцией, которую я написал, которая облегчает сортировку итерируемого (кортеж, список, dict). В последнем случае вы можете сортировать либо по ключам, либо по значениям, и это может учитывать числовое сравнение. Только для>= 3.6!
Когда вы попытаетесь использовать сортировку для итерируемого объекта, содержащего, например, строки и целые числа, sorted() завершится ошибкой. Конечно, вы можете форсировать сравнение строк с помощью str(). Тем не менее, в некоторых случаях вы хотите сделать фактическое числовое сравнение, где 12
меньше чем 20
(что не так в сравнении строк). Итак, я придумал следующее. Когда вы хотите явное числовое сравнение, вы можете использовать флаг num_as_num
который попытается выполнить явную числовую сортировку, пытаясь преобразовать все значения в числа с плавающей точкой. Если это удастся, он выполнит числовую сортировку, иначе он прибегнет к сравнению строк.
Комментарии по улучшению или push-запросы приветствуются.
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")
Используйте ValueSortedDict из dicts:
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)]
Итерируйте через dict и сортируйте его по значениям в порядке убывания:
$ 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