设为首页 收藏本站
查看: 2577|回复: 0

[经验分享] Python:使用Counter进行计数统计及collections模块

[复制链接]

尚未签到

发表于 2018-8-5 11:07:58 | 显示全部楼层 |阅读模式
  计数统计就是统计某一项出现的次数。实际应用中很多需求需要用到这个模型。比如测试样本中某一指出现的次数、日志分析中某一消息出现的频率等等‘这种类似的需求有很多实现方法。下面就列举几条。
  (1)使用dict
  看下面代码
#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
  代码如下:
#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类的话,也很简单,代码如下:
#coding=utf-8  
from collections import Counter
  
data = ['a','2',2,4,5,'2','b',4,7,'a',5,'d','a','z']
  
c = Counter(data)
  
print c
  输出结果如下:
  Counter({'a': 3, 4: 2, 5: 2, '2': 2, 2: 1, 'b': 1, 7: 1, 'z': 1, 'd': 1})
  咱们接着看代码
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']
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'就被排除掉了。
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()在原基础上增加了计数值
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()在原基础上减少计数值
print c.most_common()  输出结果如下:
  [('a', 2), (4, 2), (5, 2), ('2', 2), (2, 1), ('b', 1), (7, 1), ('d', 1), ('z', 0)]
  以上代码都是连接在一起的。
  (3)算术和集合操作
#coding=utf-8  
from collections import Counter
  
data = ['a','2','2','b','a','d','a',]
  
c = Counter(data)
  
b = 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对象也适用。如下:
#coding=utf-8  
from collections import Counter
  
data = ['a','2',2,4,5,'2','b',4,7,'a',5,'d','a','z']
  
c = 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是为了高效实现插入和删除操作的双向列表,适合用于队列和栈,它最大的好处就是实现了从队列 头部快速增加和取出对象。
  双端队列的创建很简单,如下:
from collections import deque  
q = deque(['a', 'b', 'c'])
  双端队列的主要方法如下:
  append():在右边加入一个元素
  appendleft():在左边加入一个元素
  clear():情况双端队列,使其长度为0
  count():统计某个元素出现的次数
  extend():扩展队列,接受一个可迭代对象参数
  extendleft():也是扩展队列,也是接受一个可迭代对象参数,与extend()不同的是,先把可迭代对象翻转后在添加到列表前端
  pop():从deque的右端删除一个元素
  popleft():从deque的左端删除一个元素。
  remove():删除一个元素
  reverse():对deque对象反序
  rotate():将左端元素右移n个位置,如果是负数表示向左移。
  前面几个方法都比较简单,也比较好理解,主要是最后一个方法可能有点难理解,通过几个例子来说明。
#coding=utf-8  
from collections import deque
  
q = deque(['a', 'b', 'c'])
  
print q
  
q.rotate(2)
  
print q
  结果如下:
  deque(['a', 'b', 'c'])
  deque(['b', 'c', 'a'])
  它就相当于这三个元素组成了一个“闭环”,在“闭环”里移动。另外,通信和电子信息等专业,如果学过单片机,应该知道跑马灯,其实利用rotate()函数,我们也可以写个类似的“跑马灯"。代码如下:
#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类型的数据时尤其好用。看下面例子
#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],如果元素很多的时候操作起来就很麻烦。
#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关键字或者有重复元素名时,自动进行重命名。
  如下代码:
#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的子类,它记住了内容添加的顺序。看下面代码:
#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:
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)
  上面的代码不难理解,可以仔细理解下。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-546933-1-1.html 上篇帖子: python thrift 服务端与客户端使用 下篇帖子: Python学习笔记整理(十七)异常处理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表