← 返回题目列表

Python 的 Counter 是什么?怎么统计出现次数、取 Top N?

高频 中等 第 7 / 21 题 更新于 2026/07/31
PythonCountercollections计数

简化版

**collections.Counter 是 dict 的子类,专门用来「计数」——传入一个可迭代对象,它自动统计每个元素出现的次数,结果是 {元素: 次数} 的字典。**核心用法:① Counter(iterable) 一行统计(Counter("mississippi"){'i':4,'s':4,'p':2,'m':1});② c.most_common(n) 取出现次数最多的前 n 个(c.most_common(2)[('i',4),('s',4)]),不传 n 则返回全部按频率降序;③ 访问不存在的键返回 0(不报 KeyError、也不插入键,比 defaultdict(int) 安全);④ 支持「计数运算」:c1 + c2(次数相加)、c1 - c2(相减、负数丢弃)、c1 & c2(取交集最小值)、c1 | c2(取并集最大值)。还能手动计数c.update(iterable)(累加)、c[key] += 1Counter(a=3, b=1)(直接指定)。典型场景:词频统计、找最高频元素、字符/元素计数、多集合(multiset)运算。核心记忆:Counter(iterable) 一行统计次数,most_common(n) 取 Top N,缺键返回 0,支持 +/-/&/| 计数运算,是计数的首选工具。

详细版

Counter 的核心 API

操作说明
Counter(iterable)统计每个元素次数
Counter(a=3, b=1) / Counter({...})直接指定次数
c[key]取次数(缺键返回 0,不插入)
c.most_common(n)前 n 个高频(不传返回全部降序)
c.update(iterable)累加计数
c.subtract(iterable)减少计数(可为负)
c.elements()按次数展开元素(迭代器)
c1 + c2 / - / & / ``
from collections import Counter

# 一行统计
c = Counter("mississippi")
print(c)                # Counter({'i':4, 's':4, 'p':2, 'm':1})

# 取 Top N
print(c.most_common(2)) # [('i', 4), ('s', 4)]
print(c.most_common())  # 全部,按频率降序

# 缺键返回 0(安全,不报错不插入)
print(c["z"])           # 0
print("z" in c)         # False(没插入)

# 手动计数
c2 = Counter()
for word in ["a", "b", "a"]:
    c2[word] += 1       # {'a':2, 'b':1}
c2.update(["a", "c"])   # 累加 → {'a':3, 'b':1, 'c':1}

# 计数运算(多集合 multiset)
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2, c=1)
print(c1 + c2)          # Counter({'a':4, 'b':3, 'c':1})  相加
print(c1 - c2)          # Counter({'a':2})  相减(≤0 丢弃)
print(c1 & c2)          # Counter({'a':1, 'b':1})  交集(取 min)
print(c1 | c2)          # Counter({'a':3, 'b':2, 'c':1})  并集(取 max)

# elements:按次数展开
print(list(Counter(a=2, b=1).elements()))   # ['a', 'a', 'b']

⚠️ Counter 是「计数的瑞士军刀」,它比 defaultdict(int) 更专业、也更安全——关键差别在于「访问不存在的键」:defaultdict(int)[k] 会返回 0 但同时把 k 插入字典(副作用),而 Counter[k] 只返回 0、不插入,所以用 Counter 做「查询/判断某元素次数」不会污染数据。除了统计,Counter 最强大的是把计数当成「多重集合(multiset)」来做集合运算:+ 是次数相加、- 是相减(结果 ≤0 的元素被丢弃)、& 取每个元素次数的最小值(交集)、| 取最大值(并集)——这在「合并多份统计」「求两个词袋的公共部分」等场景一步到位。两个易错点:① most_common()次数相同的元素,顺序是「首次出现的顺序」(Python 3.7+ 保证);② c.total()(3.10+)返回所有计数之和,旧版本用 sum(c.values())

完整版教学

一、Counter 是什么

先理解 Counter 的定位:

