攝影或3C

Python: Counter 教學; from collections import Counter

1. Counter 是什麼?

Counter 是 Python collections 模組提供的計數工具,主要用途是統計:

  • 字串中每個字元出現幾次;
  • 清單中每個元素出現幾次;
  • 不同類別的資料筆數;
  • 狀態、錯誤類型、FTP 類型的分布;
  • 多組資料的計數合併與比較。

它本質上是一個「元素 → 次數」的字典。

# %%
from collections import Counter

counter = Counter(["apple", "banana", "apple"])

print(counter)

輸出:

2. 建立 Counter

2.1 從 list 建立

from collections import Counter

items = ["active", "bypassed", "active", "unknown"]

status_counter = Counter(items)

print(status_counter)

輸出:

2.2 從字串建立

char_counter = Counter("banana")

print(char_counter)

字串會被視為一個可迭代物件,因此會逐字元統計。


2.3 從字典建立

counter = Counter({
    "active": 10,
    "bypassed": 4,
})

print(counter)

2.4 建立空的 Counter

counter = Counter()

counter["active"] += 1
counter["bypassed"] += 1

print(counter)

輸出:

這比一般字典方便,因為不存在的 key 預設次數是 0

一般字典則會發生 KeyError

3. 增加計數

3.1 使用 update

counter = Counter()

counter.update(["active", "active", "bypassed"])

print(counter)

也可以分批增加:

3.2 直接使用索引累加

counter = Counter()

counter["active"] += 1
counter["active"] += 1
counter["bypassed"] += 1

print(counter)

3.3 從另一個 Counter 增加

counter_a = Counter(active=3, bypassed=1)
counter_b = Counter(active=2, unknown=4)

counter_a.update(counter_b)

print(counter_a)

4. 取得計數

4.1 取得特定元素次數

counter = Counter(["active", "active", "bypassed"])

print(counter["active"])
print(counter["bypassed"])
print(counter["missing"])

4.2 使用 get

Counter 通常不需要特別使用 get
因為不存在的 key 已經會回傳 0
但若要明確表達預設值,仍可使用。


4.3 取得所有元素

假設:

counter = Counter(active=2, bypassed=1)

elements() 會依次數展開元素。

注意:次數為零或負數的項目不會被 elements() 輸出。


5. 取得最多項目

5.1 使用 most_common

counter = Counter({
    "active": 12,
    "bypassed": 5,
    "unknown": 2,
})

print(counter.most_common())

5.2 只取得前幾名

這適合用於:

  • 最常見的錯誤類型;
  • 出現次數最多的 Class Name;
  • 最常見的 Family;
  • 最常見的 FTP 類型。

6. 轉換成一般字典

counter = Counter(active=3, bypassed=1)

data = dict(counter)

print(data)

輸出 JSON 時通常也可以直接使用:

import json

json_text = json.dumps(counter, ensure_ascii=False)

但建議轉成一般字典,讓資料結構更明確:

json_text = json.dumps(
    dict(counter),
    ensure_ascii=False,
    indent=2,
)

7. 計數排序

most_common() 會由大到小排序。

如果要由小到大排序:

counter = Counter(active=10, bypassed=3, unknown=1)

ascending = sorted(
    counter.items(),
    key=lambda item: item[1],
)

print(ascending)

如果要依名稱排序:

by_name = sorted(counter.items())

print(by_name)

sorted(counter.items()) vs sorted(counter)

8. Counter 的加減

8.1 Counter 相加

counter_a = Counter(active=3, bypassed=1)
counter_b = Counter(active=2, unknown=4)

result = counter_a + counter_b

print(result)

8.2 Counter 相減

counter_a = Counter(active=5, bypassed=3)
counter_b = Counter(active=2, bypassed=1)

result = counter_a - counter_b

print(result)

要注意,使用 - 時,結果中的零與負數通常會被移除。

8.3 保留負數

如果需要保留負數,使用 subtract

counter_a = Counter(active=2)
counter_b = Counter(active=5)

counter_a.subtract(counter_b)

print(counter_a)

9. Counter 的集合運算

9.1 取最大值:|

counter_a = Counter(active=5, bypassed=2)
counter_b = Counter(active=3, unknown=4)

result = counter_a | counter_b

print(result)

每個 key 取兩邊較大的次數:

9.2 取最小值:&

每個 key 取兩邊較小的次數:

Counter 與 defaultdict 的差異

Counter

適合:

# 只需要統計每個 key 出現幾次
counter = Counter()
counter["active"] += 1

defaultdict

適合:

# 每個 key 需要保存一組複雜資料
from collections import defaultdict

data = defaultdict(list)

data["active"].append(node)

簡單比較:

如果需要同時保存數量與明細,可以同時使用:

from collections import Counter, defaultdict

counts = Counter()
details = defaultdict(list)

for node in nodes:
    reason = node["invalid_reason"]

    counts[reason] += 1
    details[reason].append(node)

12. 常見陷阱

12.1 Counter 不會自動刪除零值

如需移除零值:

counter += Counter()
# 
counter = Counter({
    key: value
    for key, value in counter.items()
    if value > 0
})

不要把 len(counter) 當成總筆數

這代表不同元素種類數,不是總筆數。

總筆數應使用:

若 Python 版本不支援 total(),可使用:

Counter 的 key 必須可雜湊

以下可以作為 key:

"active"
123
("FamilyA", "MP")

以下通常不行:

["active"]
{"status": "active"}

因為 list 與 dict 不可作為字典 key。

總結

Counter 最適合解決「某些資料各出現幾次」的問題。
最重要的使用原則是:

Counter 用來保存數量,
dict / defaultdict 用來保存明細。
兩者搭配使用,就能同時支援統計、報表、追蹤與下游查詢。

推薦hahow線上學習python: https://igrape.net/30afN

儲蓄保險王

儲蓄險是板主最喜愛的儲蓄工具,最喜愛的投資理財工具則是ETF,最喜愛的省錢工具則是信用卡