查看全课大纲(第 51 / 51 节)
  1. 1.Python 是什么?为什么值得学?
  2. 2.如何为 Python 编程准备一个“全家桶”环境?
  3. 3.Python 有哪几种执行方法?
  4. 4.行与缩进到底有什么用?
  5. 5.注释有什么用,怎么写?
  6. 6.变量:如何给数据起个好名字?
  7. 7.运算符的优先级和常见坑点是什么?
  8. 8.Python 中,数字和布尔值究竟有哪几种?
  9. 9.输入函数 `input()` 和文件读取 `open()` 怎么用?
  10. 10.如何将程序的结果展示给用户或保存下来?
  11. 11.CSV 文件怎么读怎么写才方便?
  12. 12.列表是什么,如何创建和访问它?
  13. 13.列表的修改操作,有哪些方法?
  14. 14.列表切片为什么能取到部分元素?排序和查找又该怎么用?
  15. 15.判断语句:如何让程序根据条件做不同的事?
  16. 16.for 循环如何遍历序列中的元素?
  17. 17.while 循环什么时候才会停?
  18. 18.while 循环的结束条件,你写对了吗?
  19. 19.循环中途想提前结束,怎么办?
  20. 20.`break` 和 `continue` 到底有什么区别?
  21. 21.列表推导式如何让代码更简洁高效?
  22. 22.字符串怎么定义、拼接和操作?
  23. 23.元组:为什么定义单个元素时要加逗号?
  24. 24.集合(set)怎么用?如何快速去重和做集合运算?
  25. 25.字典是什么,为什么比列表更方便?
  26. 26.字典的增删改查与遍历,如何避免 KeyError?
  27. 27.函数里的变量,为什么有的能用有的不能用?
  28. 28.在函数内部修改全局变量,为什么有时不生效?
  29. 29.函数里能修改全局变量吗?返回值怎么用?
  30. 30.匿名函数:如何用 lambda 快速定义简单函数?
  31. 31.面向对象编程是什么,为什么需要它?
  32. 32.类中的方法和属性到底怎么定义和使用?
  33. 33.如何控制对对象内部属性和方法的访问?
  34. 34.异常发生时,程序会怎样继续执行?
  35. 35.如何组织代码:包和模块
  36. 36.`if __name__ == '__main__'` 到底有什么用?
  37. 37.如何用 IDE 的 Debug 功能排查程序错误?
  38. 38.Matplotlib 绘图的第一步该做什么?
  39. 39.Figure 是什么,如何设置图形尺寸?
  40. 40.如何自定义 Matplotlib 的坐标轴?
  41. 41.如何为图表添加图例,让不同曲线一目了然?
  42. 42.饼图怎么画才能直观展示数据占比?
  43. 43.如何用 Matplotlib 绘制散点图?
  44. 44.柱状图怎么画?
  45. 45.如何使用 Matplotlib 绘制等高线图?
  46. 46.如何在 Matplotlib 中绘制子图?
  47. 47.zip 函数如何把多个列表“打包”在一起?
  48. 48.如何给可迭代对象加上索引?
  49. 49.random 模块如何生成随机数?
  50. 50.math 模块怎么用?
  51. 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] += 1KeyError: 'red'普通字典中不存在的键不能直接累加改用 Counter(),或先判断 if word in cnt
以为 c - d 会保留负值结果为负的键在结果里消失了Counter 的减法只保留结果大于 0 的项需要保留负值时手动逐键相减
直接 print(set(c)) 并依赖输出顺序每次运行顺序可能不同集合无序,set(c) 只保留键且不保证顺序print(sorted(set(c))) 或改用 most_common()

小结

  1. collections.Counter 是字典的子类,元素作 key、出现次数作 value,适合快速计数。
  2. 可以直接用列表、字符串、元组、集合、字典或关键字参数构造 Counter。
  3. 用 c[key] 访问计数,不存在的键返回 0,不会抛 KeyError。
  4. most_common(n) 按计数从大到小返回前 n 项;keys()、values()、items() 等字典方法都可直接使用。
  5. 支持 +、-、&、| 四种运算;减法只保留正值,与空 Counter 相加可过滤非正数项。
  6. 转成 list 或 set 只保留键,转成 dict 才保留完整计数;集合无序,打印前应 sorted()。

📋 行动清单

  • 用普通字典写一遍计数循环,再换成 Counter 重写,比较两种写法的代码量。
  • 构造两个 Counter,分别做加、减、交、并,观察每一项的结果。
  • 用 most_common(3) 统计一段文字中出现最多的 3 个单词。

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 第6章代码包:CollectionsCounter_样例代码(含1 个 Notebook)
    ZIP · 1KB
    下载