collections.Counter:dict 的子类,专为"计数"设计
  键 = 元素、值 = 该元素出现的次数

创建方式(四种):
  ① Counter(iterable):统计可迭代对象里每个元素次数
     Counter("aabbc") → {'a':2,'b':2,'c':1}
     Counter([1,1,2])  → {1:2, 2:1}
  ② Counter(映射):从字典建
     Counter({'a':3, 'b':1})
  ③ Counter(键=次数):关键字参数
     Counter(a=3, b=1)
  ④ Counter():空的,之后手动累加

本质是 dict:
  isinstance(c, dict) → True
  支持所有 dict 操作(items、keys、in、update...)
  但 update 的语义不同(累加而非覆盖,见后)

和普通 dict 的关键差别:
  访问缺键返回 0(不是 KeyError、不插入键)
  → Counter 天生适合"这个元素出现几次"的查询

定位:
  需要"统计次数、找高频、多集合运算" → Counter
  (比手写循环计数、比 defaultdict(int) 都更专业)

所以 Counter 是 dict 子类专为计数,键=元素/值=次数,缺键返回 0

collections.Counter 是 dict 的子类、专为「计数」设计(键=元素、值=出现次数)。创建方式(四种):① Counter(iterable)(统计每个元素次数,Counter("aabbc"){'a':2,'b':2,'c':1})、② Counter(映射)(从字典建)、③ Counter(键=次数)(关键字参数 Counter(a=3, b=1))、④ Counter()(空的、之后手动累加)。本质是 dictisinstance(c, dict) 为 True、支持所有 dict 操作、但 update 语义不同是累加)。和普通 dict 的关键差别访问缺键返回 0(不是 KeyError、不插入键),天生适合「这个元素出现几次」的查询。理解「Counter 是 dict 子类专为计数(键=元素/值=次数);四种创建(iterable/映射/键=次数/空);本质是 dict;关键差别缺键返回 0 不报错不插入」,就理解了 Counter 是什么。

二、统计与 most_common

理解 Counter 最核心的统计和取 Top N:

统计:Counter(iterable) 一行搞定
  c = Counter(words)          # 词频
  c = Counter(text)           # 字符频率
  c = Counter(len(w) for w in words)  # 长度分布

most_common(n):取出现次数最多的前 n 个
  c.most_common(3)   → [(元素,次数), ...] 前 3 个,降序
  c.most_common()    → 全部,按次数降序排列
  c.most_common()[-1] → 次数最少的(末尾)
  c.most_common()[:-4:-1] → 次数最少的 3 个(反向切片)

  返回的是 (元素, 次数) 元组的列表

相同次数的顺序(易错点):
  次数相同的元素,按"首次遇到的顺序"排列(Python 3.7+)
  Counter("bbaacc").most_common()
  → [('b',2), ('a',2), ('c',2)]  # b 先出现排前

取所有次数之和:
  c.total()          # Python 3.10+
  sum(c.values())    # 通用写法

典型场景:
  找出现最多的单词:Counter(words).most_common(1)[0]
  Top 10 高频词:Counter(words).most_common(10)
  找唯一/最少:most_common()[-1]

所以 Counter(iterable)统计、most_common(n)取 Top N(降序,同次数按首现顺序)

统计:Counter(iterable) 一行搞定(词频 Counter(words)、字符频率 Counter(text)、长度分布 Counter(len(w) for w in words))。most_common(n):取出现次数最多的前 n 个——c.most_common(3) 前 3 个降序、c.most_common() 全部按次数降序、c.most_common()[-1] 次数最少的,返回 (元素, 次数) 元组的列表。相同次数的顺序(易错点):次数相同的元素按「首次遇到的顺序」排列(Python 3.7+),Counter("bbaacc").most_common()[('b',2),('a',2),('c',2)](b 先出现排前)。取所有次数之和c.total()(3.10+)或 sum(c.values())(通用)。典型场景:找出现最多的单词 Counter(words).most_common(1)[0]、Top 10 高频词。理解「Counter(iterable)一行统计;most_common(n)取 Top N 降序(返回(元素,次数)元组列表);同次数按首现顺序(3.7+);次数和用 total()或 sum(values())」,就掌握了统计与 most_common。

