Python如何按值对字典进行排序?

我有一个从数据库中的两个字段中读取值的字典:一个字符串字段和一个数字字段。字符串字段是唯一的,所以这是字典的关键。

我可以对键进行排序,但是如何根据这些值进行排序?

注意:我已阅读Stack Overflow问题如何按照Python中字典的值对字典列表进行排序?并可能可以改变我的代码有一个字典的列表,但因为我真的不需要一个字典的列表,我想知道是否有一个更简单的解决方案。

答案


无法对字典进行排序,只能得到已排序的字典的表示形式。字典本质上是无序的,但其他类型,如列表和元组,不是。所以你需要一个有序的数据类型来表示排序的值,这将是一个列表 – 可能是一个元组列表。

例如,

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


简单如下: sorted(dict1, key=dict1.get)

那么,它实际上可以做一个“按字典值排序”。最近我不得不在Code Golf中做这件事(Stack Overflow question Code golf:Word frequency chart)。摘要的问题是这样的:给定一个文本,计算每个单词遇到的频率并显示一个顶级单词列表,按照频率降低排序。

如果您构造一个字典,其中的关键词和每个词的出现次数为值,在此简化为:

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]

我正在写这个详细的解释来说明人们通常所说的“我可以轻松地按键排序字典,但我怎么按价值排序” – 我认为OP正试图解决这个问题。解决方案是根据数值对键进行排序,如上所示。


你可以使用:

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

这将按字典中的每个条目从最小到最大的值对字典进行排序。


字典不能排序,但你可以从它们建立一个排序列表。

字典值的排序列表:

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的行为像一个正常的字典:

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

更新:2015年12月5日使用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文档也提供了一个非常相似的例子,但是使用lambda作为排序函数:

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

使用namedtuple通常非常方便。例如,您有一个’name’字典作为键和’score’作为值,并且您想按’score’排序:

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)非常像Python:

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

Python 3.6开始,内置的字典将会被订购

好消息,所以OP的原始用例将映射从数据库中检索的对以唯一的字符串ID作为键和数值作为值映射到内置的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 +内置字典!):

foo 0
bar 1
baz 42

按每个v值的顺序排列。

在我的机器上安装Python 3.5的地方,它现在会产生:

bar 1
foo 0
baz 42

细节:

正如Raymond Hettinger在2012年提出的(参见python-dev主题为“更紧凑的词典,更快的迭代”),现在(2016年),Victor Stinner在Python-dev的邮件中宣布了Python的Python 3.6 dict变为压缩并获得一个私有版本;而且由于Python 3.6中的问题27350的修复/实现“紧凑且有序的dict”,我们现在可以使用内置的字典来维护插入顺序,因此关键字变得有序

希望这将导致OrderedDict实现的薄层作为第一步。正如@ JimFasarakis-Hilliard所指出的那样,有些人在将来也会看到OrderedDict类型的用例。我认为,如果这将经得起时间的考验,接下来的步骤是什么,那么Python社区将会仔细检查。

现在是时候重新思考我们的编码习惯,不要错过稳定排序开放的可能性:

  • 关键字参数和
  • (中级)dict存储

第一个是因为它在某些情况下简化了函数和方法的实现。

第二,它鼓励更容易地使用dicts作为处理管道中的中间存储。

Raymond Hettinger亲切地提供了解释“ The Python Behind Python 3.6 Dictionaries ”的文档- 来自他的旧金山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名单邮件中,Guido van Rossum宣称:

做到这一点。“Dict保持插入顺序”是裁决。谢谢!

因此,dict插入排序的3.6版CPython副作用现在已成为语言规范的一部分(而不仅仅是一个实现细节)。collections.OrderedDict正如Raymond Hettinger在讨论中提醒的那样,该邮件线程还提供了一些可区分的设计目标。


我遇到了同样的问题,我解决了这个问题:

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

(回答“不可能排序字典”的人没有读到这个问题!事实上,“我可以对键进行排序,但是我怎样才能根据这些值进行排序?”显然意味着他想要一个列表按照它们的值的值排序的键)。

请注意订单没有被很好地定义(在输出列表中,具有相同值的键将以任意顺序)。


这是代码:

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}

添加评论

友情链接:蝴蝶教程