Python Cookbook-5.4 根据对应值将键或索引排序

任务

需要统计不同元素出现的次数,并且根据它们的出现次数安排它们的顺序——比如,你想制作一个柱状图。

解决方案

柱状图,如果不考虑它在图形图像上的含义,实际上是基于各种不同元素(用Python的列表或字典很容易处理)出现的次数,根据对应值将键或索引排序。下面是dict的一个子类,它为了这种应用加入了两个方法:

class hist(dict):
	def add(self,item,increment=1):
		'''为item的条目增加计数'''
		self[item] = increment + self.get(item,0)
	def counts(self,reverse=False):
		'''返回根据对应值排序的键的列表'''
		aux = [ (self[k],k) for k in self]
		aux.sort()
		if reverse: aux.reverse()
		return [k for v,k in aux]

如果想将元素的统计结果放到一个列表中,做法也非常类似:

class hist1(list):
	def __init__(self,n):
		'''初始化列表,统计几个不同项的出现'''
		list.__init__(self,n*[0])
	def add(self,item,increment=1):
		'''为item的条目增加计数1'''
		self[item] += increment
	def counts(self,reverse=False):
		'''返回根据对应值排序的索引的列表'''
		aux = [(v,k) for k,v in enumerate(self)]
		aux.sort()
		if reverse: aux.reverse()
		return [k for v,k in aux]

讨论

hist 的 add 方法展示了 Python用于统计任意(可哈希的)元素的常用方法,并使用 dict来记录次数。在类 hist1中,在一个普通的列表的基础上,我们采用了不同的方法,并在__init__中将所有的次数都设置成0,因而add 方法就变得更简单了。

counts 方法生成了一个键或者索引的列表,并且根据对应值进行了排序。这两个类针对的问题很类似,因此解决方式也几乎完全一样,都使用了前面 5.2 节和 5.3 节展示过的DSU。如果我们想要在自己的程序中使用这两个类,由于它们的相似性,我们应该进行代码重构,从中间分离出共性并置入一个单独的辅助函数_sorted_keys:

def _sorted_keys(container,keys,reverse):
	'''返回 keys的列表,根据container中的对应值排序'''
	aux = [(container[k],k) for k in keys]
	aux.sort()
	if reverse: aux.reverse()
	return [k for v,k in aux]

然后实现各个类的 counts方法,其实就是对 _sorted_keys 函数进行一层很薄的封装:

class hist(dict):
	...
	def counts(self,reverse=False):
		return _sorted_keys(self,self,reverse)
class hist1(list):
...
	def counts(self,reverse=False):
		return _sorted_keys(self,xrange(len(self)),reverse)

DSU在 Python 2.4中非常重要,前面5.2节和5.3节已经介绍过了,列表的 sort 方法和新的内建的 sorted 函数提供了一个快速的、原生的 DSU 实现。因此,在 Python2.4中_sorted_keys 还可以变得更简单快速:

def _sorted_keys(container,keys,reverse):
	return sorted(keys,key=container,__getitem__,reverse=reverse)

被绑定的 container.__getitem__方法和 Python2.3中实现的获取索引的操作 container[k]所做的事情完全一样,但是对于我们正在排序的序列而言,它是一个可调用体,可以应用于序列中的每个元素,即命名的键,因此我们可以将它传递给内建sorted 函数的作为 key 关键字参数的值。Python 2.4还提供了一个简单直接的方法来获取字典元素根据值排序后的列表:

from operator import itemgetter
def dict_items_sorted_by_value(d,reverse=False):
	return sorted(d,iteritems(),key=itemgetter(1),reverse=reverse)

如果想排序一个元素为子容器的容器,Python 2.4新出现的高级函数 operator.itermgette是一个很方便的提供 key 参数的方法,它可以针对每个子容器的特定元素建立键。这正是我们想要的,因为字典的条目实际上就是一个键和值构成的对(两元素的元组)的序列,所谓根据对应值排序,就是根据每个元组的第二个元素进行排序。回到本节的主题,下面是本节解决方案中的 hist类的一个使用示例:

sentence = '''Hello there this is a test. Hello there this was a test, but now it is not.'
words = sentence.split()
c = hist()
for word in words:c.add(word)
print("Ascending count:")
print c.counts()
print "Descending count:"
print c.counts(reverse = True)

上述代码片段产生了如下的输出:

Ascending count:
[(1,'but'),(1,'it'),(1,'not.'),(1,'now'),(1,'test,'),(1,'test.'),(1,'was'),(2,'He1lo'),(2,'a'),(2,'is'),(2,'there'),(2,'this')]
Descending count:
[(2,'this'),(2,'there'),(2,'is'),(2,'a'),(2,'Hello'),(1,'was'),(1,'test.'),(1,'test,'),(1,'now'),(1,'not.'),(1,'it'),(1,'but')]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我不会编程555

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值