三、手动计数与 update/subtract

理解手动增减计数的方法:

手动累加/修改计数:

① 下标累加(缺键从 0 起):
   c = Counter()
   c["a"] += 1        # a: 0→1(缺键当 0)
   c["a"] += 1        # a: 1→2

② update(iterable/映射):累加(不是覆盖!)
   c = Counter(a=1)
   c.update(["a", "b"])       # a:1→2, b:0→1
   c.update({"a": 10})        # a:2→12(累加,不是设为 10)
   c.update(Counter(c=5))     # 合并另一个 Counter
   ⚠️ 和 dict.update(覆盖)不同:Counter.update 是"加"

③ subtract(iterable/映射):减少(可为负)
   c = Counter(a=3)
   c.subtract(["a","a","a","a"])   # a: 3-4 = -1(可为负!)
   → subtract 允许负值,- 运算符则丢弃 ≤0

④ 直接删/设:
   c["x"] = 0         # 设为 0(但键还在!c 里仍有 x:0)
   del c["x"]         # 真正删除键

清理 0 和负数:
   +c                 # 一元 +:只保留 >0 的项(去掉 0 和负)
   Counter(a=2, b=0, c=-1)  →  +c  →  Counter({'a':2})

所以手动:c[k]+=1、update 累加(非覆盖)、subtract 减(可负)、+c 去 0/负

手动累加/修改计数① 下标累加c["a"] += 1,缺键从 0 起);update(iterable/映射):累加(不是覆盖!)c.update(["a","b"]) 各 +1、c.update({"a": 10}) 是 a 累加 10 不是设为 10、c.update(Counter(...)) 合并;⚠️ 和 dict.update(覆盖)不同,Counter.update 是「加」);subtract(iterable/映射):减少(可为负)c.subtract([...]) 允许负值,而 - 运算符丢弃 ≤0);④ 直接删/设c["x"] = 0 设为 0 但键还在、del c["x"] 真正删除)。清理 0 和负数+c(一元 +,只保留 >0 的项,去掉 0 和负)。理解「手动:c[k]+=1 缺键从 0、update 累加(非覆盖,和 dict.update 不同)、subtract 减少(可为负,-运算符丢弃≤0)、del 才真删、+c 去 0/负」,就掌握了手动计数。

四、计数运算——多重集合

理解 Counter 的四则/集合运算:

Counter 支持"多重集合(multiset)"运算:

+ 相加(次数相加):
  Counter(a=3,b=1) + Counter(a=1,b=2)
  → Counter({'a':4, 'b':3})
  用途:合并多份统计(多个文件的词频合起来)

- 相减(次数相减,结果 ≤0 丢弃):
  Counter(a=3,b=1) - Counter(a=1,b=2)
  → Counter({'a':2})   # b: 1-2=-1 丢弃、a: 3-1=2
  ⚠️ 与 subtract 不同:- 丢负数、subtract 保留负数

& 交集(每个元素取 min):
  Counter(a=3,b=1) & Counter(a=1,b=2)
  → Counter({'a':1, 'b':1})   # min(3,1)=1, min(1,2)=1
  用途:两个词袋的"公共部分"

| 并集(每个元素取 max):
  Counter(a=3,b=1) | Counter(a=1,b=2)
  → Counter({'a':3, 'b':2})   # max(3,1)=3, max(1,2)=2

一元运算:
  +c   保留 >0 的项(去掉 0 和负)
  -c   把所有计数取负后保留 >0(相当于取原来的负数项)

运算结果都丢弃 ≤0(+、-、&、| 结果里没有 0 或负值)
  → 除了 subtract/直接赋值 会留下 0 和负

用途总结:
  合并统计 → +
  求差异   → -
  求公共   → &
  求合并(取多的)→ |

