Python:使用Counter进行计数统计及collections模块

 计数统计就是统计某一项出现的次数。实际应用中很多需求需要用到这个模型。比如测试样本中某一指出现的次数、日志分析中某一消息出现的频率等等‘这种类似的需求有很多实现方法。下面就列举几条。

(1)使用dict

看下面代码

1
2
3
4
5
6
7
8
9
10
#coding=utf-8
data  =  [ 'a' , '2' , 2 , 4 , 5 , '2' , 'b' , 4 , 7 , 'a' , 5 , 'd' , 'a' , 'z' ]
count_frq  =  dict ()
for  one  in  data:
      if  one  in  count_frq:
           count_frq[one]  + =  1
      else :
           count_frq[one]  =  1
 
print  count_frq

输出结果如下:

{'a': 3, 2: 1, 'b': 1, 4: 2, 5: 2, 7: 1, '2': 2, 'z': 1, 'd': 1}

这种方法最简单,也是最容易想到的,鄙人这写这篇博文之前用的最多,不过以后应该不会用来,我们应该使代码更加Pythonic

(2)使用set和list

代码如下:

1
2
3
4
5
6
7
8
#coding=utf-8
data  =  [ 'a' , '2' , 2 , 4 , 5 , '2' , 'b' , 4 , 7 , 'a' , 5 , 'd' , 'a' , 'z' ]
data_set  =  set (data)
count_list  =  []
for  one  in  data_set:
      count_list.append((one,data.count(one)))
 
print  count_list

输出结果如下:

[('a', 3), (2, 1), ('b', 1), (4, 2), (5, 2), (7, 1), ('2', 2), ('z', 1), ('d', 1)]

    这里面利用了list的通用方法和集合(set)的特性,集合是一个无序不重复的元素集,而工厂函数set()可以将列表转换为一个无序不重复的元素集合。

以上方法都很简单,但不够Pythonic。下面来介绍collections中的Counter类。

(一)Counter类

    Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)支持集合操作+、-、&、|,其中&、|操作分别返回两个Counter对象各元素的最大值和最小值。

(1)Counter的初始化

跟平时自定义类的初始化方法差不多,如下:

c = Counter("hello world")#可迭代对象创建

c = Counter(h=1,l=3,o=2)#关键字创建

c = Counter({'h':1,'l':3,'o':2})#字典创建

c = Counter()#空Counter类

(2)Counter类常见方法

elements():返回一个迭代器。元素被重复了多少次,在该迭代器中就包含多少个该元素。所有元素按照字母序排序,个数小于1的元素不被包含。

update():用于统计对象元素的更新,原有的Counter计数器对象与新增元素的统计计数值相加而不是直接替换。

subtract():该方法用于计数器对象中元素统计值减少,输入输出的统计值书可以为0或者负数的。

most_common([n]):可以查找出前n个出现频率最高的元素以及它们对于的次数,也就是说频率搞的排在最前面。

copy():浅拷贝。关于浅拷贝,深拷贝可以参考上篇博文。http://11026142.blog.51cto.com/11016142/1851472

所以上面的例子用Counter类的话,也很简单,代码如下:

1
2
3
4
5
#coding=utf-8
from  collections  import  Counter
data  =  [ 'a' , '2' , 2 , 4 , 5 , '2' , 'b' , 4 , 7 , 'a' , 5 , 'd' , 'a' , 'z' ]
=  Counter(data)
print  c

输出结果如下:

Counter({'a': 3, 4: 2, 5: 2, '2': 2, 2: 1, 'b': 1, 7: 1, 'z': 1, 'd': 1})

咱们接着看代码

1
2
print  c.elements()
print  list (c.elements())

输出结果如下:

<itertools.chain object at 0x7f94b81683d0>

['a', 'a', 'a', 2, 'b', 4, 4, 5, 5, 7, '2', '2', 'z', 'd']

