【Python】详解 collections 模块之 Counter 类

目录

一、绪论

二、Counter 类

2.1 创建 Counter 对象

2.2 数值访问(键-值索引)

2.3 元素删除 (键-值对删除)

2.4 update() / subtract() —— 元素更新 (键-值对增减)

2.5 elements() —— 元素全部列出

2.6 most_common() —— 元素按出现频数从大到小列出

2.7 算术与集合运算

2.8 常用操作范例


一、绪论

collections 作为 Python 的内建集合模块,实现了许多十分高效的特殊容器数据类型,即除了 Python 通用内置容器: dict、list、set 和 tuple 等的替代方案。在 IDLE 输入 help(collections) 可查看帮助文档,其中常见的类/函数如下:

名称 功能
namedtuple 用于创建具有命名字段的 tuple 子类的 factory 函数 (命名元组)
deque 类似 list 的容器,两端都能实现快速 append 和 pop (双端队列)
ChainMap 类似 dict 的类,用于创建多个映射的单视图
Counter 用于计算 hashable 对象的 dict 子类 (可哈希对象计数)
OrderedDict 记住元素添加顺序的 dict 子类 (有序字典)
defaultdict dict 子类调用 factory 函数来提供缺失值
UserDict 包装 dict 对象以便于 dict 的子类化
UserList 包装 list 对象以便于 list 的子类化
UserString 包装 string 对象以便于 string 的子类化

而本文详述的对象为 Counter 类。


二、Counter 类

Counter,顾名思义是一个计数器。Counter 类作为一个无序的容器类型,以字典的 key-value 对形式存储元素,旨在统计各元素 (哈希项) 出现的次数。具体而言,key 表示元素,value 表示各元素 key 出现的次数,可为任意整数 (即包括0与负数)。Counter 类有时被称为 bags 或 multisets 。我们在 IDLE 通过 help(collections.Counter) 可查看帮助文档。

在 Python 3.7 版更新后,作为 dict 的子类,Counter 继承了记住插入顺序的功能。 Counter 对象进行数学运算时同样会保持顺序。 结果会先按每个元素在运算符左边的出现时间排序,然后再按其在运算符右边的出现时间排序。

2.1 创建 Counter 对象

实例化 Counter 的常见方式为:

## 导入 Counter 类
>>> from collections import Counter  # class collections.Counter([iterable-or-mapping])

>>> hashmap1 = Counter()  # a new, empty counter
>>> hashmap1
Counter()

## 通过可迭代对象、映射和关键字参数等实例化 Counter 对象
>>> hashmap2 = Counter('happy')  # a new counter from an iterable
>>> hashmap2
Counter({'p': 2, 'h': 1, 'a': 1, 'y': 1})

>>> hashmap3 = Counter(['A', 'A', 'K', 4, 7, 7])  # a new counter from an iterable
>>> hashmap3
Counter({'A': 2, 'K': 1, 4: 1, 7: 2})

>>> hashmap4 = Counter(('M', 'M', 4, 'A', 1, 1))  # a new counter from an iterable
>>> hashmap4
Counter({'M': 2, 4: 1, 'A': 1, 1: 2})

>>> hashmap5 = Counter({'x':1, 'y':2})  # a new counter from a mapping
>>> hashmap5
Counter({'y': 2, 'x': 1})

>>> hashmap6 = Counter(kangkang=1, daming=2, sam=3)  # a new counter from keyword args
>>> hashmap6
Counter({'sam': 3, 'daming': 2, 'kangkang': 1})

# 类型检查
>>> type(hashmap1), type(hashmap2), type(hashmap3), type(hashmap4), type(hashmap5), type(hashmap6)
(<class 'collections.Counter'>, <class 'collections.Counter'>, <class 'collections.Counter'>, <class 'collections.Counter'>, <class 'collections.Counter'>), <class 'collections.Counter'>)

可见元素从一个 iterable 被计数或从其他的 mapping (or counter) 实现初始化。

2.2 数值访问(键-值索引)

Counter 对象类似于 dict 接口,也通过 key 索引访问 value,但注意返回的 value 表示的是 key 的计数值

>>> hashmap2['p']  # hashmap2 = Counter({'p': 2, 'h': 1, 'a': 1, 'y': 1})
2

>>> hashmap3['A']  # hashmap3 = Counter({'A': 2, 'K': 1, 4: 1, 7: 2})
2

>>> hashmap4['M']  # hashmap4 = Counter({'M': 2, 4: 1, 'A': 1, 1: 2})
2

>>> hashmap5['x']  # hashmap5 = Counter({'y': 2, 'x': 1})
1

当访问的 key 不存在时,返回 0 而非 KeyError:

# count of a missing element is zero
>>> hashmap2[6]  # hashmap2 = Counter({'p': 2, 'h': 1, 'a': 1, 'y': 1})
0
>>> hashmap3[6]  # hashmap3 = Counter({'A': 2, 7: 2, 'K': 1, 4: 1})
0
>>> hashmap4[6]  # hashmap4 = Counter({'M': 2, 1: 2, 4: 1, 'A': 1})
0
>>> hashmap5[6]  # hashmap5 = Counter({'y': 2, 'x': 1})
0