所以计数运算:+相加、-相减(丢负)、&交集取 min、|并集取 max(都丢弃≤0)

Counter 支持「多重集合(multiset)」运算+ 相加(次数相加)(合并多份统计);- 相减(结果 ≤0 丢弃)Counter(a=3,b=1) - Counter(a=1,b=2){'a':2},b 的 -1 被丢弃;⚠️ 与 subtract 不同,- 丢负数、subtract 保留);& 交集(每个元素取 min)(两个词袋的公共部分);| 并集(每个元素取 max)一元运算+c(保留 >0)、-c(取负后保留 >0)。运算结果都丢弃 ≤0。用途:合并统计用 +、求差异用 -、求公共用 &、求合并取多的用 |。理解「计数运算:+相加(合并统计)、-相减(丢≤0,和 subtract 不同)、&交集取 min(公共部分)、|并集取 max;都丢弃≤0;+c 保留>0」,就掌握了多重集合运算。

五、Counter vs defaultdict(int) vs dict.get

理解三种计数方案的区别:

三种计数方案对比:

① Counter(专业计数,首选):
   c = Counter(data)          # 一行统计
   c.most_common(n)           # Top N
   c["x"]                     # 缺键 0,不插入(安全)
   c1 + c2                    # 计数运算
   优点:功能全、缺键安全、API 专业
   缺点:是特殊类型(有的场景要转 dict)

② defaultdict(int)(通用计数):
   d = defaultdict(int)
   for x in data: d[x] += 1
   优点:灵活(计数+其他逻辑)
   缺点:无 most_common、读缺键会插入键、无计数运算

③ dict.get(原始):
   d = {}
   for x in data: d[x] = d.get(x, 0) + 1
   优点:普通 dict、无副作用
   缺点:最啰嗦、无高级功能

选择:
  纯计数、找高频、多集合运算 → Counter
  计数 + 复杂逻辑/自定义结构   → defaultdict(int)
  简单几行、不想引 import      → dict.get

缺键行为对比(重要):
  Counter["x"]        → 0,不插入(安全)
  defaultdict(int)["x"]→ 0,插入 x=0(副作用)
  dict.get("x", 0)    → 0,不插入
  d["x"](普通 dict) → KeyError

所以计数首选 Counter(功能全+缺键安全),复杂逻辑用 defaultdict(int)

三种计数方案对比① Counter(专业计数,首选)Counter(data) 一行统计、most_common(n) Top N、缺键 0 不插入(安全)、c1+c2 计数运算,功能全但是特殊类型;② defaultdict(int)(通用计数)for x in data: d[x] += 1,灵活(计数+其他逻辑)但无 most_common、读缺键会插入键、无计数运算;③ dict.get(原始)d[x] = d.get(x, 0) + 1,普通 dict 无副作用但最啰嗦无高级功能。选择:纯计数/找高频/多集合运算用 Counter、计数+复杂逻辑用 defaultdict(int)、简单几行用 dict.get。缺键行为对比(重要)Counter["x"]→0 不插入(安全)、defaultdict(int)["x"]→0 插入(副作用)、dict.get("x",0)→0 不插入、d["x"]→KeyError。理解「计数首选 Counter(功能全+缺键安全 most_common/运算)、defaultdict(int)通用(读缺键插入、无高级)、dict.get 原始;缺键 Counter/get 不插入、defaultdict 插入」,就掌握了三方案区别。

六、其他方法与总结

理解 elements 等方法并总结:

elements():按次数"展开"元素(迭代器)
  Counter(a=3, b=1).elements()  → 迭代 'a','a','a','b'
  list(...)  → ['a','a','a','b']
  → 计数的"逆操作"(还原成元素序列,忽略 ≤0)
  用途:需要按频率重复元素时

其他实用:
  c.total()          # 所有计数之和(3.10+)
  sum(c.values())    # 通用求和
  set(c)             # 所有不同元素
  dict(c)            # 转普通 dict
  c.most_common()[::-1]  # 升序(最少的在前)

