Skip to content

Geting started

Install

uk_address_matcher is a Python package available on PyPI. You can install with pip:

pip install uk_address_matcher

Input data requirements

Both your messy addresses and your canonical addresses need at least these columns:

Column Description
unique_id Stable unique identifier
address_concat Address text, which can include the postcode

Optionally you can provide:

Column Description
postcode If provided, this postcode is used in favour over any postcode provided in address_concat
ukam_label The unique ID of the true match. If provided, it enables accuracy analysis output

Choose whether to pre-process your canonical dataset

If you're linking to a small canonical dataset (of say, less than 500,000 rows), then it's simplest to process the data on-the-fly.

If you're linking to a large canonical dataset (for example, national-scale NGD), then we recommend a one-time pre-processing step. It computes reusable datasets (indices and feature tables) once, so subsequent matching runs are fast.

The examples below use the fictional London datasets from ukam_datasets, which are included for runnable examples.

import duckdb
from uk_address_matcher import AddressMatcher, ukam_datasets

con = duckdb.connect()

df_messy = ukam_datasets.as_relation("fictional_london_messy", con=con)
df_canonical = ukam_datasets.as_relation("fictional_london_canonical", con=con)

matcher = AddressMatcher(
    canonical_addresses=df_canonical,
    addresses_to_match=df_messy,
    con=con,
)
result = matcher.match()
print(result.matches().limit(5).to_df().to_markdown(index=False))
unique_id resolved_canonical_id ukam_label original_address_concat clean_full_address_canonical original_address_concat_canonical match_reason match_weight distinguishability
m_0001165 c_0008592 c_0008592 Suite 1, 171 Primrosemarket St, West Alder, London SUITE 1 171 PRIMROSEMARKET STREET WEST ALDER LONDON Suite 1, 171 Primrosemarket Street, West Alder, London splink: probabilistic match 56.2066 nan
m_0001287 c_0002728 c_0002728 Unit 11,164 Kestrellane Cl,Maple Green,London UNIT 11 164 KESTRELLANE CLOSE MAPLE GREEN LONDON Unit 11, 164 Kestrellane Close, Maple Green, London splink: probabilistic match 55.3354 nan
m_0001609 c_0003965 c_0003965 Apt 15, 39 Kestrelmead Ave, West Alder, London APARTMENT 15 39 KESTRELMEAD AVENUE WEST ALDER LONDON Apt 15, 39 Kestrelmead Avenue, West Alder, London exact: full match nan nan
m_0000713 c_0006590 c_0006590 Unit 16, 63 Cliftongate Gdns, West Alder, London UNIT 16 63 CLIFTONGATE GARDENS WEST ALDER LONDON Unit 16, 63 Cliftongate Gardens, West Alder, London exact: full match nan nan
m_0001408 c_0005878 c_0005878 APT 17, 122 AMBERHURST LN, NEW HUXLEY, LONDON APARTMENT 17 122 AMBERHURST LANE NEW HUXLEY LONDON Apt 17, 122 Amberhurst Lane, New Huxley, London exact: full match nan nan
import duckdb
import os
import tempfile
from uk_address_matcher import (
    AddressMatcher,
    prepare_canonical_folder,
    ukam_datasets,
)

con = duckdb.connect()
df_messy = ukam_datasets.as_relation("fictional_london_messy", con=con)
df_canonical = ukam_datasets.as_relation("fictional_london_canonical", con=con)

# One-time preparation
output_folder = tempfile.mkdtemp()
prepare_canonical_folder(
    df_canonical,
    output_folder=output_folder,
    con=con,
    overwrite=True,
)

# Pass the folder path instead of a relation
matcher = AddressMatcher(
    canonical_addresses=output_folder,
    addresses_to_match=df_messy,
    con=con,
)
result = matcher.match()

print("Prepared folder contents:")
for f in sorted(os.listdir(output_folder)):
    print(f"  {f}")
print()
print(result.matches().limit(5).to_df().to_markdown(index=False))

Prepared folder contents: ukam_canonical_addresses.parquet ukam_inverted_index.parquet ukam_manifest.json ukam_term_frequencies.parquet

unique_id resolved_canonical_id ukam_label original_address_concat clean_full_address_canonical match_reason match_weight distinguishability
m_0001165 c_0008592 c_0008592 Suite 1, 171 Primrosemarket St, West Alder, London SUITE 1 171 PRIMROSEMARKET STREET WEST ALDER LONDON splink: probabilistic match 56.2066 nan
m_0001287 c_0002728 c_0002728 Unit 11,164 Kestrellane Cl,Maple Green,London UNIT 11 164 KESTRELLANE CLOSE MAPLE GREEN LONDON splink: probabilistic match 55.3354 nan
m_0001609 c_0003965 c_0003965 Apt 15, 39 Kestrelmead Ave, West Alder, London APARTMENT 15 39 KESTRELMEAD AVENUE WEST ALDER LONDON exact: full match nan nan
m_0000713 c_0006590 c_0006590 Unit 16, 63 Cliftongate Gdns, West Alder, London UNIT 16 63 CLIFTONGATE GARDENS WEST ALDER LONDON exact: full match nan nan
m_0001408 c_0005878 c_0005878 APT 17, 122 AMBERHURST LN, NEW HUXLEY, LONDON APARTMENT 17 122 AMBERHURST LANE NEW HUXLEY LONDON exact: full match nan nan

The output_folder contains parquet files plus ukam_manifest.json (package version, row counts, file hashes) for reproducibility.

Subsequent matching exercises that use the same canonical data can reuse this folder, skipping the prepare_canonical_folder step.

Reading results

matcher.match() returns a MatchResult object:

Property / method Returns
.matches() DuckDB relation with unique_id, resolved_canonical_id, match_reason, and more.
.match_metrics() Match-reason breakdown with counts and percentages.
.accuracy_analysis() Threshold-based accuracy analysis from labelled data (requires ukam_label in messy input).
Customising stages

The default pipeline is ExactMatchStageSplinkStage. Pass your own stages list to change behaviour:

from uk_address_matcher import (
    AddressMatcher,
    ExactMatchStage,
    PeeledAddressStage,
    UniqueTrigramStage,
    SplinkStage,
)

matcher = AddressMatcher(
    canonical_addresses=df_canonical,
    addresses_to_match=df_messy,
    con=con,
    stages=[
        ExactMatchStage(),
        PeeledAddressStage(),
        UniqueTrigramStage(),
        SplinkStage(
            final_match_weight_threshold=20.0,
            final_distinguishability_threshold=5.0,
        ),
    ],
)

Use AddressMatcher.available_stages() to discover registered stage classes. See Choosing a matching threshold and Optimising accuracy for further accuracy advice. The API reference covers the main API docs.

Using labelled data

If you know the correct match for each address, add a ukam_label column to your messy data. It propagates through to results, enabling accuracy analysis with MatchResult.accuracy_analysis().