python字典值排序
Posted
技术标签:
【中文标题】python字典值排序【英文标题】:python dictionary values sorting 【发布时间】:2011-08-28 02:40:58 【问题描述】:我有 2 个字典,dict1
和 dict2
,它们包含相同的键,但键的值不同。我想要做的是对每个字典,从最大到最小对值进行排序,然后给每个值一个 1-N 的等级,1 是最大值。从这里,我想获得每个字典中相同键的值的等级差异。例如:
dict1 = a:0.6, b:0.3, c:0.9, d:1.2, e:0.2
dict2 = a:1.4, b:7.7, c:9.0, d:2.5, e:2.0
# sorting by values would look like this:
dict1 = d:1.2, c:0.9, a:0.6, b:0.3, e:0.2
dict2 = c:9.0, b:7.7, d:2.5, e:2.0, a:1.4
#ranking the values would produce this:
dict1 = d:1, c:2, a:3, b:4, e:5
dict2 = c:1, b:2, d:3, e:4, a:5
#computing the difference between ranks would be something like this:
diffs =
for x in dict1.keys():
diffs[x] = (dict1[x] - dict2[x])
#diffs would look like this:
diffs[a] = -2
diffs[b] = 2
diffs[c] = 1
diffs[d] = -2
diffs[e] = 1
我知道字典是随机的且不可排序的,但也许有一种方法可以将键和值放入列表中?我面临的主要挑战是获取按值(从大到小)排序的键和值,然后将值更改为其在排序列表中的相应排名。
【问题讨论】:
【参考方案1】:小字典的简单解决方案是
dict1 = "a":0.6, "b":0.3, "c":0.9, "d":1.2, "e":0.2
dict2 = "a":1.4, "b":7.7, "c":9.0, "d":2.5, "e":2.0
k1 = sorted(dict1, key=dict1.get)
k2 = sorted(dict2, key=dict2.get)
diffs = dict((k, k2.index(k) - k1.index(k)) for k in dict1)
一个更高效、可读性更强的版本,适用于更大的字典:
ranks1 = dict(map(reversed, enumerate(sorted(dict1, key=dict1.get))))
ranks2 = dict(map(reversed, enumerate(sorted(dict2, key=dict2.get))))
diffs = dict((k, ranks2[k] - ranks1[k]) for k in dict1)
【讨论】:
+1。没有多少人以这种方式使用map
和reversed
。棘手:-) 但我建议使用itertools.imap
来节省一些内存。
同意。通常,出于性能原因,我建议在 map 上使用列表推导或生成器表达式(直到现在才知道 imap),但我认为这是一个更好、更易读的解决方案。荣誉:-)
sorted(dict1.items(), key=lambda item: item[1])
会不会更快,因为这样您就不需要查找字典中的每个值了?
@Aleksi:首先,字典查找非常快。我认为sorted(dict1, key=dict1.get)
更快,因为您没有为每个项目调用 Python 函数的开销。当然你的表达式可以写成sorted(dict1.items(), key=operator.itermgetter(1))
,去掉lambda函数并使这个参数无效。但其次,您的表达式返回的列表与我的不同。我需要以某种方式摆脱这些价值观。
@Sven:是的,我在想dict((key, rank) for rank, (key, value) in enumerate(sorted(dict1.items(), key=operator.itemgetter(1))))
中的一些东西,但是如果列表理解比map
+reversed
慢,我的观点确实没有实际意义。跨度>
【参考方案2】:
您可能对collections.OrderedDict感兴趣
这是一个示例,我最初的想法是您还在寻找具有按值排序的键的字典,od1
和 od2
是。
d1 = "a":0.6, "b":0.3, "c":0.9, "d":1.2, "e":0.2
d2 = "a":1.4, "b":7.7, "c":9.0, "d":2.5, "e":2.0
od1 = OrderedDict(sorted(d1.items(), key=lambda t: t[1]))
od2 = OrderedDict(sorted(d2.items(), key=lambda t: t[1]))
k1 = od1.keys()
k2 = od2.keys()
diff = dict((k, n - k2.index(k)) for n, k in enumerate(k1))
如果您不需要它们,那么 Sven 解决方案可能会更快。
编辑:说实话没那么快...(sven.py 是他的第二个更高效的版本):
$ cat /tmp/mine.py | time python -m timeit
10000000 loops, best of 3: 0.0842 usec per loop
real 0m 3.69s
user 0m 3.38s
sys 0m 0.03s
$ cat /tmp/sven.py | time python -m timeit
10000000 loops, best of 3: 0.085 usec per loop
real 0m 3.86s
user 0m 3.42s
sys 0m 0.03s
如果有人想发布更大的格式化字典,我也会对其进行测试。
【讨论】:
这是错误的工作工具,提问者并不真正需要有序字典,他只需要一种对值进行排序的方法。 Asker 说:我面临的主要挑战是获取按值排序的键和值,OrderedDict
在我看来是正确的工具
我看不出OrderedDict
如何简化生成diffs
字典的任务。你能举一个完整的例子吗? (顺便说一句,不是我的反对票。)
是的,但是看问题。首先你有字典,然后你需要得到值的排名(你不需要有序的字典),然后你想要最终字典中的排名结果。您在任何地方都需要有序字典,它们是适合这项工作的错误工具。例如,看看 Svens 的回答。【参考方案3】:
你用的是什么版本的python?如果是 2.7,请使用 OrderedDict。
根据 Python 2.7 docs:
OrderedDict(sorted(d.items(), key=d.get))
如果您使用的是 Python 2.4-2.6,您仍然可以通过从 pypi here 安装 OrderedDict 来使用它,或者如果您有 setuptools,请运行
easy_install ordereddict
【讨论】:
同意!特别是因为我们的两个答案都是完全有效的解决方案。我也对你的投票表示赞同,以否定做这件事的人的行为。 我没有投反对票,但我认为OrderedDict
在这里没有任何帮助。要实现 OP 想要的,您不需要实际对字典进行排序。而且,你给的key函数是错误的。
哦该死的,你说得对关键功能。我只是直接从文档中复制/粘贴...将编辑。【参考方案4】:
字典不是解决这个问题的正确数据结构。您应该尽快转换为排序列表并仅生成字典作为最终结果。以下示例解决方案尽可能使用迭代器和生成器表达式,以避免在此过程中创建过多(可能很大)的帮助器列表:
def get_ranking(vals):
'''Return a list of pairs: (key, ranking), sorted by key.'''
ranking = sorted(((v, k) for k, v in vals.iteritems()), reverse=True)
return sorted((k, i) for (i, (_v, k)) in enumerate(ranking))
def ranking_diff(rank1, rank2):
return dict((k, v1 - v2) for (k, v1), (_, v2) in itertools.izip(rank1, rank2))
def get_diffs(dict1, dict2):
r1 = get_ranking(dict1)
r2 = get_ranking(dict2)
return ranking_diff(r1, r2)
print get_diffs(dict1, dict2)
# prints: 'a': -2, 'c': 1, 'b': 2, 'e': 1, 'd': -2
请注意,此解决方案假定两个 dicts 包含完全相同的键。
【讨论】:
以上是关于python字典值排序的主要内容,如果未能解决你的问题,请参考以下文章