坑与注意:
  ① update 是累加不是覆盖(和 dict.update 不同)
  ② -、&、| 结果丢弃 ≤0;subtract 保留负值
  ③ 相同次数按首次出现顺序(3.7+)
  ④ 是 dict 子类,序列化/某些库可能要 dict(c)

核心总结:
  Counter(iterable):一行统计次数
  most_common(n):取 Top N(降序,同次数按首现)
  缺键返回 0(不插入,安全)
  计数运算:+ 相加、- 相减、& 取 min、| 取 max
  手动:c[k]+=1、update(累加)、subtract(可负)
  计数首选(比 defaultdict(int)/dict.get 专业)

所以 Counter 计数首选:统计/most_common/缺键 0/计数运算/elements 展开

elements():按次数「展开」元素(迭代器)——Counter(a=3,b=1).elements() 迭代 ‘a’,‘a’,‘a’,‘b’(计数的逆操作、还原成元素序列、忽略 ≤0)。其他实用c.total()(3.10+)、sum(c.values())set(c)(所有不同元素)、dict(c)(转普通 dict)、most_common()[::-1](升序)。坑与注意:① update 是累加不是覆盖、② -/&/| 结果丢弃 ≤0 而 subtract 保留负值、③ 相同次数按首次出现顺序(3.7+)、④ 是 dict 子类某些库要 dict(c)。理解「elements()按次数展开(逆操作);total()/sum(values())求和;坑 update 累加、运算丢≤0、同次数按首现、序列化转 dict;Counter 计数首选」,就掌握了其他方法与总结。

记忆钩子:「collections.Counter 是 dict 的子类、专为计数设计(键=元素、值=出现次数);创建 Counter(iterable)一行统计(Counter(‘mississippi’)→{‘i’:4,‘s’:4,‘p’:2,‘m’:1})、Counter(a=3,b=1)直接指定;★核心 API:①most_common(n)取出现次数最多的前 n 个(返回(元素,次数)元组列表、降序,同次数按首次出现顺序 3.7+),不传返回全部②访问缺键返回 0(不报 KeyError、也不插入键,比 defaultdict(int)安全)③计数运算(多重集合 multiset):c1+c2 次数相加(合并统计)、c1-c2 相减(结果≤0 丢弃)、c1&c2 交集取 min(公共部分)、c1|c2 并集取 max④手动:c[k]+=1、update(累加不是覆盖!和 dict.update 不同)、subtract(减少可为负)、elements()按次数展开元素、total()/sum(values())求和;计数首选 Counter(比 defaultdict(int)专业且缺键不插入更安全,比 dict.get 功能全)」

