Skip to main content

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

fair_ly_accurate-0.4.0.tar.gz (45.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fair_ly_accurate-0.4.0-py3-none-any.whl (35.2 kB view details)

Uploaded Python 3

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

Hashes for fair_ly_accurate-0.4.0.tar.gz
Algorithm Hash digest
SHA256 1179eb121ce39696390409fcb34eccbba90e8f1696931ffde5d13494ba222dea
MD5 3ecb159b2f0c0d7af4a1931d9446fd6b
BLAKE2b-256 09892ca0bf7f512020bb8260ba7343271b869af872d429d9db201f9a9b0ee40e

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fair_ly_accurate-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for fair_ly_accurate-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 36261d78beba0b67904c09c782f98fb9b07230fa04d4853e5371bdc55ecda3bf
MD5 21404c12df898cbf4458812917c5a99b
BLAKE2b-256 c0b8161b0f75cacf98ae22f95b201021d799a6640569cfb4c2e95e5d6cf7be5f

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 files

0.3.0

2 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