1
2
3
4
c[ 'z' - =  1
print  c
print  c.elements()
print  list (c.elements())

输出结果如下:

Counter({'a': 3, 4: 2, 5: 2, '2': 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 0})

<itertools.chain object at 0x7f0e928723d0>

['a', 'a', 'a', 2, 'b', 4, 4, 5, 5, 7, '2', '2', 'd']

元素’z'的统计值变为了0,然后进行elements()运算后,‘z'就被排除掉了。

1
2
c.update( "aaaa" )
print  c

输出结果:

Counter({'a': 7, 4: 2, 5: 2, '2': 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 0})

update()在原基础上增加了计数值

1
2
c.subtract( "aaaaa" )
print  c

输出结果如下:

Counter({'a': 2, 4: 2, 5: 2, '2': 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 0})

subtract()在原基础上减少计数值

1
print  c.most_common()

输出结果如下:

[('a', 2), (4, 2), (5, 2), ('2', 2), (2, 1), ('b', 1), (7, 1), ('d', 1), ('z', 0)]

以上代码都是连接在一起的。

(3)算术和集合操作

1
2
3
4
5
6
7
8
9
10
11
#coding=utf-8
from  collections  import  Counter
data  =  [ 'a' , '2' , '2' , 'b' , 'a' , 'd' , 'a' ,]
=  Counter(data)
=  Counter(a = 1 ,b = 2 )
print  c
print  b
print  b + c   # c[x] + d[x]
print  c - b    # subtract(只保留正数计数的元素)
print  c&b    # 交集:  min(c[x], d[x])
print  c|b     # 并集:  max(c[x], d[x])

输出结果如下:


Counter({'a': 3, '2': 2, 'b': 1, 'd': 1})

Counter({'b': 2, 'a': 1})

Counter({'a': 4, 'b': 3, '2': 2, 'd': 1})

Counter({'a': 2, '2': 2, 'd': 1})

Counter({'a': 1, 'b': 1})

Counter({'a': 3, '2': 2, 'b': 2, 'd': 1})


(4)其它

Counter类返回值跟字典很类似,所以字典类的方法对Counter对象也适用。如下:

1
2
3
4
5
6
7
8
9
10
11
#coding=utf-8
from  collections  import  Counter
data  =  [ 'a' , '2' , 2 , 4 , 5 , '2' , 'b' , 4 , 7 , 'a' , 5 , 'd' , 'a' , 'z' ]
=  Counter(data)
print  c.keys()
print  c.has_key( 'a' )
print  c.get( 'a' )
print  c.items()
print  c.values()
print  c.viewitems()
print  c.viewkeys()

输出如下:

['a', 2, 'b', 4, 5, 7, '2', 'z', 'd']

True

3

[('a', 3), (2, 1), ('b', 1), (4, 2), (5, 2), (7, 1), ('2', 2), ('z', 1), ('d', 1)]

[3, 1, 1, 2, 2, 1, 2, 1, 1]

dict_items([('a', 3), (2, 1), ('b', 1), (4, 2), (5, 2), (7, 1), ('2', 2), ('z', 1), ('d', 1)])

dict_keys(['a', 2, 'b', 4, 5, 7, '2', 'z', 'd'])

这只是其中一部分,其它的方法可以参考字典类的方法。

另外,Counter对象还支持工厂函数操作set()、list()、dict().


(二)collections模块中其它类/方法

常见的内置数据类型有列表、字典、集合、元组等等,collections模块,在此基础上定义了一些其它的数据类型,如果用的好的话,对提升代码运行效率还是有很大的帮助的,下面一一介绍。

1.deque

 deque其实是 double-ended queue 的缩写,翻译过来就是双端队列。与list相比, 使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低;deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈,它最大的好处就是实现了从队列 头部快速增加和取出对象。

双端队列的创建很简单,如下:

1
2
from  collections  import  deque
=  deque([ 'a' 'b' 'c' ])

双端队列的主要方法如下:

append():在右边加入一个元素

appendleft():在左边加入一个元素

clear():情况双端队列,使其长度为0

count():统计某个元素出现的次数

extend():扩展队列,接受一个可迭代对象参数

extendleft():也是扩展队列,也是接受一个可迭代对象参数,与extend()不同的是,先把可迭代对象翻转后在添加到列表前端

pop():从deque的右端删除一个元素

popleft():从deque的左端删除一个元素。

remove():删除一个元素

reverse():对deque对象反序

rotate():将左端元素右移n个位置,如果是负数表示向左移。

前面几个方法都比较简单,也比较好理解,主要是最后一个方法可能有点难理解,通过几个例子来说明。

1
2
3
4
5
6
#coding=utf-8
from  collections  import  deque
=  deque([ 'a' 'b' 'c' ])
print  q
q.rotate( 2 )
print  q

结果如下:

deque(['a', 'b', 'c'])

deque(['b', 'c', 'a'])

它就相当于这三个元素组成了一个“闭环”,在“闭环”里移动。另外,通信和电子信息等专业,如果学过单片机,应该知道跑马灯,其实利用rotate()函数,我们也可以写个类似的“跑马灯"。代码如下:

1
2
3
4
5
6
7
8
9
10
#coding=utf-8
import  sys
import  time
from  collections  import  deque
fancy_loading  =  deque( '>--------------------' )
while  True :
     print  '\r%s'  %  ''.join(fancy_loading),
     fancy_loading.rotate( 1 )
     sys.stdout.flush()
     time.sleep( 0.1 )

有兴趣的可以运行一下该代码看看效果,对理解这个函数会有一定的帮助。


2. namedtuple

 namedtuple正如其名字,给元组命名,术语就是命名元组。namedtuple主要用来产生可以使用名称来访问元素的数据对象,通常用来增强代码的可读性, 在访问一些tuple类型的数据时尤其好用。看下面例子

1
2
3
4
5
6
7
#coding=utf-8
Bob = ( 'bob' , 30 , 'male' )  
print  'Representation:' ,Bob  
Jane = ( 'Jane' , 29 , 'female' )  
print  'Field by index:' ,Jane[ 0 ]  
for  people  in  [Bob,Jane]:  
     print  "%s is %d years old %s"  %  people

Bob与Jane是元组,如果想获取就用索引,比如上面的Jane[0],如果元素很多的时候操作起来就很麻烦。

1
2
3
4
5
6
7
8
9
10
#coding=utf-8
import  collections  
Person  =  collections.namedtuple( 'Person' , 'name age gender' )  
print  'Type of Person:' type (Person)  
Bob  =  Person(name = 'Bob' , age = 30 , gender = 'male' )  
print  'Representation:' , Bob  
Jane  =  Person(name = 'Jane' , age = 29 , gender = 'female' )  
print  'Field by Name:' , Jane.name  
for  people  in  [Bob,Jane]:  
      print  "%s is %d years old %s"  %  people

 解释一下nametuple的几个参数:

     以Person = collections.namedtuple(‘Person’, 'name age gender’)为例,其中 ’Person’是这个namedtuple的名称,后面的’name age gender’这个字符串中三个用空格隔开的字符告诉我们,我们的这个namedtuple有三个元素,分别名为name, age和gender。也可以这样表示,用中括号或者小括号,Person = collections.namedtuple(‘Person’, ['name','age','gender’])或者Person =collections.namedtuple(‘Person’, ('name','age','gender’)),也就是说这个表达式是在定义一个nametuple型的Person类,它有三个属性,然后在创建它的时候可以通过Bob = Person(name=’Bob’, age=30, gender=’male’)这种方式,这类似于Python中类对象的使用。而且,我们也可以像访问类对象的属性那样使用Jane.name这种方式访问namedtuple的元素。

 其输出结果如下:

 Type of Person: <type 'type'>  

Representation: Person(name='Bob', age=30, gender='male')  

Field by Name: Jane  

Bob is 30 years old male  

Jane is 29 years old female  


  但是在使用namedtyuple的时候要注意其中的名称不能使用Python的关键字,如:class def等;而且也不能有重复的元素名称,比如:不能有两个’age age’。如果出现这些情况,程序会报错。但是,在实际使用的时候可能无法避免这种情况,比如:可能我们的元素名称是从数据库里读出来的记录,这样很难保 证一定不会出现Python关键字。这种情况下的解决办法是将namedtuple的重命名模式打开,这样如果遇到Python关键字或者有重复元素名时,自动进行重命名。

如下代码:

1
2
3
4
5
6
#coding=utf-8
import  collections
with_class = collections.namedtuple( 'Person' , 'name age class gender' ,rename = True )
print  with_class._fields
two_ages = collections.namedtuple( 'Person' , 'name age gender age' ,rename = True )
print  two_ages._fields

输出结果如下:

('name', 'age', '_2', 'gender')

('name', 'age', 'gender', '_3')

使用rename=True的方式打开重命名选项。可以看到第一个集合中的class被重命名为 ‘_2′ ; 第二个集合中重复的age被重命名为 ‘_3′,这是因为namedtuple在重命名的时候使用了下划线 _ 加元素所在索引数的方式进行重命名。

3.OrderedDict

    直译的话就是有序字典。dict这个数据结构由于hash的特性,是无序的,这在有的时候会带来一些麻烦,还好collections模块为我们提供了OrderedDict,当你要获得一个有序的字典对象时,可以用OrderedDict,它是dict的子类,它记住了内容添加的顺序。看下面代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#coding=utf-8
from  collections  import  OrderedDict
items  =  (
     ( 'A' 1 ),
     ( 'B' 2 ),
     ( 'C' 3 )
)
regular_dict  =  dict (items)
ordered_dict  =  OrderedDict(items)
print  'Regular Dict:'
for  k, v  in  regular_dict.items():
     print  k, v
print  'Ordered Dict:'
for  k, v  in  ordered_dict.items():
     print  k, v

输出结果如下:

Regular Dict:

A 1

C 3

B 2

Ordered Dict:

A 1

B 2

C 3

注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序。

OrderedDict可以实现一个FIFO(先进先出)的dict,当容量超出限制时,先删除最早添加的Key:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from  collections  import  OrderedDict
 
class  LastUpdatedOrderedDict(OrderedDict):
 
     def  __init__( self , capacity):
         super (LastUpdatedOrderedDict,  self ).__init__()
         self ._capacity  =  capacity
 
     def  __setitem__( self , key, value):
         containsKey  =  1  if  key  in  self  else  0
         if  len ( self -  containsKey > =  self ._capacity:
             last  =  self .popitem(last = False )
             print  'remove:' , last
         if  containsKey:
             del  self [key]
             print  'set:' , (key, value)
         else :
             print  'add:' , (key, value)
         OrderedDict.__setitem__( self , key, value)

上面的代码不难理解,可以仔细理解下。







转载自:http://11026142.blog.51cto.com/11016142/1851791

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值