Skip to main content

Jaro Winkler Distance

PyPI - Version License PyPI - Python Version GitHub Actions Workflow Status

Finds a non-euclidean distance or similarity between two strings.

Jaro and Jaro-Winkler equations provide a score between two short strings where errors are more prone at the end of the string. Jaro's equation measure is the weighted sum of the percentage of matching and transposed characters from each string. Winkler's factor adds weight in Jaro's formula to increase the calculated measure when there is a sequence of characters (a prefix) in both strings.

This version is based on the original C implementation of strcmp95 implementation but does not attempt to normalize homoglyph (e.g.: O vs. 0).

  • Impact of the prefix is limited to 4 characters, as originally defined by Winkler.
  • Input strings are not modified beyond whitespace trimming.
  • In-word whitespace and characters case will optionally impact score.
  • Supports optional UTF-8 normalization and homoglyph sanitization.
  • Returns a floating point number rounded to the desired decimals (defaults to 2) using Python's round.
  • Consider usual floating point arithmetic characteristics when working with this module.

Implementation

The complexity of this algorithm resides in finding the matching and transposed characters. That is because of the interpretation of what are the matching conditions and the definition of transposed. Definitions of those two will make the score vary between implementations of this algorithm.

Here is how matching and transposed are defined in this module:

  • A character of the first string at position N is matching if found at position N or within distance on either side in the second string.
  • The distance is calculated using the rounded down length of the longest string divided by two minus one.
  • Characters in the first string are matched only once against characters of the second string.
  • Two characters are transposed if they previously matched and aren't at the same position in the matching character subset.
  • Decimals are rounded according to the scientific method.

Example

Calculate the Jaro Winkler similarity ($sim_{w}$) between PENNSYLVANIA and PENNCISYLVNIA:

$$ s_{1}=\text{PENNSYLVANIA} \qquad\text{and}\qquad s_{2}=\text{PENNCISYLVNIA} $$

    P E N N C I S Y L V N I A
  ┌-─────────────────────────
P │ 1          ╎
E │   1          ╎
N │     1          ╎
N │       1          ╎           Symbols '╎' represent the sliding windows
S │             1      ╎        boundary in the second string where we look
Y │ ╎             1      ╎           for the first string's character.
L │   ╎             1      ╎
V │     ╎             1                   d = 5 in this example.
A │       ╎                 1
N │         ╎           1
I │           ╎           1
A │             ╎

$$ \begin{split} d &= \left\lfloor {\max(12, 13) \over 2} \right\rfloor - 1 \newline &= 5 \newline \end{split} \qquad \text{ and } \qquad \begin{split} |s_{1}| &= 12 \newline |s_{2}| &= 13 \newline \end{split} \qquad \text{ and } \qquad \begin{split} \ell &= 4 \newline m &= 11 \newline t &= 3 \newline p &= 0.1 \newline \end{split} $$

Considering the input parameters calculated above:

$$ \begin{split} sim_{j} &=\begin{cases} 0 & \text{if } m = 0 \newline {1 \over 3} \times \left({m \over |s_{1}|} + {m \over |s_{2}|} + {{m - t} \over m} \right) & \text{otherwise} \end{cases} \newline &={1 \over 3} \times \left({11 \over 12} + {11 \over 13} + {{11 - 3} \over 11}\right) \newline &= 0.83003108003 \newline \end{split} \qquad \text{then} \qquad \begin{split} sim_{w} &= sim_{j} + \ell \times p \times (1 - sim_{j}) \newline &= 0.83003108003 + 4 \times 0.1 \times (1 - 0.83003108003) \newline &= 0.89801864801 \newline \end{split} $$

We found that the $\lceil sim_{w} \rceil$ is $0.9$.

Benchmark

Function Minimum Time (1k runs of 10 pairs)
get_jaro_distance(s1, s2) 0.0149s
get_jaro_similarity(s1, s2) 0.0148s
get_jaro_winkler_distance(s1, s2) 0.0176s
get_jaro_winkler_similarity(s1, s2) 0.0172s

Benchmarking ran on a 2024 Macbook Pro with an M4 Pro chip running macOS 26.2.

Usage

from pyjarowinkler import distance

distance.get_jaro_similarity("PENNSYLVANIA", "PENNCISYLVNIA", decimals=12)
# 0.830031080031
distance.get_jaro_winkler_similarity("PENNSYLVANIA", "PENNCISYLVNIA", decimals=12)
# 0.898018648019
distance.get_jaro_distance("hello", "haloa", decimals=4)
# 0.2667
distance.get_jaro_similarity("hello", "haloa", decimals=2)
# 0.73
distance.get_jaro_winkler_distance("hello", "Haloa", scaling=0.1, norm_case=False)
# 0.4
distance.get_jaro_winkler_distance("hello", "HaLoA", scaling=0.1, norm_case=True)
# 0.24
distance.get_jaro_winkler_similarity("café", "cafe\u0301", norm_utf8=True)
# 1.0
distance.get_jaro_winkler_similarity("pаypal", "paypal", norm_ambiguous=True)
# 1.0
distance.get_jaro_winkler_similarity("hello", "haloa", decimals=2)
# 0.76

Contribute

You need to have installed mise on your system. Then, running the commands below will install python, uv, and github-cli.

Typical order of execution is as follow:

$ cd ./jaro-winkler-distance
$ mise install
$ uv venv
$ source .venv/bin/activate
$ uv pip install '.[dev]'

Other helpful commands:

  • uvx --python=3.12 python -m unittest discover -s tests/
  • uvx ruff check --diff
  • uvx ruff format --diff
  • uvx mypy
  • uvx coverage run -m unittest discover -s tests/
  • uvx coverage report

Release

$ ./release.sh help
Usage: release.sh [help|major|minor|patch]
$ PYPI_REPO=main ./release.sh minor

Metadata

Release files for pyjarowinkler 3.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyjarowinkler 3.0.0
File Size Uploaded
pyjarowinkler-3.0.0.tar.gz 19.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyjarowinkler 3.0.0
File Interpreter ABI Platform
pyjarowinkler-3.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 37.7 kB

Release files / pyjarowinkler-3.0.0.tar.gz

Download URL pyjarowinkler-3.0.0.tar.gz
Size 19.0 kB
Tags Source
SHA-256 checksum
How to use checksums
f9d1005c562b21b0815e0e23d0386442772ca57d444b37a1adfed2407c317b21
BLAKE2b-256 checksum
How to use checksums
14b9076418d17a751baacb1e1e9b0b5116285b3cfc758a921c6e44702aee781c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / pyjarowinkler-3.0.0-py3-none-any.whl

Download URL pyjarowinkler-3.0.0-py3-none-any.whl
Size 18.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0622f704e8467d9471b33865ed7af480632d50a5c5d1799f51f46da5c4f0b47a
BLAKE2b-256 checksum
How to use checksums
647e2df527163d503218c8eb8914e548f27d4b2a4b5ccd6948c8dab175b25598
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

3.0.0 This release

2 release files

2.4.0

2 release files

2.3.0

2 release files

2.2.0

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.8

2 release files

1.7

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page