Python中Collections模块的Counter容器类使用教程


Posted in Python onMay 31, 2016

1.collections模块

collections模块自Python 2.4版本开始被引入,包含了dict、set、list、tuple以外的一些特殊的容器类型,分别是:

OrderedDict类:排序字典,是字典的子类。引入自2.7。
namedtuple()函数:命名元组,是一个工厂函数。引入自2.6。
Counter类:为hashable对象计数,是字典的子类。引入自2.7。
deque:双向队列。引入自2.4。
defaultdict:使用工厂函数创建字典,使不用考虑缺失的字典键。引入自2.5。
文档参见:http://docs.python.org/2/library/collections.html。

2.Counter类

Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key,其计数作为value。计数值可以是任意的Interger(包括0和负数)。Counter类和其他语言的bags或multisets很相似。

2.1 创建

下面的代码说明了Counter类创建的四种方法:

Counter类的创建Python

>>> c = Counter() # 创建一个空的Counter类
>>> c = Counter('gallahad') # 从一个可iterable对象(list、tuple、dict、字符串等)创建
>>> c = Counter({'a': 4, 'b': 2}) # 从一个字典对象创建
>>> c = Counter(a=4, b=2) # 从一组键值对创建

>>> c = Counter() # 创建一个空的Counter类
>>> c = Counter('gallahad') # 从一个可iterable对象(list、tuple、dict、字符串等)创建
>>> c = Counter({'a': 4, 'b': 2}) # 从一个字典对象创建
>>> c = Counter(a=4, b=2) # 从一组键值对创建
2.2 计数值的访问与缺失的键

当所访问的键不存在时,返回0,而不是KeyError;否则返回它的计数。

计数值的访问Python

>>> c = Counter("abcdefgab")
>>> c["a"]
2
>>> c["c"]
1
>>> c["h"]
0

>>> c = Counter("abcdefgab")
>>> c["a"]
2
>>> c["c"]
1
>>> c["h"]
0

2.3 计数器的更新(update和subtract)

可以使用一个iterable对象或者另一个Counter对象来更新键值。

计数器的更新包括增加和减少两种。其中,增加使用update()方法:

计数器的更新(update)Python

>>> c = Counter('which')
>>> c.update('witch') # 使用另一个iterable对象更新
>>> c['h']
3
>>> d = Counter('watch')
>>> c.update(d) # 使用另一个Counter对象更新
>>> c['h']
4

>>> c = Counter('which')
>>> c.update('witch') # 使用另一个iterable对象更新
>>> c['h']
3
>>> d = Counter('watch')
>>> c.update(d) # 使用另一个Counter对象更新
>>> c['h']
4

 
减少则使用subtract()方法:

计数器的更新(subtract)Python

>>> c = Counter('which')
>>> c.subtract('witch') # 使用另一个iterable对象更新
>>> c['h']
1
>>> d = Counter('watch')
>>> c.subtract(d) # 使用另一个Counter对象更新
>>> c['a']
-1

>>> c = Counter('which')
>>> c.subtract('witch') # 使用另一个iterable对象更新
>>> c['h']
1
>>> d = Counter('watch')
>>> c.subtract(d) # 使用另一个Counter对象更新
>>> c['a']
-1

2.4 键的删除

当计数值为0时,并不意味着元素被删除,删除元素应当使用del。

键的删除Python

>>> c = Counter("abcdcba")
>>> c
Counter({'a': 2, 'c': 2, 'b': 2, 'd': 1})
>>> c["b"] = 0
>>> c
Counter({'a': 2, 'c': 2, 'd': 1, 'b': 0})
>>> del c["a"]
>>> c
Counter({'c': 2, 'b': 2, 'd': 1})

>>> c = Counter("abcdcba")
>>> c
Counter({'a': 2, 'c': 2, 'b': 2, 'd': 1})
>>> c["b"] = 0
>>> c
Counter({'a': 2, 'c': 2, 'd': 1, 'b': 0})
>>> del c["a"]
>>> c
Counter({'c': 2, 'b': 2, 'd': 1})

 
2.5 elements()

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

