Use Levenshtein distance with Corpus retrieval#

scikitplot.levenshtein is an always-importable facade for edit distance. It prefers the bundled Scikit-Plots implementation, can use the MIT-licensed RapidFuzz package when available, and retains a dependency-free Python fallback. The separately licensed Levenshtein package is available only when explicitly requested.

Edit distance is lexical, not semantic. It is particularly useful for OCR variants, spelling variants, identifiers, short labels, and deterministic retrieval fallbacks. score_cutoff is backend-neutral normalized similarity, so low-quality candidates can be rejected consistently across accelerators.

# Authors: The scikit-plots developers
# SPDX-License-Identifier: BSD-3-Clause

from __future__ import annotations

from scikitplot import levenshtein
from scikitplot.corpus import CorpusDocument, RetrievalConfig

Basic distance#

print("kitten -> sitting:", levenshtein.distance("kitten", "sitting"))
print("active backend:", levenshtein.backend_info())
assert levenshtein.distance("kitten", "sitting") == 3
kitten -> sitting: 3
active backend: BackendInfo(name='internal', available=True, version=None, license='MIT', implementation='scikitplot.cexternals._editdistance', reason=None)

Rank noisy lexical variants#

choices = [
    "Scikit-Plots",
    "scikit plot",
    "science plots",
    "scikit-learn",
]

for match in levenshtein.rank(
    "scikit plots",
    choices,
    score_cutoff=0.55,
):
    print(match.choice, round(match.similarity, 3), match.distance)
scikit plot 0.917 1
Scikit-Plots 0.75 3
science plots 0.692 4

Adapt the scorer to Corpus#

The adapter is lazy: importing scikitplot.levenshtein does not import Corpus. When used with Corpus it returns normal RetrievalHit records with explicit lexical provenance.

documents = [
    CorpusDocument(
        doc_id="a",
        input_path="memory://a",
        chunk_index=0,
        text="hemoglobin concentration",
        normalized_text="hemoglobin concentration",
    ),
    CorpusDocument(
        doc_id="b",
        input_path="memory://b",
        chunk_index=0,
        text="haemoglobin concentration",
        normalized_text="haemoglobin concentration",
    ),
    CorpusDocument(
        doc_id="c",
        input_path="memory://c",
        chunk_index=0,
        text="platelet count",
        normalized_text="platelet count",
    ),
]

scorer = levenshtein.make_corpus_scorer(score_cutoff=0.60)
hits = scorer(
    "hemoglobin concentration",
    documents,
    RetrievalConfig(top_k=3),
)

for hit in hits:
    print(hit.rank, hit.doc.doc_id, round(hit.score, 3), hit.backend)

assert hits[0].doc.doc_id == "a"
assert hits[0].backend.startswith("levenshtein:")
0 a 1.0 levenshtein:internal
1 b 0.96 levenshtein:internal

Total running time of the script: (0 minutes 0.005 seconds)

Related examples

Use Levenshtein with Corpus

Use Levenshtein with Corpus

Sequences and explicit normalization

Sequences and explicit normalization

Levenshtein basics

Levenshtein basics

Backend discovery and safe fallback

Backend discovery and safe fallback

Gallery generated by Sphinx-Gallery