Find Near-Duplicate Rows
find_fuzzy_duplicates · duplicates · any
2026-09-23
กำลังประมวลผล…
find_fuzzy_duplicates · duplicates · any
2026-09-23
สมชาย ใจดี กับ สมชาย ใจดี เป็นคนเดียวกัน แต่การลบแถวซ้ำแบบเทียบตรงตัวจับไม่ได้ เทคนิคนี้เทียบความคล้ายของคีย์ แล้วจัดแถวที่ใกล้เคียงกันเข้ากลุ่มเดียว โดยค่าเริ่มต้นจะเพียงใส่หมายเลขกลุ่มไว้ในคอลัมน์ใหม่ ให้คุณตรวจเองก่อนตัดสินใจลบ
keyColumns โดยตัดช่องว่างหัวท้าย ยุบช่องว่างซ้ำ และทำตัวพิมพ์เล็กlevenshtein หรือ jaro_winkler แล้วรวมเป็นกลุ่มเมื่อความคล้ายถึง thresholdflag ใส่หมายเลขกลุ่มให้เฉพาะแถวที่มีคู่ ส่วน remove เก็บตัวแทนกลุ่มตาม keep เหมือน remove_duplicate_by_column| Parameter | Type | Default |
|---|---|---|
keyColumns | string[] | — |
algorithm | levenshtein | jaro_winkler | "levenshtein" |
threshold | number | 0.9 |
action | flag | remove | "flag" |
keep | first | last | max_completeness | "first" |
newColumnName | string | "duplicate_group" |
import json
import re
def normalize(value):
return re.sub(r'\s+', ' ', str(value or '').strip()).lower()
def levenshtein(a, b):
previous = list(range(len(b) + 1))
for i, ca in enumerate(a, 1):
current = [i]
for j, cb in enumerate(b, 1):
current.append(min(previous[j] + 1, current[j - 1] + 1, previous[j - 1] + (ca != cb)))
previous = current
return previous[-1]
def similarity(a, b):
longest = max(len(a), len(b))
return 1.0 if longest == 0 else 1 - levenshtein(a, b) / longest
def find_fuzzy_duplicates(rows, key_columns, threshold=0.9):
keys = [None if all(row.get(c) is None for c in key_columns)
else '␟'.join(normalize(row.get(c)) for c in key_columns) for row in rows]
groups = {}
for key in [k for k in keys if k is not None]:
if key in groups:
continue
match = next((g for g in groups if similarity(g, key) >= threshold), None)
groups[key] = groups[match] if match else len(groups)
sizes = {}
for key in keys:
if key is not None:
sizes[groups[key]] = sizes.get(groups[key], 0) + 1
numbers = {}
result = []
for key in keys:
if key is None or sizes[groups[key]] < 2:
result.append(None)
continue
group = groups[key]
numbers.setdefault(group, len(numbers) + 1)
result.append(numbers[group])
return result
rows = [
{'name': 'สมชาย ใจดี'},
{'name': 'สมชาย ใจดี '},
{'name': 'Somchai Jaidee'},
{'name': None},
]
print(json.dumps(find_fuzzy_duplicates(rows, ['name']), ensure_ascii=False))
ไฟล์ผู้เข้าร่วมงาน 2,400 แถวมีชื่อซ้ำที่พิมพ์ต่างกันเล็กน้อย รัน find_fuzzy_duplicates แบบ flag ด้วย threshold 0.9 แล้วเรียงตามคอลัมน์หมายเลขกลุ่มเพื่อไล่ดูทีละกลุ่ม ถ้าพบว่าจับคู่ผิดบ่อย ให้เพิ่ม threshold หรือเพิ่มคอลัมน์คีย์ เมื่อมั่นใจแล้วจึงเปลี่ยนเป็น remove พร้อม keep แบบ max_completeness
ใช้ trim_whitespace และ collapse_spaces ก่อนเพื่อลดความต่างที่ไม่จำเป็น ใช้ remove_duplicate_by_column เมื่อคีย์ตรงกันเป๊ะอยู่แล้ว และใช้ find_similar_values เมื่อปัญหาอยู่ที่ค่าในคอลัมน์เดียว ไม่ใช่ทั้งแถว