查看全课大纲(第 51 / 51 节)
- 1.Python 是什么?为什么值得学?
- 2.如何为 Python 编程准备一个“全家桶”环境?
- 3.Python 有哪几种执行方法?
- 4.行与缩进到底有什么用?
- 5.注释有什么用,怎么写?
- 6.变量:如何给数据起个好名字?
- 7.运算符的优先级和常见坑点是什么?
- 8.Python 中,数字和布尔值究竟有哪几种?
- 9.输入函数 `input()` 和文件读取 `open()` 怎么用?
- 10.如何将程序的结果展示给用户或保存下来?
- 11.CSV 文件怎么读怎么写才方便?
- 12.列表是什么,如何创建和访问它?
- 13.列表的修改操作,有哪些方法?
- 14.列表切片为什么能取到部分元素?排序和查找又该怎么用?
- 15.判断语句:如何让程序根据条件做不同的事?
- 16.for 循环如何遍历序列中的元素?
- 17.while 循环什么时候才会停?
- 18.while 循环的结束条件,你写对了吗?
- 19.循环中途想提前结束,怎么办?
- 20.`break` 和 `continue` 到底有什么区别?
- 21.列表推导式如何让代码更简洁高效?
- 22.字符串怎么定义、拼接和操作?
- 23.元组:为什么定义单个元素时要加逗号?
- 24.集合(set)怎么用?如何快速去重和做集合运算?
- 25.字典是什么,为什么比列表更方便?
- 26.字典的增删改查与遍历,如何避免 KeyError?
- 27.函数里的变量,为什么有的能用有的不能用?
- 28.在函数内部修改全局变量,为什么有时不生效?
- 29.函数里能修改全局变量吗?返回值怎么用?
- 30.匿名函数:如何用 lambda 快速定义简单函数?
- 31.面向对象编程是什么,为什么需要它?
- 32.类中的方法和属性到底怎么定义和使用?
- 33.如何控制对对象内部属性和方法的访问?
- 34.异常发生时,程序会怎样继续执行?
- 35.如何组织代码:包和模块
- 36.`if __name__ == '__main__'` 到底有什么用?
- 37.如何用 IDE 的 Debug 功能排查程序错误?
- 38.Matplotlib 绘图的第一步该做什么?
- 39.Figure 是什么,如何设置图形尺寸?
- 40.如何自定义 Matplotlib 的坐标轴?
- 41.如何为图表添加图例,让不同曲线一目了然?
- 42.饼图怎么画才能直观展示数据占比?
- 43.如何用 Matplotlib 绘制散点图?
- 44.柱状图怎么画?
- 45.如何使用 Matplotlib 绘制等高线图?
- 46.如何在 Matplotlib 中绘制子图?
- 47.zip 函数如何把多个列表“打包”在一起?
- 48.如何给可迭代对象加上索引?
- 49.random 模块如何生成随机数?
- 50.math 模块怎么用?
- 51.Counter 如何快速统计元素出现次数?
Counter 如何快速统计元素出现次数?
约 18 分钟
小象实战讲义 · Python 编程从入门到实践
统计一个列表里每个元素出现了多少次,用普通字典要不停地判断键是否存在,代码又长又容易漏。collections 模块中的 Counter 专为计数而生:元素作键、出现次数作值,构造、访问、排序、合并都有现成方法。本节是第 6 章《一些常用的函数》的第 5 节,学完你能用它替代手写计数循环。
💡 核心导读
- 是什么:
Counter是字典的子类,元素作 key、出现次数作 value。 - 怎么创建:可以直接传入列表、字符串、元组、集合、字典,也可以用关键字参数。
- 常用操作:访问计数、删除元素、
most_common排序、转成列表/集合/字典。 - 集合运算:支持加、减、交集、并集四种运算。
- 注意点:计数值可以是 0 或负数,减法等运算会自动过滤掉非正数的项。
用 Counter 替代手写计数
Counter 是无序的容器类型,以字典的键值对方式存储;它继承自字典,所以字典的 keys()、values()、items() 等方法它都能用。
先用一个列表统计颜色出现次数,对比普通字典和 Counter 两种写法:
from collections import Counter
color_list = ['red', 'blue', 'red', 'green', 'blue', 'blue']
# 写法一:普通字典,每次累加前都要判断键是否存在
counts = {}
for color in color_list:
if color in counts:
counts[color] += 1
else:
counts[color] = 1
print(dict(counts))
# 写法二:Counter 无参构造后直接累加,不用判断
cnt = Counter()
for color in color_list:
cnt[color] += 1
print(dict(cnt)){'red': 2, 'blue': 3, 'green': 1}
{'red': 2, 'blue': 3, 'green': 1}两种写法结果一样:blue 出现 3 次、red 2 次、green 1 次。区别在于普通字典如果一上来就写 counts[color] += 1,会因为键不存在而抛 KeyError,必须先判断;Counter 访问不存在的键时返回 0,可以直接累加。
创建 Counter 的几种方式
Counter 的构造函数可以接收多种数据,最常见的是直接传入要统计的对象:
from collections import Counter
# 直接传入列表
print(dict(Counter(['red', 'blue', 'red', 'green', 'blue', 'blue'])))
# 传入字符串:统计每个字母出现的次数
print(dict(Counter('gallahad')))
# 传入字典:键是元素,值是初始计数
print(dict(Counter({'red': 4, 'blue': 2})))
# 使用关键字参数
print(dict(Counter(cats=4, dogs=8)))
# 传入由键值对组成的列表,先转成字典再交给 Counter
print(dict(Counter(dict([('x', 2), ('ham', 1)]))))
# 元组同样可以
print(dict(Counter(('a', 'b', 'a'))))
# 集合也可以,但集合本身会去重,所以每个元素只出现一次
print(sorted(Counter({'a', 'b'}).items())){'red': 2, 'blue': 3, 'green': 1}
{'g': 1, 'a': 3, 'l': 2, 'h': 1, 'd': 1}
{'red': 4, 'blue': 2}
{'cats': 4, 'dogs': 8}
{'x': 2, 'ham': 1}
{'a': 2, 'b': 1}
[('a', 1), ('b', 1)]可以看到,列表、字符串、元组、集合、字典都能作为构造参数。集合本身有去重功能,因此用它构造出来的 Counter,每个元素的计数都是 1;集合无序,所以这里用 sorted() 让输出稳定。
访问、删除与类型转换
Counter 用方括号加 key 访问计数。和普通字典不同的是,访问不存在的键不会报错,而是返回 0。del 可以删除某个键,clear() 可以清空整个对象。
from collections import Counter
c = Counter(['x', 'ham', 'x', 'apple'])
print(c['x']) # 存在的键,返回 2
print(c['bacon']) # 不存在的键,返回 0,不会 KeyError
del c['ham'] # 删除 ham 这一项
print(dict(c))
# 字典自带的方法都可以用
print(list(c.keys()))
print(list(c.values()))
print(list(c.items()))
print(sum(c.values())) # 所有计数之和
# 转成列表或集合时只保留键,计数值被丢弃
print(list(c))
print(sorted(set(c))) # set 无序,打印前先排序
print(dict(c)) # 转成字典保留完整内容
c.clear()
print(dict(c))2
0
{'x': 2, 'apple': 1}
['x', 'apple']
[2, 1]
[('x', 2), ('apple', 1)]
3
['x', 'apple']
['apple', 'x']
{'x': 2, 'apple': 1}
{}list(c) 与 set(c) 只拿到键,dict(c) 才能保留每个键对应的计数。因为集合无序,示例里用 sorted(set(c)) 打印,避免不同环境输出顺序不同。
most_common 与 Counter 之间的运算
most_common(n) 按计数从大到小排序,返回前 n 项组成的列表,每项是 (元素, 次数) 元组。计数值还可以手动改成 0 或负数;把 Counter 与一个空的 Counter 相加,能过滤掉所有非正数的项。
from collections import Counter
c = Counter(['x', 'ham', 'x', 'apple', 'ham', 'x'])
print(c.most_common(1))
print(c.most_common(2))
# 手动添加计数为 0 和负数的项
c['negative'] = -100
c['zero'] = 0
print(dict(c))
# 与空 Counter 相加,0 和负数项会被过滤掉
c += Counter()
print(dict(c))
# 两个 Counter 之间的加、减、交、并
c2 = Counter(a=3, b=1)
d2 = Counter(a=1, b=2)
print(dict(c2 + d2)) # 相加:对应计数相加
print(dict(c2 - d2)) # 相减:只保留结果大于 0 的项
print(dict(c2 & d2)) # 交集:取较小值
print(dict(c2 | d2)) # 并集:取较大值[('x', 3)]
[('x', 3), ('ham', 2)]
{'x': 3, 'ham': 2, 'apple': 1, 'negative': -100, 'zero': 0}
{'x': 3, 'ham': 2, 'apple': 1}
{'a': 4, 'b': 3}
{'a': 2}
{'a': 1, 'b': 1}
{'a': 3, 'b': 2}四种运算的规则:+ 把相同键的计数相加,- 相减后只保留正值,& 取两个计数中的较小值,| 取较大值。c2 - d2 的结果里没有 b,因为 1 - 2 = -1,负值被丢弃。
📝 动手练一练
统计下面这句话中每个单词出现的次数,并找出出现频率最高的两个单词:
the quick brown fox jumps over the lazy dog and the lazy dog is not quick
👉 点击查看参考答案
思路:用 split() 按空格切成单词列表,交给 Counter 统计,再用 most_common(2) 取前两名。
from collections import Counter
sentence = 'the quick brown fox jumps over the lazy dog and the lazy dog is not quick'
word_counts = Counter(sentence.split())
print(dict(word_counts))
print(word_counts.most_common(2)){'the': 3, 'quick': 2, 'brown': 1, 'fox': 1, 'jumps': 1, 'over': 1, 'lazy': 2, 'dog': 2, 'and': 1, 'is': 1, 'not': 1}
[('the', 3), ('quick', 2)]常见错误
| 错误写法 | 现象或报错 | 原因 | 正确写法 |
|---|---|---|---|
cnt = {} 后直接 cnt[word] += 1 | KeyError: 'red' | 普通字典中不存在的键不能直接累加 | 改用 Counter(),或先判断 if word in cnt |
以为 c - d 会保留负值 | 结果为负的键在结果里消失了 | Counter 的减法只保留结果大于 0 的项 | 需要保留负值时手动逐键相减 |
直接 print(set(c)) 并依赖输出顺序 | 每次运行顺序可能不同 | 集合无序,set(c) 只保留键且不保证顺序 | print(sorted(set(c))) 或改用 most_common() |
小结
collections.Counter是字典的子类,元素作 key、出现次数作 value,适合快速计数。- 可以直接用列表、字符串、元组、集合、字典或关键字参数构造
Counter。 - 用
c[key]访问计数,不存在的键返回 0,不会抛KeyError。 most_common(n)按计数从大到小返回前 n 项;keys()、values()、items()等字典方法都可直接使用。- 支持
+、-、&、|四种运算;减法只保留正值,与空Counter相加可过滤非正数项。 - 转成
list或set只保留键,转成dict才保留完整计数;集合无序,打印前应sorted()。
📋 行动清单
- 用普通字典写一遍计数循环,再换成
Counter重写,比较两种写法的代码量。 - 构造两个
Counter,分别做加、减、交、并,观察每一项的结果。 - 用
most_common(3)统计一段文字中出现最多的 3 个单词。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载第6章代码包:CollectionsCounter_样例代码(含1 个 Notebook)ZIP · 1KB