elements()方法Python

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> list(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> list(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']

2.6 most_common([n])

返回一个TopN列表。如果n没有被指定,则返回所有元素。当多个元素计数值相同时,按照字母序排列。

most_common()方法Python

>>> c = Counter('abracadabra')
>>> c.most_common()
[('a', 5), ('r', 2), ('b', 2), ('c', 1), ('d', 1)]
>>> c.most_common(3)
[('a', 5), ('r', 2), ('b', 2)]

>>> c = Counter('abracadabra')
>>> c.most_common()
[('a', 5), ('r', 2), ('b', 2), ('c', 1), ('d', 1)]
>>> c.most_common(3)
[('a', 5), ('r', 2), ('b', 2)]

2.7 fromkeys

未实现的类方法。

2.8 浅拷贝copy

浅拷贝copyPython

>>> c = Counter("abcdcba")
>>> c
Counter({'a': 2, 'c': 2, 'b': 2, 'd': 1})
>>> d = c.copy()
>>> d
Counter({'a': 2, 'c': 2, 'b': 2, 'd': 1})

>>> c = Counter("abcdcba")
>>> c
Counter({'a': 2, 'c': 2, 'b': 2, 'd': 1})
>>> d = c.copy()
>>> d
Counter({'a': 2, 'c': 2, 'b': 2, 'd': 1})

2.9 算术和集合操作

+、-、&、|操作也可以用于Counter。其中&和|操作分别返回两个Counter对象各元素的最小值和最大值。需要注意的是,得到的Counter对象将删除小于1的元素。

Counter对象的算术和集合操作Python

>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d # c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d # subtract(只保留正数计数的元素)
Counter({'a': 2})
>>> c & d # 交集: min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d # 并集: max(c[x], d[x])
Counter({'a': 3, 'b': 2})

>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d # c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d # subtract(只保留正数计数的元素)
Counter({'a': 2})
>>> c & d # 交集: min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d # 并集: max(c[x], d[x])
Counter({'a': 3, 'b': 2})

3.常用操作

下面是一些Counter类的常用操作,来源于Python官方文档

Counter类常用操作Python

sum(c.values()) # 所有计数的总数
c.clear() # 重置Counter对象,注意不是删除
list(c) # 将c中的键转为列表
set(c) # 将c中的键转为set
dict(c) # 将c中的键值对转为字典
c.items() # 转为(elem, cnt)格式的列表
Counter(dict(list_of_pairs)) # 从(elem, cnt)格式的列表转换为Counter类对象
c.most_common()[:-n:-1] # 取出计数最少的n个元素
c += Counter() # 移除0和负值

sum(c.values()) # 所有计数的总数
c.clear() # 重置Counter对象,注意不是删除
list(c) # 将c中的键转为列表
set(c) # 将c中的键转为set
dict(c) # 将c中的键值对转为字典
c.items() # 转为(elem, cnt)格式的列表
Counter(dict(list_of_pairs)) # 从(elem, cnt)格式的列表转换为Counter类对象
c.most_common()[:-n:-1] # 取出计数最少的n个元素
c += Counter() # 移除0和负值

4.实例
4.1判断两个字符串是否由相同的字母集合调换顺序而成的(anagram)

def is_anagram(word1, word2):
  """Checks whether the words are anagrams.

  word1: string
  word2: string

  returns: boolean
  """

  return Counter(word1) == Counter(word2)

Counter如果传入的参数是字符串,就会统计字符串中每个字符出现的次数,如果两个字符串由相同的字母集合颠倒顺序而成,则它们Counter的结果应该是一样的。

4.2多元集合(MultiSets)
multiset是相同元素可以出现多次的集合,Counter可以非常自然地用来表示multiset。并且可以将Counter扩展,使之拥有set的一些操作如is_subset。

class Multiset(Counter):
  """A multiset is a set where elements can appear more than once."""

  def is_subset(self, other):
    """Checks whether self is a subset of other.

    other: Multiset

    returns: boolean
    """
    for char, count in self.items():
      if other[char] < count:
        return False
    return True

  # map the <= operator to is_subset
  __le__ = is_subset

4.3概率质量函数
概率质量函数(probability mass function,简写为pmf)是离散随机变量在各特定取值上的概率。可以利用Counter表示概率质量函数。

class Pmf(Counter):
  """A Counter with probabilities."""

  def normalize(self):
    """Normalizes the PMF so the probabilities add to 1."""
    total = float(sum(self.values()))
    for key in self:
      self[key] /= total

  def __add__(self, other):
    """Adds two distributions.

    The result is the distribution of sums of values from the
    two distributions.

    other: Pmf

    returns: new Pmf
    """
    pmf = Pmf()
    for key1, prob1 in self.items():
      for key2, prob2 in other.items():
        pmf[key1 + key2] += prob1 * prob2
    return pmf

  def __hash__(self):
    """Returns an integer hash value."""
    return id(self)

  def __eq__(self, other):
    return self is other

  def render(self):
    """Returns values and their probabilities, suitable for plotting."""
    return zip(*sorted(self.items()))

normalize: 归一化随机变量出现的概率,使它们之和为1
add: 返回的是两个随机变量分布两两组合之和的新的概率质量函数
render: 返回按值排序的(value, probability)的组合对,方便画图的时候使用
下面以骰子(ps: 这个竟然念tou子。。。)作为例子。

d6 = Pmf([1,2,3,4,5,6])
d6.normalize()
d6.name = 'one die'
print(d6)
Pmf({1: 0.16666666666666666, 2: 0.16666666666666666, 3: 0.16666666666666666, 4: 0.16666666666666666, 5: 0.16666666666666666, 6: 0.16666666666666666})

使用add,我们可以计算出两个骰子和的分布:

d6_twice = d6 + d6
d6_twice.name = 'two dices'

for key, prob in d6_twice.items():
  print(key, prob)

借助numpy.sum,我们可以直接计算三个骰子和的分布:

import numpy as np
d6_thrice = np.sum([d6]*3)
d6_thrice.name = 'three dices'

最后可以使用render返回结果,利用matplotlib把结果画图表示出来:

for die in [d6, d6_twice, d6_thrice]:
  xs, ys = die.render()
  pyplot.plot(xs, ys, label=die.name, linewidth=3, alpha=0.5)

pyplot.xlabel('Total')
pyplot.ylabel('Probability')
pyplot.legend()
pyplot.show()

结果如下:

Python中Collections模块的Counter容器类使用教程

4.4贝叶斯统计
我们继续用掷骰子的例子来说明用Counter如何实现贝叶斯统计。现在假设,一个盒子中有5种不同的骰子,分别是:4面、6面、8面、12面和20面的。假设我们随机从盒子中取出一个骰子,投出的骰子的点数为6。那么,取得那5个不同骰子的概率分别是多少?
(1)首先,我们需要生成每个骰子的概率质量函数:

def make_die(num_sides):
  die = Pmf(range(1, num_sides+1))
  die.name = 'd%d' % num_sides
  die.normalize()
  return die


dice = [make_die(x) for x in [4, 6, 8, 12, 20]]
print(dice)

(2)接下来,定义一个抽象类Suite。Suite是一个概率质量函数表示了一组假设(hypotheses)及其概率分布。Suite类包含一个bayesian_update函数,用来基于新的数据来更新假设(hypotheses)的概率。

class Suite(Pmf):
  """Map from hypothesis to probability."""

  def bayesian_update(self, data):
    """Performs a Bayesian update.

    Note: called bayesian_update to avoid overriding dict.update

    data: result of a die roll
    """
    for hypo in self:
      like = self.likelihood(data, hypo)
      self[hypo] *= like

    self.normalize()

其中的likelihood函数由各个类继承后,自己实现不同的计算方法。

(3)定义DiceSuite类,它继承了类Suite。

class DiceSuite(Suite):

  def likelihood(self, data, hypo):
    """Computes the likelihood of the data under the hypothesis.

    data: result of a die roll
    hypo: Die object
    """
    return hypo[data]

并且实现了likelihood函数,其中传入的两个参数为: data: 观察到的骰子掷出的点数,如本例中的6 hypo: 可能掷出的那个骰子

(4)将第一步创建的dice传给DiceSuite,然后根据给定的值,就可以得出相应的结果。

dice_suite = DiceSuite(dice)

dice_suite.bayesian_update(6)

for die, prob in sorted(dice_suite.items()):
  print die.name, prob

d4 0.0
d6 0.392156862745
d8 0.294117647059
d12 0.196078431373
d20 0.117647058824

正如,我们所期望的4个面的骰子的概率为0(因为4个面的点数只可能为0~4),而6个面的和8个面的概率最大。 现在,假设我们又掷了一次骰子,这次出现的点数是8,重新计算概率:

dice_suite.bayesian_update(8)

for die, prob in sorted(dice_suite.items()):
  print die.name, prob


d4 0.0
d6 0.0
d8 0.623268698061
d12 0.277008310249
d20 0.0997229916898

现在可以看到6个面的骰子也被排除了。8个面的骰子是最有可能的。
以上的几个例子,展示了Counter的用处。实际中,Counter的使用还比较少,如果能够恰当的使用起来将会带来非常多的方便。

Python 相关文章推荐
python 装饰器功能以及函数参数使用介绍
Jan 27 Python
Python中lambda的用法及其与def的区别解析
Jul 28 Python
Python中给List添加元素的4种方法分享
Nov 28 Python
python实现根据主机名字获得所有ip地址的方法
Jun 28 Python
Python时间获取及转换知识汇总
Jan 11 Python
python pandas库中DataFrame对行和列的操作实例讲解
Jun 09 Python
Python设计模式之模板方法模式实例详解
Jan 17 Python
Python中使用pypdf2合并、分割、加密pdf文件的代码详解
May 21 Python
python super的使用方法及实例详解
Sep 25 Python
django models里数据表插入数据id自增操作
Jul 15 Python
如何使用python写截屏小工具
Sep 29 Python
Python使用struct处理二进制(pack和unpack用法)
Nov 12 Python
Python的Django应用程序解决AJAX跨域访问问题的方法
May 31 #Python
python语言使用技巧分享
May 31 #Python
Windows中安装使用Virtualenv来创建独立Python环境
May 31 #Python
Windows下Python使用Pandas模块操作Excel文件的教程
May 31 #Python
深入理解python中的浅拷贝和深拷贝
May 30 #Python
浅谈Python的文件类型
May 30 #Python
python中string模块各属性以及函数的用法介绍
May 30 #Python
You might like
PHP新手上路(六)
2006/10/09 PHP
ThinkPHP php 框架学习笔记
2009/10/30 PHP
PHP mysql事务问题实例分析
2016/01/18 PHP
JavaScript起点(严格模式深度了解)
2013/01/28 Javascript
7款吸引人眼球的jQuery/CSS3特效实例分享
2013/04/25 Javascript
js读取注册表的键值示例
2013/09/25 Javascript
JS,Jquery获取select,dropdownlist,checkbox下拉列表框的值(示例代码)
2014/01/11 Javascript
JavaScript中用于生成随机数的Math.random()方法
2015/06/15 Javascript
js实现点击文本框显示日期选择器特效代码分享
2020/05/21 Javascript
jQuery EasyUI 布局之动态添加tabs标签页
2015/11/18 Javascript
jQuery得到多个值只能用取Class ,不能用取ID的方法
2016/12/04 Javascript
JavaScript切换搜索引擎的导航网页搜索框实例代码
2017/06/11 Javascript
nodejs多版本管理总结
2018/04/03 NodeJs
vue props传值失败 输出undefined的解决方法
2018/09/11 Javascript
node.js使用express框架进行文件上传详解
2019/03/03 Javascript
微信小程序实现页面跳转传递参数(实体,对象)
2019/08/12 Javascript
[04:22]DOTA2上海特级锦标赛主赛事第四日TOP10
2016/03/06 DOTA
[01:34]DAC2018主赛事第四日五佳镜头 Gh巨牙海民助Miracle-死里逃生
2018/04/07 DOTA
浅析Python的web.py框架中url的设定方法
2016/07/11 Python
Python中logging.NullHandler 的使用教程
2018/11/29 Python
Python文件路径名的操作方法
2019/10/30 Python
python return逻辑判断表达式实现解析
2019/12/02 Python
PYcharm 激活方法(推荐)
2020/03/23 Python
基于Python共轭梯度法与最速下降法之间的对比
2020/04/02 Python
python如何删除文件、目录
2020/06/23 Python
使用CSS3制作响应式导航菜单的方法
2015/07/12 HTML / CSS
小型女装店的创业计划书
2014/01/09 职场文书
公司门卫的岗位职责
2014/02/19 职场文书
入党积极分子批评与自我批评思想汇报
2014/09/14 职场文书
继承权公证书范本
2015/01/23 职场文书
预备党员考察表党小组意见
2015/06/01 职场文书
鸦片战争观后感
2015/06/09 职场文书
小学生暑假安全公约
2015/07/14 职场文书
2015国庆节放假通知范文
2015/07/30 职场文书
学习经验交流会演讲稿
2015/11/02 职场文书
小学生优秀作文范文(六篇)
2019/07/10 职场文书