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[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 from a ROR v2 dump
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.3.0.tar.gz (41.8 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.3.0-py3-none-any.whl (32.9 kB view details)

Uploaded Python 3

File details

Details for the file fair_ly_accurate-0.3.0.tar.gz.

File metadata

  • Download URL: fair_ly_accurate-0.3.0.tar.gz
  • Upload date:
  • Size: 41.8 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.3.0.tar.gz
Algorithm Hash digest
SHA256 62e30383721460037bfee1da824ff11a0bfda03d0cc3da5e942e75b5bea7675c
MD5 fe867b3516ec0c993a23c793b1d36410
BLAKE2b-256 19fc9aa939f5b6e4053d88a5fd1a7e0bee32a714aaa598ed0a609f2ab9b164b4

See more details on using hashes here.

Provenance

The following attestation bundles were made for fair_ly_accurate-0.3.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.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for fair_ly_accurate-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 089d8ab93336a880d4d9cb570f1b4f152c4527c42e4f3d9d83b8e49751115774
MD5 085e924a6574878e2e3d08d0794ebc51
BLAKE2b-256 e25bc5a1e4e63f4adf9aeeeb20a97dd27b1fc84ebb6920e47494a012a56d8e2b

See more details on using hashes here.

Provenance

The following attestation bundles were made for fair_ly_accurate-0.3.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

0.4.0

2 files

This release

0.3.0 This release

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