Chuyển đến nội dung chính

Đếm trong Python: Mẫu hiện đại, cạm bẫy, và ví dụ thực tế

Đếm trong Python nhanh, xác thực dữ liệu, và tránh lỗi thường gặp với Counter, count(), và pandas
· 4 phút đọc

Khám phá cùng AI

Việc đếm nghe có vẻ đơn giản cho đến khi kết quả sai lệch đôi chút—hoặc rất nhiều. Tôi từng thấy người ta kiểm tra tần suất bằng cách gọi lặp đi lặp lại list.count() hoặc dùng sai các phương thức của pandas vốn âm thầm bỏ qua giá trị thiếu. Hệ quả: mã chậm và con số sai. Cách khắc phục là dùng đúng công cụ: collections.Counter cho bản đồ tần suất, count() tích hợp sẵn cho truy vấn nhanh một giá trị, và value_counts() của pandas khi bạn làm việc trong DataFrame.

Counter là gì và hoạt động ra sao

collections.Counter là một lớp con của dictionary dùng để cộng dồn các đối tượng băm được (một multiset). Bạn có thể tạo Counter từ một iterable hoặc một mapping. Khóa thiếu mặc định là 0; số đếm có thể tăng hoặc giảm; và một số phương thức tiện lợi giúp thao tác thường gặp trở nên đơn giản.

from collections import Counter

# From an iterable (one pass over the data)
nyc_eatery_types = [
    "Mobile Food Truck", "Food Cart", "Snack Bar", "Restaurant", "Food Cart",
    "Restaurant", "Mobile Food Truck", "Snack Bar", "Mobile Food Truck"
]
eatery_type_counts = Counter(nyc_eatery_types)
print(eatery_type_counts)  # dict-like view
print(eatery_type_counts["Restaurant"])  # missing keys return 0 instead of KeyError
print(eatery_type_counts["Kiosk"])
Counter({'Mobile Food Truck': 3, 'Food Cart': 2, 'Snack Bar': 2, 'Restaurant': 2})
2
0

Đừng phụ thuộc vào thứ tự lặp của một Counter. Khi cần xếp hạng, hãy dùng most_common().

Tìm giá trị xuất hiện nhiều nhất với most_common()

Counter.most_common(n) trả về danh sách các cặp (item, count) theo thứ tự giảm dần của số đếm. Truyền n để giới hạn kết quả, hoặc bỏ qua để nhận tất cả các cặp.

top_3 = eatery_type_counts.most_common(3)
print(top_3)
[('Mobile Food Truck', 3), ('Food Cart', 2), ('Snack Bar', 2)]

Dùng khi bạn cần phân tích tần suất, bảng xếp hạng, hoặc kiểm tra nhanh dữ liệu phân loại.

Làm được nhiều hơn với Counter

update và subtract số đếm

Dữ liệu nghiệp vụ luôn thay đổi. update() cộng thêm vào số đếm, trong khi subtract() giảm chúng. Cả hai đều nhận iterable hoặc mapping.

new_permits = ["Restaurant", "Food Cart", "Restaurant"]
eatery_type_counts.update(new_permits)  # add 1 for each occurrence
print(eatery_type_counts)

closures = {"Snack Bar": 1}
eatery_type_counts.subtract(closures)   # reduce counts (can go negative)
print(eatery_type_counts)
Counter({'Mobile Food Truck': 3, 'Restaurant': 4, 'Food Cart': 3, 'Snack Bar': 2})
Counter({'Restaurant': 4, 'Mobile Food Truck': 3, 'Food Cart': 3, 'Snack Bar': 1})

Nếu số đếm giảm về 0 hoặc âm, các phương thức như elements() và most_common() sẽ bỏ qua. Để loại bỏ hoàn toàn một khóa, dùng del counter[key].

tính tổng và top-N

Bắt đầu từ Python 3.10, Counter.total() trả về tổng của tất cả số đếm. Hữu ích khi bạn cần tỷ trọng hoặc thị phần.

total_permits = eatery_type_counts.total()
for eatery, count in eatery_type_counts.most_common(3):
    share = count / total_permits
    print(f"{eatery}: {count} ({share:.1%})")

xây lại dãy với elements()

elements() trả về mỗi phần tử số lần tương ứng với số đếm của nó. Thứ tự là tùy ý.

expanded = list(eatery_type_counts.elements())
print(len(expanded), "items reconstructed from counts")

dùng phép toán và thao tác kiểu tập hợp

Bạn có thể kết hợp các counter với số học và các phép kiểu min/max. Kết quả sẽ loại bỏ số đếm bằng 0 và âm.

from collections import Counter

a = Counter({"Food Cart": 5, "Restaurant": 2})
b = Counter({"Food Cart": 3, "Snack Bar": 4})

print(a + b)   # add counts
print(a - b)   # subtract (keeps positives only)
print(a & b)   # intersection: min of counts
print(a | b)   # union: max of counts
Counter({'Food Cart': 8, 'Snack Bar': 4, 'Restaurant': 2})
Counter({'Food Cart': 2, 'Restaurant': 2})
Counter({'Food Cart': 3})
Counter({'Food Cart': 5, 'Snack Bar': 4, 'Restaurant': 2})

