Skip to content

All projects

CRM Dedupe Toolkit

Synthetic benchmark Synthetic data: Ledgerly

Finds duplicate accounts and contacts in CRM exports, decides which record survives, and produces merge files a Salesforce admin can act on, each with its reasons.

Status
Code on GitHub
Relevant to
Data and analytics, GTM Systems
Code
github.com/jungmarker/crm-dedupe-toolkit

Results · Synthetic data: Ledgerly

  • account auto-merge precision on a company the model never sawSynthetic benchmark
  • account F1 after steward review of 23 pairs, against 0.63 for a same-domain baselineSynthetic benchmark
  • contact recall after review, at 99.8% precisionSynthetic benchmark
  • contact merges kept the right surviving recordSynthetic benchmark

At a glance

Question
Which CRM accounts and contacts are duplicates, and which record should survive a merge?
Data
Ledgerly’s synthetic CRM exports with a hidden list of true duplicates, and a second company the model never saw.
Compared with
Matching on the same website domain.
How it was tested
Normalize and block candidate pairs, train a small logistic model on 400 steward-reviewed pairs, and score merges against the answer key.
Result
Account F1 of 0.97 after steward review of 23 pairs, against 0.63 for the domain baseline, and 100% auto-merge precision on the unseen company.
What it doesn’t show
The duplicates and the answer key come from the same synthetic generator, so these scores describe that data, not a real CRM.
Dedupe report with accuracy against ground truth and the review queue
Dedupe report with accuracy against ground truth and the review queue

The problem

No single field is enough. Matching on website alone would merge 114 pairs of different companies that share a domain pattern, and matching on name alone is worse.

What I built, in detail

  • Normalization for names, domains, states, emails, phones and nicknames, then blocking that cuts 4.9M possible account comparisons to about 2,700 candidates while keeping 99% of true duplicates.
  • A logistic regression on field agreement levels with readable weights, trained on 400 steward-reviewed pairs. Every merge row lists its top reasons.
  • Union-find clustering, survivorship rules, and merge-plan, review-queue, re-parent and golden-record files.
  • An unsupervised Fellegi-Sunter model came first. Territory-correlated fields broke its independence assumption, so I switched to a small labeled sample.

Stack

Python, pandas, RapidFuzz, scikit-learn, pytest, GitHub Actions