作为 dict 的子类,通过 Counter.keys()、Counter.values()、Counter.items() 访问键-值对仍然适用:

# 以 hashmap2 为例
>>> hashmap2 = Counter('happy')
>>> hashmap2 
Counter({'p': 2, 'h': 1, 'a': 1, 'y': 1})

>>> hashmap2.keys()
dict_keys(['h', 'a', 'p', 'y'])
>>> type(hashmap2.keys())
<class 'dict_keys'>

>>> hashmap2.values()
dict_values([1, 1, 2, 1])
>>> type(hashmap2.values())
<class 'dict_values'>

>>> hashmap2.items()
dict_items([('h', 1), ('a', 1), ('p', 2), ('y', 1)])
>>> type(hashmap2.items())
<class 'dict_items'>

注意,原地操作如 hashmap[key] += 1,值类型只支持加和减,故分数、小数、十进制甚至负值都适用。

2.3 元素删除 (键-值对删除)

对于 Counter 对象的元素删除应使用内置函数 del,而令 value=0 是不能删除元素(键-值对)的。

# 以 hashmap2 为例
>>> hashmap2 = Counter('happy')
>>> hashmap2 
Counter({'p': 2, 'h': 1, 'a': 1, 'y': 1})

>>> hashmap2['y'] = 0
>>> hashmap2
Counter({'p': 2, 'h': 1, 'a': 1, 'y': 0})  # 'y' 还在, 只是 value=0 而已

>>> del hashmap2['y']
>>> hashmap2
Counter({'p': 2, 'h': 1, 'a': 1})  # 'y' 被删除了

2.4 update() / subtract() —— 元素更新 (键-值对增减)

可以使用另外的 iterable 对象或 Counter 对象实现对原 Counter 对象的元素更新 (键-值对增减)。

其中,元素增加使用 Counter.update(iterable-or-mapping) 方法:

## 以 hashmap2 为例, 其余同理
>>> hashmap2 = Counter('happy')
>>> hashmap2 
Counter({'p': 2, 'h': 1, 'a': 1, 'y': 1})

## 用 iterable 对象更新
>>> hashmap2.update('hp')  # 用 string 更新 (iterable 对象)
>>> hashmap2
Counter({'p': 3, 'h': 2, 'a': 1, 'y': 1})  
 
>>> hashmap2.update(['a','y'])  # 用 list 更新 (iterable 对象)
>>> hashmap2
Counter({'p': 3, 'h': 2, 'a': 2, 'y': 2})

>>> hashmap2.update(('a'))  # 用 tuple 更新 (iterable 对象)
>>> hashmap2
Counter({'a': 3, 'p': 3, 'h': 2, 'y': 2})

## 用 mapping 对象更新
>>> hashmap2.update({'h':1, 'y':1})  # 用 dict 更新 (mapping 对象)
>>> hashmap2
Counter({'h': 3, 'a': 3, 'p': 3, 'y': 3})

## 用 Counter 对象更新
>>> hashmap2.update(Counter('hapy'))
>>> hashmap2
Counter({'h': 4, 'a': 4, 'p': 4, 'y': 4})

注意,元素的添加来自 iterable 对象计数元素,或另一个 mapping 对象 (或 Counter 对象) ,其中 iterable 对象应为序列 sequence 元素。

同理,元素减少使用 Counter.subtract(iterable-or-mapping) 方法,但注意元素的计数 —— value 为 0 和 负数都是允许的

# 以 hashmap2 为例, 其余同理
>>> hashmap2 = Counter('happy')
>>> hashmap2 
Counter({'p': 2, 'h': 1, 'a': 1, 'y': 1})

# 用 iterable 对象更新元素
>>> hashmap2.subtract('p')  # 用 string 更新
>>> hashmap2
Counter({'h': 1, 'a': 1, 'p': 1, 'y': 1}) 

>>> hashmap2.subtract(['p'])  # 用 list 更新
>>> hashmap2
Counter({'h': 1, 'a': 1, 'p': 0, 'y': 1})  # 允许 value <= 0

>>> hashmap2.subtract(('p'))  # 用 tuple 更新
>>> hashmap2
Counter({'h': 1, 'a': 1, 'y': 1, 'p': -1})  # 允许 value <= 0

## 用 mapping 对象更新
>>> hashmap2.subtract({'h':2})  # 用 dict 更新
>>> hashmap2
Counter({'a': 1, 'y': 1, 'h': -1, 'p': -1})  # 允许 value <= 0

## 用 Counter 对象更新
>>> hashmap2.update(Counter('ay'))
>>> hashmap2
Counter({'a': 0, 'y': 0, 'h': -1, 'p': -1})

可见使用 iterable 对象更新原 Counter 对象之前,都会被隐式地转换为 Counter 对象,再把元素更新到原 Counter 对象上。

2.5 elements() —— 元素全部列出