七、常见误区与追问

  • 误区:Counter.update() 和 dict.update() 一样是覆盖。 不一样——dict.update 是「覆盖」(同键的值被新值替换),而 Counter.update 是「累加」(同键的计数相加):Counter(a=1).update({"a": 10}) 得到 a: 11(1+10)而不是 a: 10;这是 Counter 为计数场景专门定制的语义,用来合并多份统计;同理 Counter 有个 subtract 方法做「累减」。
  • 误区:访问 Counter 里不存在的键会报 KeyError 或插入键。 都不会——Counter 访问不存在的键返回 0(因为「没出现过就是 0 次」),既不抛 KeyError 也不把这个键插入字典;这点比 defaultdict(int) 更安全(后者读缺键会返回 0 但同时插入键、污染数据);所以用 Counter 查询「某元素出现几次」可以放心用 c[key]、不会有副作用。
  • 误区:most_common() 里次数相同的元素顺序是随机的。 不是随机——Python 3.7+ 保证:次数相同的元素按「首次出现(插入)的顺序」排列(因为 Counter 基于有序的 dict);Counter("bbaacc").most_common() 得到 [('b',2),('a',2),('c',2)],b 因为先出现所以排在同为 2 次的 a、c 前面;如果需要「同频按元素本身排序」等其他顺序,要自己再排序。
  • 误区:Counter 相减 c1 - c2c1.subtract(c2) 效果一样。 不一样——c1 - c2(减法运算符)会丢弃结果 ≤0 的元素(只保留正计数,符合「多重集合差」语义),且返回一个新 Counter、不改变 c1;c1.subtract(c2) 是原地累减、会保留负数计数(Counter(a=1).subtract({"a": 3}) 得到 a: -2)、且修改 c1 本身;需要「保留负值/记录差额」用 subtract,需要「多重集合减法/只要正的」用 -
  • 追问:怎么用 Counter 找出出现次数最多的前 3 个元素?次数最少的呢? 最多用 c.most_common(3),返回按次数降序的前 3 个 (元素, 次数) 元组列表,如 Counter(words).most_common(3);要「出现最多的那一个元素」用 c.most_common(1)[0][0];次数最少的可以用 c.most_common()[:-4:-1](反向切片取末尾 3 个)或先 c.most_common()[::-1][:3](整体升序后取前 3);注意 most_common 不传参数返回全部(按次数降序),相同次数按首次出现顺序排列。
  • 追问:Counter 的「计数运算」(+、-、&、|)分别是什么,有什么用? 把 Counter 当「多重集合(multiset)」运算:c1 + c2 是对应元素次数相加(用于合并多份统计,比如把多个文档的词频汇总);c1 - c2 是次数相减、结果 ≤0 的元素被丢弃(用于求「多出来的部分」);c1 & c2 是每个元素取两者次数的最小值(交集,用于求两个词袋的「公共部分」);c1 | c2 是每个元素取最大值(并集,合并时保留出现更多的一方);这四个运算结果都会自动丢弃计数 ≤0 的项,让你一步完成多重集合的合并、求差、求交、求并,比手写循环简洁得多。
  • 追问:Counter、defaultdict(int)、dict.get 都能计数,实际怎么选? 优先 Counter——它专为计数设计、功能最全(Counter(iterable) 一行统计、most_common 取 Top N、支持 +/-/&/| 多集合运算)、且访问缺键返回 0 不插入键(最安全);当你「计数的同时还要做其他复杂逻辑、或需要自定义默认结构(如按 key 建列表)」时用 defaultdict(int)(更灵活,但注意读缺键会插入键、没有 most_common);只是临时几行、不想 import 时可以用 d[x] = d.get(x, 0) + 1(普通 dict、无副作用但最啰嗦);一句话:纯计数和统计分析选 Counter,计数混合其他逻辑选 defaultdict,极简场景用 dict.get。

八、加强记忆

collections.Counter 是 dict 的子类、专为「计数」设计(键=元素、值=出现次数)。创建Counter(iterable) 一行统计(Counter("mississippi"){'i':4,'s':4,'p':2,'m':1})、Counter(a=3, b=1) 直接指定。核心 APImost_common(n) 取出现次数最多的前 n 个(返回 (元素, 次数) 元组列表、降序,同次数按首次出现顺序 3.7+),不传返回全部;② 访问缺键返回 0(不报 KeyError、也不插入键,比 defaultdict(int) 安全);③ 计数运算(多重集合)c1 + c2 次数相加(合并统计)、c1 - c2 相减(结果 ≤0 丢弃)、c1 & c2 交集取 min(公共部分)、c1 | c2 并集取 max;④ 手动c[k] += 1update(累加、不是覆盖,和 dict.update 不同)、subtract(减少、可为负)、elements()(按次数展开元素)、total()/sum(c.values()) 求和。计数首选 Counter(比 defaultdict(int) 专业且缺键不插入更安全、比 dict.get 功能全)。一句话「Counter(iterable)一行统计次数;most_common(n)取 Top N(降序、同次数按首现);缺键返回 0 不插入(安全);计数运算+相加/-相减/&取 min/|取 max;update 是累加不是覆盖;计数首选」。