Khi nào dùng list.count(), Counter, hoặc pandas

Hãy chọn công cụ đơn giản nhất đáp ứng yêu cầu và ràng buộc hiệu năng của bạn.

kiểm tra một giá trị với count()

Dùng count() tích hợp khi bạn chỉ cần biết một giá trị xuất hiện bao nhiêu lần trong một dãy, hoặc đếm số lần xuất hiện không chồng lặp của một chuỗi con trong chuỗi.

numbers = [1, 2, 2, 3, 2]
print(numbers.count(2))  # 3

word = "banana"
print(word.count("a"))   # 3

text = "aaa"
print(text.count("aa"))  # 1: non-overlapping matches only
print("cat".count(""))   # 4: empty string counts len(s)+1 positions

Cẩn thận với boolean và số nguyên: True == 1 và False == 0. Điều này có thể làm tăng số đếm nếu bạn trộn kiểu.

mixed = [1, True, 0, False, True]
print(mixed.count(1))  # 3 (counts 1 and True)
print(mixed.count(0))  # 2 (counts 0 and False)

Tránh gọi count() lặp đi lặp lại trong vòng lặp trên dữ liệu lớn; mỗi lần gọi đều quét toàn bộ dãy.

# Inefficient: O(n^2) for large lists
# freq = {x: numbers.count(x) for x in numbers}

# Efficient: one pass
from collections import Counter
freq = Counter(numbers)

nhiều tần suất với Counter

Khi bạn cần số đếm cho nhiều giá trị duy nhất, hãy xây một Counter một lần và truy vấn khi cần. Rõ ràng, nhanh, và được thiết kế cho trường hợp này.

dữ liệu dạng bảng với pandas

Trong pandas, DataFrame.count() và Series.count() tính số lượng không thiếu. Để có bảng tần suất của giá trị, dùng Series.value_counts().

import pandas as pd
df = pd.DataFrame({
    "line": ["A", "A", "B", None, "B", "B"],
    "ridership": [100, None, 120, 130, None, 150],
})

print(df["line"].count())         # 5 (non-missing)
print(df["line"].value_counts())  # value frequencies

# For full row count, use len(df), not df.count()
print(len(df))                    # 6

Xác thực và làm sạch dữ liệu với Counter

Trước khi phân tích, tôi kiểm tra tính nhất quán của danh mục, giá trị bất ngờ, và trùng lặp. Counter giúp các kiểm tra này nhanh và lặp lại được.

from collections import Counter

# Example: validate allowed categories
allowed_types = {"Mobile Food Truck", "Food Cart", "Snack Bar", "Restaurant"}
type_counts = Counter(nyc_eatery_types)

unexpected = {t: c for t, c in type_counts.items() if t not in allowed_types}
if unexpected:
    print("Unexpected categories found:", unexpected)

# Example: flag duplicates (e.g., station IDs appearing more than once)
station_ids = ["ST-001", "ST-002", "ST-003", "ST-002", "ST-004", "ST-002"]
dup_counts = Counter(station_ids)
duplicates = [sid for sid, c in dup_counts.items() if c > 1]
print("Duplicate station IDs:", duplicates)

Ghi chú thực tế và lỗi thường gặp

Những chi tiết này giúp tiết kiệm thời gian và tránh lỗi tinh vi.

  • Counter giống dict: khóa thiếu trả về 0; dùng del counter[key] để xóa mục. Tránh lưu số 0 trừ khi bạn cần tạm thời.
  • Đừng dựa vào thứ tự lặp. Dùng most_common() cho đầu ra có xếp hạng.
  • str.count() đếm các kết quả không chồng lặp. Với số lần xuất hiện chồng lặp, dùng re.finditer().
  • Tránh gọi list.count() lặp lại trên cùng một dãy lớn. Thay vào đó, xây một Counter.
  • Trong pandas, .count() dùng để đếm không thiếu; dùng .value_counts() cho tần suất giá trị.

Những mẫu tối giản tôi thường dùng

Đây là các đoạn mã tôi dùng thường xuyên trong sản xuất và giảng dạy.

  • Bản đồ tần suất một lượt: Counter(iterable)
  • Top-N danh mục: Counter(data).most_common(n)
  • Cập nhật gia tăng: counter.update(batch) hoặc counter.subtract(batch)
  • Kiểm tra một giá trị: sequence.count(x) (tránh dùng trong vòng lặp)
  • Tần suất dạng bảng: df["col"].value_counts() (không phải df.count())

Kết luận

collections.Counter là công cụ phù hợp để đếm nhiều giá trị một cách nhanh chóng và rõ ràng. Dùng count() tích hợp cho các kiểm tra đơn lẻ, Counter cho bản đồ tần suất và phân tích top-N, và Series.value_counts() khi bạn làm việc trong pandas. Hãy để ý các trường hợp biên khi đếm chuỗi, sự tương đương boolean–số nguyên, và khác biệt giữa đếm không thiếu và tần suất giá trị. Với những mẫu này, các con số đếm của bạn sẽ vừa nhanh vừa chính xác.

Chủ đề

Liên quan