首页 > Python资料 博客日记
详解Python中非常好用的计数器Counter
2024-08-16 15:00:04Python资料围观57次
详解Python中非常好用的计数器Counter
Counter简介
Counter是Python内置模块collections中的一个计数器工具,可以方便快捷地计数。
Counter是字典dict的子类,用于计数可哈希(hashable)对象。(Python中实现了魔法方法__hash__的对象是hashable对象,关于可哈希和不可哈希,可以自行搜索了解,后面有时间我可以再专门写文章详细介绍)
Counter是一个多项集,元素被存储为字典的键,元素的计数被存储为字典的值。计数可以是任何整数,包括零或负数。(甚至也可以是小数,不过本文不对此展开讨论。)
Counter计数器的优点:
-
用法简单:它可以通过一个可迭代对象(iterable)来初始化,用一个映射(mapping)对象(包括Counter本身)来初始化,用键值对来初始化,或者直接创建一个空的Counter实例。
-
访问不存在的元素不报错:Counter对象的接口类似于字典,不同的是,如果查询的键不在Counter中,它会返回0,而不是抛出KeyError异常。相当于对任意键都有一个默认值0,这可以在一些遍历搜索的代码中避免程序报错。(严格来说,不报错有利有弊,不过在计数这种场景利大于弊,程序设计团队肯定也权衡过。)
-
支持的方法很多:作为dict的子类,Counter继承了dict的大部分方法,此外还实现了一些非常便利的方法。Counter也可以转换成列表、集合、字典等Python内置数据类型,从而使用这些数据类型的方法,如切片操作等。
接下来,本文将详细对Counter的各种特性和功能进行逐一介绍。
计数器功能实现
假设要统计一个列表[1, 2, 3, 4, 5, 1, 2, 3]中各元素出现的次数,可以利用字典,用如下代码实现。
# coding=utf-8
nums = [1, 2, 3, 4, 5, 1, 2, 3]
result = dict()
for n in nums:
if n not in result:
result[n] = 1
else:
result[n] += 1
print(result)
Output:
{1: 2, 2: 2, 3: 2, 4: 1, 5: 1}
上面的代码中我们自己新建了一个字典,将待计数的列表nums中的元素作为字典的键,遍历nums,对每个元素进行计数。
Counter的计数功能就类似于上面的代码,创建一个Counter对象即可完成计数。
from collections import Counter
nums = [1, 2, 3, 4, 5, 1, 2, 3]
print(Counter(nums))
Output:
Counter({1: 2, 2: 2, 3: 2, 4: 1, 5: 1})
可以看到,Counter封装了计数功能,只需要把待计数的数据传给Counter计数器,它即可立即返回一个计数器对象,完成对元素的计数。
在需要计数时,可以直接调用Counter来使用,不用再自己写计数的代码。虽然自己写代码也并不复杂,但是更关键的是,Counter除了可以完成计数,还实现了一些非常好用的方法,方便我们对计数器进行更丰富的处理和解析。
Counter初始化的几种方式
# 创建一个空计数器,然后用字典的方式给计数器添加值
c = Counter()
c['ai'] = 100
print(c)
# 实例化时将可迭代对象传入Counter
c1 = Counter('pythoncode')
print(c1)
# 实例化时将映射对象传入Counter
c2 = Counter({'a': 5, 'b': 2, 'c': 1})
print(c2)
# 实例化时将键值对传入Counter
c3 = Counter(A=1, B=2, C=5, D=6, E=7)
print(c3)
Output:
Counter({'ai': 100})
Counter({'o': 2, 'p': 1, 'y': 1, 't': 1, 'h': 1, 'n': 1, 'c': 1, 'd': 1, 'e': 1})
Counter({'a': 5, 'b': 2, 'c': 1})
Counter({'E': 7, 'D': 6, 'C': 5, 'B': 2, 'A': 1})
在实例化Counter时,可以传入可迭代对象(如字符串、列表),映射类型对象(如字典),键值对等。
访问Counter中不存在的元素
如果访问字典中不存在的键,会报错KeyError。例如:
t_dict = {'A': 0, 'K': 3, 'Q': 3, 'J': 1}
print(t_dict['KING'])
Output:
Traceback (most recent call last):
File "..\counter_demo.py", line 36, in <module>
print(t_dict['KING'])
KeyError: 'KING'
在Counter中访问不存在的元素时,会返回0。(这相当于字典用setdefault(key, value)设置了默认值,通过魔法方法__missing__实现。)
t_counter = Counter({'A': 0, 'K': 3, 'Q': 3, 'J': 1})
print(t_counter['KING'])
Output:
0
对于计数器中不存在的元素,返回的计数值为0,这比较符合生活常识,比报错KeyError友好很多,在一些代码中也有好处。
此外,前面提到过,Counter中的计数还可以是负数,通常情况,计数不会有负数,不过负数也不是完全没有用处,对于一些特殊场景,如需要记录类似“欠账”的情况,负数也可以派上用场。
这里还有一个注意点,Counter中某元素计数为0和Counter中不存在某元素,返回值都为0,代码处理(如访问元素的计数值、执行自增操作等)没有差别,但本质上并不一样,一种是Counter存在该元素,一种是Counter中不存在该元素。
也就是说,如果把Counter中的一个元素的计数值改成0,并不代表从Counter中删除了该元素。
t_counter['J'] = 0
print(t_counter)
print('J' in t_counter)
print('KING' in t_counter)
Output:
Counter({'K': 3, 'Q': 3, 'A': 0, 'J': 0})
True
False
要将元素从Counter中删除,跟字典删除方法相同,使用del关键字。
del t_counter['J']
print(t_counter)
print('J' in t_counter)
Output:
Counter({'K': 3, 'Q': 3, 'A': 0})
False
Counter支持的方法
Counter继承了字典的大部分方法,可以直接使用,如常用的keys()、values()、items(),还有copy()、clear()、pop()等等,就不逐一例举了。
本章节主要介绍Counter支持的一些附加方法。
elements(): 返回一个迭代器,其中每个元素都重复计数值所指定的次数。相同的元素会集中在一起,不同元素会按首次出现的顺序排列,如果一个元素的计数值小于1,elements()会将其忽略。
c_counter = Counter(['red', 'pink', 'yellow', 'blue', 'yellow', 'blue', 'pink', 'blue'])
print(c_counter.elements())
print(list(c_counter.elements()))
Output:
<itertools.chain object at 0x000002569074B970>
['red', 'pink', 'pink', 'yellow', 'yellow', 'blue', 'blue', 'blue']
most_common([n]): 返回一个列表,包含计数值最大的n个元素及其计数,按计数值从高到低排序,如果计数值相等,按元素首次出现的顺序排列。如果n为None(不传值或传None),返回计数器中的所有元素。
c_counter = Counter(['red', 'pink', 'yellow', 'blue', 'yellow', 'blue', 'pink', 'blue'])
print(c_counter.most_common())
print(c_counter.most_common(2))
Output:
[('blue', 3), ('pink', 2), ('yellow', 2), ('red', 1)]
[('blue', 3), ('pink', 2)]
subtract([iterable-or-mapping]): 减去一个可迭代对象或映射对象(包含Counter)中的元素(个数)。具体执行时将每个元素的计数值减去传入元素的计数值,输入输出都可以是0或负数。如果是不存在的元素,用0减。结果中元素的顺序按计数值从高到低排序,如果计数值相等,按元素首次出现的顺序排列。(可以发现,从文章开头,除elements()外,都是按这种顺序排序,本文后面的结果也都是按这种顺序。)
c_counter = Counter(['red', 'pink', 'yellow', 'blue', 'yellow', 'blue', 'pink', 'blue'])
print(c_counter)
ls = ['blue', 'yellow']
c_counter.subtract(ls) # 减一个列表
print(c_counter)
dt = {'pink': 1, 'red': 1, 'green': 1}
c_counter.subtract(dt) # 减一个字典
print(c_counter)
c2_counter = Counter({'blue': 1, 'yellow': 2, 'green': 3})
c_counter.subtract(c2_counter) # 减另一个Counter
print(c_counter)
Output:
Counter({'blue': 3, 'pink': 2, 'yellow': 2, 'red': 1})
Counter({'pink': 2, 'blue': 2, 'red': 1, 'yellow': 1})
Counter({'blue': 2, 'pink': 1, 'yellow': 1, 'red': 0, 'green': -1})
Counter({'pink': 1, 'blue': 1, 'red': 0, 'yellow': -1, 'green': -4})
update([iterable-or-mapping]): 加上一个可迭代对象或映射对象(包含Counter)中的元素(个数)。具体执行时将每个元素的计数值加上传入元素的计数值,输入和输出都可以是0或负数。
c_counter.update(ls) # 加一个列表
print(c_counter)
c_counter.update(dt) # 加一个字典
print(c_counter)
c_counter.update(c2_counter) # 加另一个Counter
print(c_counter)
Output:
Counter({'blue': 2, 'pink': 1, 'red': 0, 'yellow': 0, 'green': -4})
Counter({'pink': 2, 'blue': 2, 'red': 1, 'yellow': 0, 'green': -3})
Counter({'blue': 3, 'pink': 2, 'yellow': 2, 'red': 1, 'green': 0})
Counter的subtract()和update()是一对互逆的方法,类似于字典的update(),不过Counter中是计数值的相加或相减,而非更新替换。
此外,可迭代对象应当是一个元素序列,不能是一个(key, value)对组成的序列,那样会将整个序列当成一个整体元素处理。
total(): 返回计数器中总的计数值,此方法从Python3.10开始支持,更早的版本不支持。
c_counter = Counter(['red', 'pink', 'yellow', 'blue', 'yellow', 'blue', 'pink', 'blue'])
print(c_counter)
print(c_counter.total())
Output:
Counter({'blue': 3, 'pink': 2, 'yellow': 2, 'red': 1})
8
Counter的算术运算
cnt1 = Counter({'red': 3, 'green': 2, 'blue': 1})
cnt2 = Counter({'red': 1, 'green': 2, 'blue': 3})
print(cnt1 + cnt2)
print(cnt1 - cnt2)
Output:
Counter({'red': 4, 'green': 4, 'blue': 4})
Counter({'red': 2})
两个Counter计数器相加或相减,会将计数器中各元素的计数值进行加减,得到一个新的Counter计数器。
结果中只保留计数大于0的元素,也就是说算术运算前的计数器中可以有0或负数,但结果中不会保留0和负数。
Counter的交并运算
cnt1 = Counter({'red': 3, 'green': 2, 'blue': 1})
cnt2 = Counter({'red': 1, 'green': 2, 'blue': 3})
print(cnt1 | cnt2) # 并集
print(cnt1 & cnt2) # 交集
Output:
Counter({'red': 3, 'blue': 3, 'green': 2})
Counter({'green': 2, 'red': 1, 'blue': 1})
Counter的并集运算使用逻辑或的符号 | ,交集使用逻辑与的符号 & 。这里要注意,用Python的关键字 or 和 and 计算的结果不一样,or 和 and 会依次判断两个Counter是否为True,做的是逻辑运算不是交并运算。
计算并集时,结果取相同元素在两个Counter中计数值较大的计数,计算交集时,结果取相同元素在两个Counter中计数值较小的计数。
Counter的比较运算
cnt3 = Counter({'red': 20, 'green': 10, 'blue': 0})
cnt4 = Counter({'red': 20, 'green': 10})
print(cnt3 == cnt4)
print(cnt4 < cnt3)
Output:
True
False
Counter之间可以进行比较运算,支持 ==, !=, <, <=, >, >= 。在比较时,如果不存在的元素,会用计数0进行比较,所以上面的cnt3和cnt4相等。对于小于,只要Counter中有一个元素的计数值小于另一个Counter,其他元素的计数值可以相等,此时会返回True。大于同理。
比较运算从Python3.10开始支持,更早的版本不支持。
参考文档:https://docs.python.org/zh-cn/3/library/collections.html#counter-objects
相关阅读:
📢欢迎 点赞👍 收藏⭐ 评论📝 关注❤ 如有错误敬请指正!
☟ 学Python,点击下方名片关注我。☟
标签:
相关文章
最新发布
- 【Python】selenium安装+Microsoft Edge驱动器下载配置流程
- Python 中自动打开网页并点击[自动化脚本],Selenium
- Anaconda基础使用
- 【Python】成功解决 TypeError: ‘<‘ not supported between instances of ‘str’ and ‘int’
- manim边学边做--三维的点和线
- CPython是最常用的Python解释器之一,也是Python官方实现。它是用C语言编写的,旨在提供一个高效且易于使用的Python解释器。
- Anaconda安装配置Jupyter(2024最新版)
- Python中读取Excel最快的几种方法!
- Python某城市美食商家爬虫数据可视化分析和推荐查询系统毕业设计论文开题报告
- 如何使用 Python 批量检测和转换 JSONL 文件编码为 UTF-8
点击排行
- 版本匹配指南:Numpy版本和Python版本的对应关系
- 版本匹配指南:PyTorch版本、torchvision 版本和Python版本的对应关系
- Python 可视化 web 神器:streamlit、Gradio、dash、nicegui;低代码 Python Web 框架:PyWebIO
- 相关性分析——Pearson相关系数+热力图(附data和Python完整代码)
- Python与PyTorch的版本对应
- Anaconda版本和Python版本对应关系(持续更新...)
- Python pyinstaller打包exe最完整教程
- Could not build wheels for llama-cpp-python, which is required to install pyproject.toml-based proj