Counter.elements() 方法将返回一个迭代器。元素的计数有多少,在该迭代器中就包含多少个该元素。元素排列无确定顺序 元素按首次出现的顺序返回,且不含 value<= 0 的元素

>>> hashmap7 = Counter('happy')
>>> hashmap7
Counter({'p': 2, 'h': 1, 'a': 1, 'y': 1})

>>> hashmap7.elements()  # 返回迭代器
<itertools.chain object at 0x00000249719226A0>

>>> list(hashmap7.elements())  # 显式类型转换
['a', 'a']

>>> tuple(hashmap7.elements())  # 显式类型转换
('a', 'a')

>>> str(hashmap7.elements())  # 那是不可能的...
'<itertools.chain object at 0x0000024971125390>'

>>> sorted(hashmap7.elements())  # 隐式类型转换
['a', 'a']

2.6 most_common() —— 元素按出现频数从大到小列出

Counter.most_common([n]) 方法将返回一个 list,其中包含前 n 个出现频数最高的元素及其出现次数,并从大到小排列。 若 n 被省略或为 None,most_common()  将返回 Counter 对象中的所有元素。若元素计数值相等,则按首次出现的顺序排序:

>>> hashmap8 = Counter(x=3, y=2, z=1, h=2)
>>> hashmap8
Counter({'x': 3, 'y': 2, 'h': 2, 'z': 1})

>>> hashmap8.most_common(1)  # n=1
[('x', 3)]
>>> hashmap8.most_common(2)  # n=2
[('x', 3), ('y', 2)]
>>> hashmap8.most_common(3)  # n=3
[('x', 3), ('y', 2), ('h', 2)]
>>> hashmap8.most_common(4)  # n=4
[('x', 3), ('y', 2), ('h', 2), ('z', 1)]  # 'y' 与 'h' 出现频次均为 2, 但 'y' 先出现, 故前排

>>> hashmap8.most_common()  # n ignored
[('x', 3), ('y', 2), ('h', 2), ('z', 1)]  # 全排:从大到小
>>> hashmap8.most_common()[::-1]  # n ignored
[('z', 1), ('h', 2), ('y', 2), ('x', 3)]  # 全排:从小到大

2.7 算术与集合运算

有几个常用的数学运算操作:

>>> h1 = Counter(x=1, y=2)
>>> h2 = Counter(x=2, y=1)
>>> h3 = Counter(x=1, y=-1)

>>> h1 + h2
Counter({'x': 3, 'y': 3})  # 按元素相加
>>> h2 + h3
Counter({'x': 3})  # value <= 0 的会被删除

>>> h1 - h2
Counter({'y': 1})  # 按元素相减
>>> h1 - h3
Counter({'y': 3})  # value <= 0 的会被删除

>>> h1 & h2
Counter({'x': 1, 'y': 1})  # 按元素取 min() (交集)

>>> h1 | h2
Counter({'x': 2, 'y': 2})  # 按元素取 max() (并集)

        还有单目加和减 (一元操作符) 及其等价形式:

>>> hashmap9 = Counter(a=2, b=1, c=0, d=-1)

>>> +hashmap9
Counter({'a': 2, 'b': 1})  # 去除 value<=0 的元素

>>> -hashmap9
Counter({'d': 1})  # 去除 value>=0 的元素

>>> hashmap9 += Counter()  
>>> hashmap9
Counter({'a': 2, 'b': 1})  # 去除 value<=0 的元素 

2.8 常用操作范例

>>> hashmap8 = Counter(x=3, y=2, z=1, h=2)
>>> hashmap8
Counter({'x': 3, 'y': 2, 'h': 2, 'z': 1})

## 对 Counter 对象的 value 求和
>>> sum(hashmap8.values())  
8

## 从小到大排序
>>> hashmap8.most_common()[::-1]
[('z', 1), ('h', 2), ('y', 2), ('x', 3)]  

## 类型转换
>>> list(hashmap8)
['x', 'y', 'z', 'h']  # 将 Counter 对象的 key 转换为 list
>>> tuple(hashmap8)
('x', 'y', 'z', 'h')  # 将 Counter 对象的 key 转换为 tuple
>>> set(hashmap8)
{'z', 'y', 'x', 'h'}  # 将 Counter 对象的 key 转换为 set
>>> dict(hashmap8)
{'x': 3, 'y': 2, 'z': 1, 'h': 2}  # 将 Counter 对象的转换为 dict

## 去除 value <= 0 的元素
>>> hashmap9 = Counter(a=2, b=1, c=0, d=-1)
>>> hashmap9
Counter({'a': 2, 'b': 1, 'c': 0, 'd': -1})

>>> hashmap9 += Counter()  
>>> hashmap9
Counter({'a': 2, 'b': 1})

参考文献

https://docs.python.org/zh-cn/3/library/collections.html#collections.Counter

https://www.liaoxuefeng.com/wiki/1016959663602400/1017681679479008

发布了14 篇原创文章 · 获赞 12 · 访问量 720

猜你喜欢

转载自blog.csdn.net/qq_39478403/article/details/105318754