Phân tích độ tương đồng văn bản bằng SequenceMatcher trong Python

Trong lĩnh vực xử lý văn bản và phân tích dữ liệu, việc so sánh mức độ tương đồng hoặc phát hiện sự khác biệt giữa hai đoạn văn bản là một tác vụ cơ bản và quan trọng. Điều này đặc biệt hữu ích trong các ứng dụng như kiểm tra đạo văn, so sánh phiên bản tài liệu, hoặc phân tích sự thay đổi trong mã nguồn. Thư viện difflib của Python cung cấp các công cụ mạnh mẽ để thực hiện điều này, và một trong những lớp nổi bật nhất là SequenceMatcher.

Tổng quan về SequenceMatcher

Lớp SequenceMatcher trong thư viện difflib được thiết kế để so sánh hai chuỗi bất kỳ (ví dụ: chuỗi ký tự, danh sách, tuple) và xác định các chuỗi con liên tục dài nhất mà chúng có điểm chung. Nó tìm kiếm các đoạn khớp tối ưu nhất, giúp đánh giá mức độ tương đồng và chỉ ra các vị trí khác biệt.

Khởi tạo SequenceMatcher

Để bắt đầu sử dụng SequenceMatcher, chúng ta cần nhập lớp này từ mô-đun difflib và khởi tạo một đối tượng, truyền vào hai chuỗi mà bạn muốn so sánh. Tham số đầu tiên (thường là None) cho phép bạn bỏ qua các ký tự nhất định, nhưng trong hầu hết các trường hợp so sánh văn bản thông thường, chúng ta không cần đến nó.

from difflib import SequenceMatcher

# Hai chuỗi văn bản cần so sánh
van_ban_goc = "Đây là một ví dụ về đoạn văn bản để minh họa."
van_ban_so_sanh = "Đây là một ví dụ về văn bản khác để so sánh."

# Khởi tạo đối tượng SequenceMatcher
trinh_so_khop = SequenceMatcher(None, van_ban_goc, van_ban_so_sanh)

Tính toán tỷ lệ tương đồng

Sau khi khởi tạo đối tượng SequenceMatcher, bạn có thể dễ dàng tính toán tỷ lệ tương đồng giữa hai chuỗi bằng phương thức ratio(). Phương thức này trả về một giá trị dấu phẩy động từ 0 đến 1, trong đó 1 biểu thị sự khớp hoàn toàn và 0 biểu thị không có sự trùng lặp nào.

# Tính toán tỷ lệ tương đồng
ty_le_tuong_dong = trinh_so_khop.ratio()
print(f"Tỷ lệ tương đồng giữa hai văn bản: {ty_le_tuong_dong:.2f}")

# Kết quả ví dụ:
# Tỷ lệ tương đồng giữa hai văn bản: 0.81

Xác định các khối văn bản trùng khớp

Phương thức get_matching_blocks() trả về một danh sách các tuple, mỗi tuple mô tả một "khối" văn bản trùng khớp giữa hai chuỗi. Mỗi tuple có dạng (i, j, n), trong đó:

  • i là chỉ số bắt đầu của khối trùng khớp trong chuỗi đầu tiên.
  • j là chỉ số bắt đầu của khối trùng khớp trong chuỗi thứ hai.
  • n là độ dài của khối trùng khớp.
print("\nCác khối văn bản trùng khớp:")
cac_khoi_khop = trinh_so_khop.get_matching_blocks()

for block in cac_khoi_khop:
    idx_goc, idx_ss, do_dai = block
    if do_dai > 0: # Chỉ hiển thị các khối có độ dài thực sự
        print(f"  Vị trí gốc [{idx_goc}:{idx_goc + do_dai}] == Vị trí so sánh [{idx_ss}:{idx_ss + do_dai}] (Dài: {do_dai})")
        print(f"    '{van_ban_goc[idx_goc:idx_goc + do_dai]}' == '{van_ban_so_sanh[idx_ss:idx_ss + do_dai]}'")

# Kết quả ví dụ:
# Các khối văn bản trùng khớp:
#   Vị trí gốc [0:10] == Vị trí so sánh [0:10] (Dài: 10)
#     'Đây là một ' == 'Đây là một '
#   Vị trí gốc [12:21] == Vị trí so sánh [12:21] (Dài: 9)
#     'văn bản để' == 'văn bản để'
#   Vị trí gốc [22:24] == Vị trí so sánh [22:24] (Dài: 2)
#     'minh' == 'so'
#   Vị trí gốc [26:30] == Vị trí so sánh [26:30] (Dài: 4)
#     'họa.' == 'sánh.'

Phân tích sự khác biệt chi tiết (Opcodes)

Để hiểu rõ hơn về cách các chuỗi được biến đổi từ chuỗi này sang chuỗi kia, phương thức get_opcodes() cung cấp một danh sách các "mã hoạt động" (opcodes). Mỗi opcode là một tuple có dạng (tag, i1, i2, j1, j2), mô tả một thao tác cần thiết:

  • tag: Loại thao tác ('replace', 'delete', 'insert', 'equal').
  • i1:i2: Phạm vi chỉ số trong chuỗi gốc (chuỗi đầu tiên) bị ảnh hưởng bởi thao tác.
  • j1:j2: Phạm vi chỉ số trong chuỗi so sánh (chuỗi thứ hai) bị ảnh hưởng bởi thao tác.
print("\nPhân tích chi tiết sự khác biệt (Opcodes):")
cac_op_codes = trinh_so_khop.get_opcodes()

for tag, i1, i2, j1, j2 in cac_op_codes:
    print(f"[{tag.upper()}] - Chuỗi gốc: '{van_ban_goc[i1:i2]}' [{i1}:{i2}] | Chuỗi so sánh: '{van_ban_so_sanh[j1:j2]}' [{j1}:{j2}]")

# Kết quả ví dụ:
# Phân tích chi tiết sự khác biệt (Opcodes):
# [EQUAL] - Chuỗi gốc: 'Đây là một ' [0:10] | Chuỗi so sánh: 'Đây là một ' [0:10]
# [REPLACE] - Chuỗi gốc: 'ví dụ' [10:12] | Chuỗi so sánh: 'văn' [10:12]
# [EQUAL] - Chuỗi gốc: 'văn bản để' [12:21] | Chuỗi so sánh: 'văn bản để' [12:21]
# [REPLACE] - Chuỗi gốc: ' minh họa.' [21:30] | Chuỗi so sánh: ' so sánh.' [21:30]

Thẻ: python difflib SequenceMatcher text-comparison text-similarity

Đăng vào ngày 19 tháng 7 lúc 12:10