skills/fuzzy-name-matching/SKILL.md
Use when linking or deduping datasets by entity name rather than a shared key — 'fuzzy match', 'fuzzy name matching', 'entity resolution', 'record linkage', 'match company/person names', 'dedupe entity names', 'name-based join', 'bridge identifiers' (CIK ↔ permno ↔ gvkey ↔ wficn ↔ EIN ↔ personid), or any use of char n-gram TF-IDF, cosine similarity on names, `sparse_dot_topn`, or RapidFuzz at scale.
npx skillsauth add edwinhu/workflows fuzzy-name-matchingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fast many-to-many fuzzy entity matching: char n-gram TF-IDF + sparse top-k cosine similarity (the ING banks recipe). Scales to ~10⁵ × 10⁵ on a laptop in seconds, ~10⁶ × 10⁶ with chunking.
The full recipe — code, threshold guide, gotchas, alternatives considered —
is in references/fuzzy-name-matching.md. Read it before writing match
code; a runnable template is in examples/fuzzy_name_match_sample.py.
Fuzzy matching is the last step of a linkage, never the first:
normalize() function in the referenceSkipping straight to TF-IDF is NOT HELPFUL — every deterministic rule you skip upfront comes back as false positives the user has to find later, in a join they now believe is clean.
"GABELLI MARIO JOSEPH" ↔ "GABELLI MARIO J" scores 0.69 in a 2-row toy fit and 0.84 in a 600K-corpus fit — same pair, different context. Fit once on pd.concat([left_all, right_all]), reuse .transform() everywhere."IBM" ↔ "International Business Machines" will never match at any threshold — that needs a name dictionary or an embedding model.sp_matmul_topn(A, B, ...) wants B already transposed (R.T). sort only orders the hits within each row — top-k selection always keeps the largest values, so top_n=1 returns the argmax with sort=False too (verified on 1.2.0: 60/60 rows matched a dense argmax either way). Pass sort=True when top_n>1 and you care about the order.top_n=1 pick one of them with no defined rule — sort does not disambiguate an exact tie (tested on 1.2.0: it kept the last duplicate in every layout, under both sort settings, but nothing documents that). Whichever it is, the id you get is an accident of row order. Deduplicate the right side first.sp_matmul_topn before an exact normalized join has been tried and measured → STOP. You cannot tell false positives from real coverage without the exact-join baseline.TfidfVectorizer inside a per-key loop → STOP. Scores become incomparable across keys; fit once on the full corpus outside the loop.Reach for this when linking two datasets whose only shared field is an entity name — bridging identifiers (CIK ↔ permno ↔ gvkey ↔ wficn ↔ EIN ↔ TR personid), deduping filer/fund/insider names, or resolving vendor records that share no key at all.
Don't use it for exact joins (pandas merge), for semantic equivalence, or
below ~5K × 5K rows where RapidFuzz is simpler. The reference's
"Alternatives considered" table covers rapidfuzz, recordlinkage,
dedupe, and edit-distance metrics and when each wins.
normalize both sides
↓
exact scoped join (issuer/year/geo + norm_name) ← most of your hits
↓
exact global join (unambiguous norm_names only)
↓
fuzzy SCOPED pass — threshold ≈0.80, per-key candidate pools
↓
fuzzy GLOBAL pass — threshold ≥0.90, residuals only, dedup right side first
↓
residual: synthetic ids / leave unmatched — never force a match
Code for each stage, and the threshold table that governs what to accept at
each one, is in references/fuzzy-name-matching.md.
# pixi.toml
[dependencies]
scikit-learn = "*"
[pypi-dependencies]
sparse_dot_topn = ">=1.2" # ING's fast sparse top-k matmul
conda-forge tops out at sparse_dot_topn 0.3.1, which predates the v1
sp_matmul_topn API this recipe uses (checked against the conda-forge channel
2026-07-22). pixi add sparse_dot_topn therefore installs a version without
the function — use pixi add --pypi sparse_dot_topn to get ≥1.2.
references/fuzzy-name-matching.md — the full recipe: minimal code, threshold guide, normalize-first rule, scoped + global two-pass pattern, seven gotchas, alternatives considered, end-to-end results tableexamples/fuzzy_name_match_sample.py — runnable template: normalize(), fuzzy_match(), fuzzy_match_scoped(), plus a toy demo linking insider names to reporting-owner CIKsskills/wrds/examples/blockholders_pipeline/redo_bridge.py — production pipeline this recipe came out of (TR personid → SEC rptOwnerCik, 97.4% hit rate)skills/wrds/references/linkage.md — try a real link table first; fuzzy matching is for identifiers that genuinely don't cross vendorsdevelopment
Build the meeting-level proxy-voting × ownership panel on the WRDS SGE grid — ISS N-PX fund votes reduced to (item × block) direction cells, joined to institutional and mutual-fund ownership. Use when working with risk.voteanalysis_npx, N-PX fund-level votes, ISS→CRSP fund linking, index/passive/active voting blocks, or a proxy-voting panel that needs ownership attached.
development
Use when "CRSP CIZ", "CRSP v2", "CRSP flat file format 2.0", "crsp.dsf_v2 / msf_v2", "StkDlySecurityData", "StkMthSecurityData", "StkSecurityInfoHist", "stocknames_v2", "DlyRet / MthRet / DlyPrc / MthPrc", "SHRCD or EXCHCD equivalent in new CRSP", "SIZ to CIZ migration", "CRSP data after 2024", "CRSP delisting returns", "CRSP cumulative adjustment factors", "CRSP index INDNO / INDFAM", or any CRSP stock/index query where the legacy SIZ column names no longer exist.
development
Use when building a publication-quality table in Python — 'regression table', 'results table', 'summary statistics table', 'etable', 'coefplot', 'great_tables', 'GT', 'gt table', 'format a table for the paper', 'export table to LaTeX/HTML', significance stars, spanners, or column formatting for a table headed into a paper, slide deck, or notebook.
development
Use this skill to BUILD a Word manuscript from LAW AND ECONOMICS markdown — author-date citations plus a reference list, double spaced throughout, Latin Modern typography — for the Journal of Law and Economics, Journal of Legal Studies, JLEO, ALER, or an econ-flavored job market paper. NOT 'law-review-docx' (that is footnote/Bluebook law review style with small caps and a TOC — different discipline, different citation model), NOT the generic 'docx' skill (which edits docx content), NOT 'docx-render' (which only converts an existing .docx to PDF). Triggers: 'build the L&E paper', 'law and economics Word template', 'JLE submission docx', 'Journal of Legal Studies format', 'JLEO manuscript', 'author-date Word document', 'econ job market paper in Word', 'Chicago author-date docx', 'make the submission docx for JLE'.