textdistance Rust Library

repository·master·Indexed 43 days ago

https://github.com/life4/textdistance.rs

A zero-dependency, no_std compatible Rust library providing over 20 algorithms for comparing the similarity or distance between two sequences. It includes edit-based (e.g., Levenshtein, DamerauLevenshtein, Hamming), token-based (e.g., Jaccard, Cosine), and sequence-based (e.g., RatcliffObershelp, LCS) algorithms. Version 1.1.1.

Tokens
5.9K
Snippets
20
Records
25
Agent score
54%

What's inside textdistance

  1. Choose the right algorithm for your use case

    master

    Algorithms are categorized by the type of sequence they compare:

    1. Edit-based: Best for short sequences (e.g., detecting typos).
    2. Token-based: Best for long sequences (e.g., comparing long texts with modifications).
    3. Sequence-based: Best for calculating diff sizes between original and changed versions.

    Edit-based Algorithm Comparison

    algorithmsubstitutionadditiondeletiontransposition
    Hamming✅❌❌❌
    Jaro❌❌❌✅
    JaroWinkler❌❌❌✅
    Sift4❌❌❌✅
    Levenshtein✅✅✅❌
    DamerauLevenshtein✅✅✅✅

    Quick Tips:

    • Fastest (Substitutions only): Hamming.
    • Fast & Battle-tested: Jaro.
    • Fastest (Transpositions): Sift4.
    • Comprehensive (No transpositions): Levenshtein.
    • Most Comprehensive: DamerauLevenshtein (but slowest).
  2. Install textdistance

    master

    To add textdistance to your project, use the following command:

    cargo add textdistance

    If you are working on an embedded system or a no_std project, install it without default features:

    cargo add --no-default-features textdistance
  3. Handle Unicode grapheme clusters

    master

    By default, for_str and the str/nstr modules use String.chars, which treats combining characters (like é) as distinct code points.

    To treat Unicode grapheme clusters as single symbols, use the unicode-segmentation crate and pass the graphemes as an iterator to the for_iter method.

    use textdistance::{Algorithm, DamerauLevenshtein};
    use unicode_segmentation::UnicodeSegmentation;
    
    let s1 = "a̐éö̲\r\n";
    let s2 = "éa̐ö̲\r\n";
    let g1 = s1.graphemes(true);
    let g2 = s2.graphemes(true);
    let a = DamerauLevenshtein::default();
    let r = a.for_iter(g1, g2);
    assert!(r.val() == 1);
  4. How the Algorithm trait works

    master

    The Algorithm<R> trait defines a unified way to interact with various distance metrics. It is generic over the result type R.

    Most implementations provide specialized methods for common data types like strings and vectors, but they all eventually resolve to the for_iter or for_vec logic. This allows you to use the same algorithm instance to compare raw characters, words, or even custom sequences by choosing the appropriate method call.

    Note that for methods involving iterators or vectors (for_iter, for_vec), the elements must implement Eq + Hash.

  5. Use the Algorithm trait for advanced usage

    master

    For more control, you can use algorithm structs that implement the Algorithm trait. This allows you to work with strings, vectors, or iterators, and provides access to both absolute and normalized results.

    Methods available on the Algorithm trait:

    • for_str: Calculate distance between two strings.
    • for_vec: Calculate distance between two vectors (slices).
    • for_iter: Calculate distance between two iterators.
    • for_words: Split text into words before calculating.
    • for_bigrams: Split text into bigrams before calculating.

    Each method returns a textdistance::Result which provides:

    • val(): Absolute value.
    • nval(): Normalized value.
    • dist() / ndist(): Distance metric.
    • sim() / nsim(): Similarity metric.
    use textdistance::{Algorithm, DamerauLevenshtein};
    
    let a = DamerauLevenshtein::default();
    let r = a.for_str("abc", "acbd");
    assert!(r.val() == 2);
    assert!(r.nval() == 2./4.);
  6. Use shortcut functions in textdistance::str and textdistance::nstr

    master

    The textdistance::str module provides shortcut functions for each algorithm to calculate the absolute distance between two strings.

    The textdistance::nstr module provides the same functions but returns a normalized value between 0.0 and 1.0.

    // Absolute distance
    use textdistance::str::damerau_levenshtein;
    assert!(damerau_levenshtein("abc", "acbd") == 2);
    
    // Normalized distance (0.0 - 1.0)
    use textdistance::nstr::damerau_levenshtein;
    assert!(damerau_levenshtein("abc", "acbd") == 2./4.);
  7. Configure Sift4Common parameters

    master

    When using Sift4Common, you can tune its performance and behavior using two public fields:

    • max_offset (usize): The number of characters to search for matching letters. The default value is 5.
    • max_distance (usize): The distance threshold at which the algorithm stops computing and exits early (indicating the strings are too different). The default value is 0 (no early exit).
    // Using default values
    let default_algo = Sift4Common::default();
    
    // Custom configuration
    let custom_algo = Sift4Common {
        max_offset: 10,
        max_distance: 3,
    };
  8. Configure Jaro-Winkler prefix parameters

    master

    You can customize how the Jaro-Winkler algorithm handles common prefixes using the following fields in the JaroWinkler struct:

    • prefix_weight: A scaling factor for how much the Jaro score is adjusted for the common prefix. The default is 0.1. Constraint: This value must not be higher than 1/ℓ, where ℓ is the max_prefix value.
    • max_prefix: The maximum length of the common prefix to consider. The default is 4.
    let custom_jw = JaroWinkler {
        jaro: Jaro::default(),
        prefix_weight: 0.1,
        max_prefix: 4,
    };
  9. Reference of available algorithms

    master

    The library contains 20+ algorithms categorized as follows:

    Edit-based:

    • DamerauLevenshtein (optimal string alignment and restricted)
    • Hamming
    • Jaro
    • JaroWinkler
    • Levenshtein
    • Sift4Common
    • Sift4Simple
    • SmithWaterman

    Token-based:

    • Bag
    • Cosine (aka Orchini, Tucker, Otsuka–Ochiai)
    • EntropyNCD (Entropy-based Normalized Compression Distance)
    • Jaccard (aka Tanimoto, Critical Success Index)
    • Overlap (aka Szymkiewicz–Simpson)
    • Roberts
    • SorensenDice (aka F1, Czekanowski, Zijdenbos)
    • Tversky

    Sequence-based:

    • LCSSeq (Longest Common SubSequence)
    • LCSStr (Longest Common SubString)
    • RatcliffObershelp (aka Gestalt pattern matching)

    Naive:

    • Prefix
    • Suffix
    • Length

    Normalization metrics:

    • LIG3 (for Hamming by Levenshtein)
    • MLIPNS (for Hamming)
    • YujianBo (for Levenshtein)
  10. Use the Jaccard similarity function for strings

    master

    For a convenient way to compare strings using the Jaccard index, you can use the jaccard function (typically available via the textdistance::str module). This function handles the string iteration and returns the similarity score directly.

    use textdistance::str::jaccard;
    
    let sim = jaccard("nelson", "neilsen");
    // Expected similarity: 5/8 = 0.625
    println!("Similarity: {}", sim);
  11. Use the Jaro-Winkler similarity algorithm

    master

    The JaroWinkler struct implements a variation of the Jaro similarity algorithm that gives higher ratings to strings with matching prefixes. The similarity score is normalized between 0.0 and 1.0.

    To use it, you can instantiate it with JaroWinkler::default() or configure the prefix weighting parameters. It implements the Algorithm<f64> trait, allowing it to be used with the for_vec method to compare slices of elements.

    use textdistance::algorithms::jaro_winkler::JaroWinkler;
    use textdistance::Algorithm;
    
    let jaro_winkler = JaroWinkler::default();
    let s1 = ["martha"];
    let s2 = ["marhta"];
    
    // Note: The implementation uses slices of elements (E: Eq + Hash)
    let result = jaro_winkler.for_vec(&["martha"], &["marhta"]).unwrap();
    println!("Similarity: {}", result.abs);
  12. Access distance and similarity results

    master

    When you run an algorithm, it returns a Result object. This object encapsulates both the raw value and normalized metrics for easy comparison.

    For algorithms returning usize (integer distance):

    • .dist(): Returns the raw integer distance.
    • .sim(): Returns the similarity as an f64 (usually 1.0 - (dist / max_len)).
    • .ndist(): Returns the normalized distance as an f64 (typically between 0.0 and 1.0).
    • .nsim(): Returns the normalized similarity as an f64 (typically between 0.0 and 1.0).

    For algorithms returning f64 (similarity score):

    • .val(): Returns the raw f64 value.
    • .ndist(): Returns the normalized distance as an f64.
    • .nsim(): Returns the normalized similarity as an f64.
    • .max(): Returns the maximum possible value for the comparison.

    All Result objects also provide .len1 and .len2 representing the character counts of the input strings.