fair-ly accurate text synonyms for data cleaning
fairly collapses spelling and format variants of FAIR / DataCite metadata
field values onto a single canonical form — fairly accurately — so that
Univ. of California, Berkeley, University of California-Berkeley and
UC Berkeley all become one publisher/affiliation, while genuinely different
entities (University of Washington vs Washington University) stay apart.
It packages four cooperating strategies, each earning its place against a real failure mode:
| Strategy | What it does | Guards against |
|---|---|---|
| Synonym-lustre | embed distinct values (gte-large) → cluster (HDBSCAN) → map each cluster to its most-frequent member |
spelling/format/punctuation variants |
| Abbreviation holdout | keep acronyms and < 4-char tokens out of clustering |
ZHAW → Z, LUH → HU over-merges |
| ROR authority split | split a cluster that spans two real institutions using a local ROR dump | University of Washington vs Washington University |
| Geocoding | for place fields, geocode and group by (country, city) → "City, Country" |
Graz merging into Vienna ("city in Austria") |
| Controlled vocab | for fixed-vocabulary fields (licenses, codes), deterministically fold + alias-resolve to a canonical id | CC-BY-SA collapsing into CC-BY-NC — clustering can't tell them apart |
Quality is measured, not assumed: a V-measure harness tunes the clustering threshold per field against a small gold set.
Method background and per-field recommendations: docs/methods.md.
Install
pip install fair-ly-accurate # core (cleaners, ROR index, I/O, V-measure math)
pip install "fair-ly-accurate[cluster]" # + embeddings & HDBSCAN (semantic clustering)
pip install "fair-ly-accurate[geo]" # + geopy (place geocoding)
pip install "fair-ly-accurate[ror]" # + requests (faster ROR dump auto-download)
pip install "fair-ly-accurate[all]" # everything
(The distribution installs as fair-ly-accurate; the import name is fairly: from fairly import Normalizer.)
The heavy ML/geo dependencies are optional extras, so the deterministic cleaners, ROR index and field-path I/O install light.
Quickstart (CLI)
# 0. (once, for institution fields) build a ROR index.
# --latest auto-downloads the newest ROR v2 dump from Zenodo:
fairly build-ror-index --latest --out ror_index.pickle
# ...or point at a dump you already have:
fairly build-ror-index --dump v2-ror-data.json --out ror_index.pickle
# 1. tune the threshold for a field against a gold set (variant<TAB>true_canonical)
fairly validate --gold affiliation_gold.tsv --preset affiliation --field affiliation
# 2. build + apply a synonym map to a DataCite-shaped field, with review + ROR split
fairly normalize \
--input records.jsonl \
--field-path 'creators[].affiliation[].name' \
--preset affiliation \
--ror-index ror_index.pickle \
--review affiliation_review.tsv \
--output records.normalized.jsonl
The --field-path understands dotted paths through nested dicts and lists, e.g.
publisher.name, fundingReferences[].funderName, subjects[].subject,
creators[].affiliation[].name. Tabular inputs use the column name as the path.
Always eyeball the --review TSV before trusting an apply — especially for
homogeneous fields like funders, where over-merge is easy. Add --dry-run to
build the map and review without writing.
Quickstart (library)
from fairly import Normalizer, get_preset
from fairly.authorities.ror import RorIndex
from fairly.io import collect_counts, read_records, map_values
records = read_records("records.jsonl")
counts = collect_counts(records, "fundingReferences[].funderName")
norm = Normalizer(get_preset("funderName"), cache_dir=".cache")
smap = norm.build(counts, authority=RorIndex.load("ror_index.pickle"))
smap.write_review("funder_review.tsv", counts) # review gate
for rec in records:
map_values(rec, "fundingReferences[].funderName", lambda v: norm.normalize(v, smap))
Field presets
Built-in FieldConfig presets for common DataCite/FAIR fields (fairly presets):
| Field | Strategy | eps | Holdout | Authority |
|---|---|---|---|---|
publisher |
semantic | 0.30 | yes | ROR |
funderName |
semantic | 0.07 | yes | ROR |
affiliation |
semantic | 0.07 | yes | ROR |
subject |
semantic | 0.10 | yes | — |
rights |
controlled | — | — | SPDX/CC vocab |
geoLocationPlace |
geocode | — | — | — |
These eps values are V-measure validated against the curated gold sets in
gold/ — each sits on its field's optimal plateau (funder/subject 1.00,
publisher 0.99, affiliation 0.96; rights 0.82, so prefer a controlled vocabulary
for licenses). Re-tune for your data with fairly validate — see
docs/methods.md for the full sweep and why
each field wants a different threshold.
Why these choices
- Most-frequent canonical gives clean, recognizable labels (the common form wins), with centroid-closeness only breaking ties.
- Holdout exists because short/all-caps tokens carry too little signal for an embedding to place; left in, they become spurious join keys.
- Authority split is what lets us cluster aggressively for coverage without paying for it in precision: the embedding proposes merges, ROR/geocoding vetoes the wrong ones.
- V-measure turns "did we over-merge?" into a number: homogeneity penalizes over-merge (the costly direction), completeness penalizes under-merge.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fair_ly_accurate-0.4.0.tar.gz.
File metadata
- Download URL: fair_ly_accurate-0.4.0.tar.gz
- Upload date:
- Size: 45.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1179eb121ce39696390409fcb34eccbba90e8f1696931ffde5d13494ba222dea
|
|
| MD5 |
3ecb159b2f0c0d7af4a1931d9446fd6b
|
|
| BLAKE2b-256 |
09892ca0bf7f512020bb8260ba7343271b869af872d429d9db201f9a9b0ee40e
|
Provenance
The following attestation bundles were made for fair_ly_accurate-0.4.0.tar.gz:
Publisher:
publish.yml on fairdataihub/fair-ly-accurate-text-synonyms-for-data-cleaning
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fair_ly_accurate-0.4.0.tar.gz -
Subject digest:
1179eb121ce39696390409fcb34eccbba90e8f1696931ffde5d13494ba222dea - Sigstore transparency entry: 2618844471
- Sigstore integration time:
-
Permalink:
fairdataihub/fair-ly-accurate-text-synonyms-for-data-cleaning@3c76c5ad7c16e25aa265be3342f39cce5830bca5 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/fairdataihub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3c76c5ad7c16e25aa265be3342f39cce5830bca5 -
Trigger Event:
release
-
Statement type:
File details
Details for the file fair_ly_accurate-0.4.0-py3-none-any.whl.
File metadata
- Download URL: fair_ly_accurate-0.4.0-py3-none-any.whl
- Upload date:
- Size: 35.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
36261d78beba0b67904c09c782f98fb9b07230fa04d4853e5371bdc55ecda3bf
|
|
| MD5 |
21404c12df898cbf4458812917c5a99b
|
|
| BLAKE2b-256 |
c0b8161b0f75cacf98ae22f95b201021d799a6640569cfb4c2e95e5d6cf7be5f
|
Provenance
The following attestation bundles were made for fair_ly_accurate-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on fairdataihub/fair-ly-accurate-text-synonyms-for-data-cleaning
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fair_ly_accurate-0.4.0-py3-none-any.whl -
Subject digest:
36261d78beba0b67904c09c782f98fb9b07230fa04d4853e5371bdc55ecda3bf - Sigstore transparency entry: 2618844483
- Sigstore integration time:
-
Permalink:
fairdataihub/fair-ly-accurate-text-synonyms-for-data-cleaning@3c76c5ad7c16e25aa265be3342f39cce5830bca5 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/fairdataihub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3c76c5ad7c16e25aa265be3342f39cce5830bca5 -
Trigger Event:
release
-
Statement type: