在很多场景中经常会用到统计计数的需求,比如在实现 kNN 算法时统计 k 个标签值的个数,进而找出标签个数最多的标签值作为最终 kNN 算法的预测结果。Python内建的 collections 集合模块中的 Counter 类能够简洁、高效的实现统计计数。
Counter 是 dict 字典的子类,Counter 拥有类似字典的 key 键和 value 值,只不过 Counter 中的键为待计数的元素,而 value 值为对应元素出现的次数 count,为了方便介绍统一使用元素和 count 计数来表示。虽然 Counter 中的 count 表示的是计数,但是 Counter 允许 count 的值为 0 或者负值。
如果要使用 Counter,必须要进行实例化,在实例化的同时可以为构造函数传入参数来指定不同类型的元素来源。
from collections import Counter
# 实例化元素为空的 Counter 对象
a = Counter()
# 从可迭代对象中实例化 Counter 对象
b = Counter('chenkc')
# 从 mapping 中实例化 Counter 对象
c = Counter({'a':1, 'b':2, 'c':3})
# 从关键词参数中实例化 Counter 对象
d = Counter(a = 1, b = 2, c = 3)
from collections import Counter
# 实例化元素为空的 Counter
a = Counter()
# 为 Counter 添加元素以及对应的 count 计数
a['a'] = 1
a['b'] = 2
a['c'] = 3
>>> print(a)
Counter({'c': 3, 'b': 2, 'a': 1})
from collections import Counter
# 从可迭代对象中实例化 Counter
b = Counter("chenkc") # string
b2 = Counter(['c', 'h', 'e', 'n', 'k', 'c']) # list
b3 = Counter(('c', 'h', 'e', 'n', 'k', 'c')) # tuple
>>> print(b)
Counter({'c': 2, 'h': 1, 'e': 1, 'n': 1, 'k': 1})
>>> print(b2)
Counter({'c': 2, 'h': 1, 'e': 1, 'n': 1, 'k': 1})
>>> print(b3)
Counter({'c': 2, 'h': 1, 'e': 1, 'n': 1, 'k': 1})
(x, y)
的列表,字典也属于 mapping 类型的数据。from collections import Counter
# 从 mapping 中实例化 Counter 对象
c = Counter([('a', 1), ('b', 2), ('a', 3), ('c', 3)])
c2 = Counter({'a':1, 'b':2, 'a':3, 'c':3}) # 字典
>>> print(c)
Counter({('a', 1): 1, ('b', 2): 1, ('a', 3): 1, ('c', 3): 1})
>>> print(c2)
Counter({'a': 3, 'c': 3, 'b': 2})
虽然传入的 mapping 类型的数据是一样的,但是由于字典中的键是唯一的,因此如果字典中的键重复会保留最后一个。
dic = {'a':1, 'b':2, 'a':3, 'c':3}
>>> print(dic)
{'a': 3, 'b': 2, 'c': 3}
from collections import Counter
# 从关键词参数中实例化 Counter 对象
d = Counter(a = 1, b = 2, c = 3)
# d2 = Counter(a = 1, b = 2, a = 3, c = 3) # SyntaxError
>>> print(d)
Counter({'c': 3, 'b': 2, 'a': 1})
我们都知道在字典中查找不存在的键,程序会抛出 KyeError的异常,但是由于 Counter 用于统计计数,因此 Counter 不同于字典,**如果在 Counter 中查找一个不存在的元素,不会产生异常,而是会返回 0,这其实很好理解,Counter 计数将不存在元素的 count 值设置为 0 **。
from collections import Counter
c = Counter({'a':1, 'b':2, 'c':3})
>>> print(c['d']) # 查找键值为'd'对应的计数
0
>>> print(c)
Counter({'c': 3, 'b': 2, 'a': 1})
c['d']
表示的查找返回元素值为d
的 count 计数,而如果使用c['d'] = 0
则表示的是为 Counter 添加元素。
from collections import Counter
c = Counter({'a':1, 'b':2, 'c':3})
c['d'] = 4 # 为 Counter 添加元素
>>> print(c['d'])
4
>>> print(c)
Counter({'d': 4, 'c': 3, 'b': 2, 'a': 1})
实例化 Counter 类对象之后,就可以使用 Counter 对象中的方法。由于 Counter 类继承自 dict 类,所以 Counter 类可以使用 dict 类的方法。下面分别从 Counter 所特有的方法和一些字典的常规方法来介绍。
Counter 额外支持字典中没有的三个方法:elements()
、most_common([m])
以及subtract([iterable-or-mapping])
。
elements()
方法返回一个迭代器,可以通过 list 或者其它方法将迭代器中的元素输出,输出的结果为对应出现次数的元素。
from collections import Counter
c = Counter({'a':1, 'b':2, 'c':3})
c2 = Counter({'a':0, 'b':-1, 'c':3}) # 将出现次数设置为 0 和负值
>>> print(c.elements())
>>> print(list(c.elements()))
['a', 'b', 'b', 'c', 'c', 'c']
>>> print(c2.elements())
>>> print(list(c2.elements()))
['c', 'c', 'c']
在 Counter 中是允许计数为 0 或者负值的,不过通过上面代码可以看出 elements 函数没有将 0 和负值对应的元素值打印出来。
most_common([n])
是 Counter 最常用的方法,返回一个出现次数从大到小的前 n 个元素的列表。
from collections import Counter
c = Counter({'a':1, 'b':2, 'c':3})
>>> print(c.most_common()) # 默认参数
[('c', 3), ('b', 2), ('a', 1)]
>>> print(c.most_common(2)) # n = 2
[('c', 3), ('b', 2)]
>>> print(c.most_common(3)) # n = 3
[('c', 3), ('b', 2), ('a', 1)]
>>> print(c.most_common(-1)) # n = -1
[]
n
为可选参数,通过上面的代码可以总结出:
n
,默认返回所有;n
小于最长长度,则返回前n
个数;n
等于最长长度,则返回所有;n = -1
,则返回空;subtract([iterable_or_mapping])
方法其实就是将两个 Counter 对象中的元素对应的计数相减。
from collections import Counter
c = Counter({'a':1, 'b':2, 'c':3})
d = Counter({'a':1, 'b':3, 'c':2, 'd':2})
c.subtract(d)
>>> print(c)
Counter({'c': 1, 'a': 0, 'b': -1, 'd': -2})
其实就是两个 Counter 中的对应的元素的计数相减。当其中某个 Counter 中对应的元素不存在的时候,默认将其计数设置为 0,这也是为什么'd'
的计数为-2的原因。
一般常规的字典方法对 Counter 对象都是有效的,将这些字典方法作用到下面的 Counter 对象c
中,并绘制到下面的表格中。
from collections import Counter
c = Counter({'a':1, 'b':2, 'c':3, 'd':0, 'e':-1})
但是在 Counter 中有两个方法和字典中的使用有些区别:
from collections import Counter
c = Counter({'a':1, 'b':2, 'c':3, 'd':0, 'e':-1})
c.update({'a':2, 'd':2, 'e':1})
>>> print(c)
Counter({'a': 3, 'c': 3, 'b': 2, 'd': 2, 'e': 0})
对于 Counter 中的update
函数简单来说,就是增加对应元素的计数。
这里直接贴出集合运算符的代码示例。
>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d # add two counters together: c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d # subtract (keeping only positive counts)
Counter({'a': 2})
>>> c & d # intersection: min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d # union: max(c[x], d[x])
Counter({'a': 3, 'b': 2})
原文地址:
Python中的计数 - Counter类
References:
Python collections中的Counter作用以及源码分析