Indonesian location extractor (gazetteer + optional NER)
Project description
Location NER (Indonesia) — Gazetteer + NER Assisted
This repo extracts Indonesian administrative locations (province / city / district / subdistrict) from free text without retraining a model.
Core idea:
- Use a Kemendagri-style gazetteer (CSV) for structured, code-based results.
- Use spaCy PhraseMatcher for fast exact matching.
- Use optional HuggingFace NER as a semantic helper (gating + linking), especially for messy/long text.
Features
- Gazetteer loader that deduplicates by admin codes (prevents duplicate candidates).
- Exact matching with
spaCyPhraseMatcher(case-insensitive). - Typo-tolerant matching (
--fuzzy) usingrapidfuzz. - Context-aware disambiguation for common prefixes (e.g.,
Kel.vsKec.). - NER-gated matching (
--ner-filter) to reduce false positives with national gazetteers. - HF-assisted recovery for addresses that omit
Kelurahan/Desa(e.g., "..., Sumber Rejo, Kecamatan Pakal, ..."). - Robust HuggingFace handling for very long texts via chunked inference (no 512-token crash).
final_result: resolves ambiguous mentions into a single best administrative path (conservative: doesn’t force uncertain subdistrict).
Why gazetteer/phrase-matcher?
- Fast to iterate: update the CSV → results update immediately
- High precision for known locations
- No costly retraining cycle
Trade-off: recall depends on how complete your location list is and how many spelling variants you handle.
Dataset format
Example CSV: gazetteer/csv/location_kemendagri_2025.csv
This location is collected from : https://github.com/cahyadsn/wilayah (thanks bro)
Required columns:
province_name,province_codecity_name,city_codedistrict_name,district_codesubdistrict_name,subdistrict_code- (optional)
postal_code
Install
Requirements: Python 3.10+
Option A: editable install (recommended)
python3 -m pip install -U pip
python3 -m pip install -e .
Install from GitHub
If you want to use it immediately without a PyPI release:
python3 -m pip install "git+https://github.com/vinbyte/id-location-ner.git"
Optional: HuggingFace NER support
python3 -m pip install -e ".[hf]"
Dev shortcuts
make smokeruns a quick gazetteer-only check.make smoke-hfruns a quick HF + fuzzy + NER-gate check.
Quick start
1) Gazetteer-only extraction (no transformer)
location-ner \
--csv gazetteer/csv/location_kemendagri_2025.csv \
--text "Alamat. Perum Prana Estate Blok A5/1 Kel. Cikole Kec. Cikole, Kota Sukabumi, Jawa Barat 43115."
2) HF-assisted extraction (recommended for messy / long text)
This runs HuggingFace NER + linking:
location-ner \
--csv gazetteer/csv/location_kemendagri_2025.csv \
--hf-model cahya/bert-base-indonesian-NER \
--text "... Kecamatan Pakal, Surabaya, Jawa Timur ..."
3) Typo-tolerant matching
location-ner \
--csv gazetteer/csv/location_kemendagri_2025.csv \
--fuzzy --fuzzy-threshold 90 \
--text "... Kelurahan Pangongngan, Kecamatan Manguharjo, Kota Mediun ..."
4) NER-gated gazetteer matching (recommended for national gazetteers)
With a full Indonesia gazetteer, many short/common words can be valid location names somewhere (e.g., "Raya"), causing false positives.
--ner-filter uses the HuggingFace NER model as a semantic gate: exact PhraseMatcher
matches are kept only if they overlap a model-predicted location span (LOC/GPE).
location-ner \
--csv gazetteer/csv/location_kemendagri_2025.csv \
--hf-model cahya/bert-base-indonesian-NER \
--ner-filter --ner-filter-min-score 0.5 \
--text "... JAWA BARAT, KOTA SUKABUMI, Cikole, Cisarua ..."
5) Compact output (one line JSON)
location-ner --csv gazetteer/csv/location_kemendagri_2025.csv --text "..." --jsonl
Output
The CLI prints JSON with:
gazetteer_mentions: all matched spans (can be ambiguous)final_result: a single best administrative path (API-friendly)hf_location_mentions: only when--hf-modelis enabled
final_result shape:
province_name,province_codecity_name,city_codedistrict_name,district_codesubdistrict_name,subdistrict_code
Resolver behavior:
- Conservative: deeper levels (especially subdistrict) are included only when supported.
Mention fields:
match_method:exact|fuzzyfuzzy_match_score: only set for fuzzy matcheslevel_hint: may be inferred from text prefixes (e.g.,Kel.vsKec.)
Notes / Limitations
- Ambiguity: some names exist in multiple regions; the extractor returns multiple candidates.
- Variants: the matcher includes a few basic normalization variants (case, punctuation, hyphens). If your real data has many aliases, add a synonym layer.
- For very large gazetteers (Indonesia full), PhraseMatcher can still work but you should profile memory/time; in some cases, a trie-based matcher or a database-backed lookup is better.
Examples
- FastAPI server example:
examples/api_server_fastapi/README.md
Project layout
src/location_ner/gazetteer.py: CSV loader + normalized lookup (+ fuzzy indexes)src/location_ner/extractor.py: spaCy PhraseMatcher extractor (+ fuzzy + context hints)src/location_ner/hf_ner.py: HuggingFace NER wrapper (chunked for long text)src/location_ner/hf_assisted.py: HF-assisted heuristics (pre-"Kecamatan" subdistrict)src/location_ner/resolver.py: final result resolversrc/location_ner/cli.py: command-line interface
Project details
Release history Release notifications | RSS feed
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 id_location_ner-0.1.0.tar.gz.
File metadata
- Download URL: id_location_ner-0.1.0.tar.gz
- Upload date:
- Size: 25.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5d052839b668a8ce2e083aceb720268b08bdee474fc307c7e618eccf9b11b46
|
|
| MD5 |
033468c6e4a122f4bc26a1a1e2b05eb4
|
|
| BLAKE2b-256 |
03ae66567465b56fe4e36c57686d7f83320a84c192f196535fced2492412ba9f
|
Provenance
The following attestation bundles were made for id_location_ner-0.1.0.tar.gz:
Publisher:
publish-pypi.yml on vinbyte/id-location-ner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
id_location_ner-0.1.0.tar.gz -
Subject digest:
d5d052839b668a8ce2e083aceb720268b08bdee474fc307c7e618eccf9b11b46 - Sigstore transparency entry: 813551319
- Sigstore integration time:
-
Permalink:
vinbyte/id-location-ner@4cfbf3955eaa0ccbe7cc6a4143e5eaca37226ed7 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/vinbyte
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@4cfbf3955eaa0ccbe7cc6a4143e5eaca37226ed7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file id_location_ner-0.1.0-py3-none-any.whl.
File metadata
- Download URL: id_location_ner-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
76336392c1b960c36b78f703eed1bbb8e0f7df95dae50348ff9bf04e40caa410
|
|
| MD5 |
5f9f4b86b2e481392e1bc03af87c32be
|
|
| BLAKE2b-256 |
c8f6198e4498224f3fa3244d2ba3d82bd6c8320749c199708bb275c541cc42a6
|
Provenance
The following attestation bundles were made for id_location_ner-0.1.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on vinbyte/id-location-ner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
id_location_ner-0.1.0-py3-none-any.whl -
Subject digest:
76336392c1b960c36b78f703eed1bbb8e0f7df95dae50348ff9bf04e40caa410 - Sigstore transparency entry: 813551320
- Sigstore integration time:
-
Permalink:
vinbyte/id-location-ner@4cfbf3955eaa0ccbe7cc6a4143e5eaca37226ed7 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/vinbyte
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@4cfbf3955eaa0ccbe7cc6a4143e5eaca37226ed7 -
Trigger Event:
push
-
Statement type: