Metadata-Version: 2.4
Name: tr-adres
Version: 0.1.1
Summary: Parse and normalise Turkish postal addresses: il, ilçe, mahalle, street, building and flat numbers, with a bundled gazetteer of all 81 provinces and 973 districts.
Author-email: Yusuf Eminoğlu <yusufeminoglu@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/YusufEminoglu/tr_adres_sdk
Project-URL: Repository, https://github.com/YusufEminoglu/tr_adres_sdk
Project-URL: Issues, https://github.com/YusufEminoglu/tr_adres_sdk/issues
Project-URL: Changelog, https://github.com/YusufEminoglu/tr_adres_sdk/blob/main/CHANGELOG.md
Keywords: turkish,turkey,turkiye,address,address-parser,address-normalization,adres,il,ilce,mahalle,postal-code,posta-kodu,gazetteer,geocoding,data-cleaning,logistics,e-commerce,nlp
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Natural Language :: Turkish
Classifier: Topic :: Text Processing :: Linguistic
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Office/Business
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: pandas
Requires-Dist: pandas>=1.3.0; extra == "pandas"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Dynamic: license-file

# tr-adres

[![PyPI](https://img.shields.io/pypi/v/tr-adres.svg)](https://pypi.org/project/tr-adres/)
[![Python](https://img.shields.io/pypi/pyversions/tr-adres.svg)](https://pypi.org/project/tr-adres/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**Türkçe posta adreslerini ayrıştırma ve standartlaştırma kütüphanesi** · *Parse and normalise
Turkish postal addresses* — saf Python, bağımlılıksız, 81 il ve 973 ilçelik gömülü sözlükle.

```python
>>> import tr_adres
>>> tr_adres.normalize("caferaga mh moda cd no 12 d 3 kadikoy istanbul")
'Caferağa Mah. Moda Cad. No:12 D:3 Kadıköy/İstanbul'

```

[Türkçe](#türkçe) · [English](#english)

---

## Türkçe

E-ticaret ödeme formları, kargo etiketleri ve CRM kayıtlarındaki serbest metin adresleri
bileşenlerine ayırır (il, ilçe, mahalle, cadde/sokak, bina no, kat, daire, site/apartman/blok,
posta kodu), yazım hatalarını ve ASCII yazımı düzeltir, tek satırlık standart biçime çevirir.

### Kurulum

```bash
pip install tr-adres            # yalnızca standart kütüphane
pip install "tr-adres[pandas]"  # pandas eklentisiyle
```

Python 3.9+ gerekir. Çalışma zamanı bağımlılığı yoktur.

### Hızlı başlangıç

```python
>>> from tr_adres import parse
>>> a = parse("caferaga mh. moda cd. no:12/3 kadikoy")
>>> a.mahalle, a.cadde, a.cadde_tipi, a.bina_no, a.daire_no
('Caferağa', 'Moda', 'Cadde', '12', '3')
>>> a.ilce, a.il, a.il_plaka
('Kadıköy', 'İstanbul', 34)
>>> a.notlar
['il, ilçeden çıkarıldı: İstanbul']
>>> str(a)
'Caferağa Mah. Moda Cad. No:12 D:3 Kadıköy/İstanbul'
>>> tr_adres.format(a, style="resmi")
'Caferağa Mahallesi, Moda Caddesi, No: 12, İç Kapı No: 3, Kadıköy/İSTANBUL'

```

### Önce / sonra

| Girdi | `normalize()` çıktısı |
|---|---|
| `caferaga mh moda cd no 12 d 3 kadikoy istanbul` | `Caferağa Mah. Moda Cad. No:12 D:3 Kadıköy/İstanbul` |
| `ISTANBUL BESIKTAS LEVENT MAH. BUYUKDERE CAD NO 185` | `Levent Mah. Büyükdere Cad. No:185 Beşiktaş/İstanbul` |
| `Kızılay Mah. Atatürk Bulvarı No:115 Kat:4 Çankaya/ANKARA` | `Kızılay Mah. Atatürk Bulv. No:115 K:4 Çankaya/Ankara` |
| `alsancak mah 1469 sk no 8 d 2 konak izmir` | `Alsancak Mah. 1469 Sk. No:8 D:2 Konak/İzmir` |
| `Bağdat Caddesi No:350 Daire:12 Kat:5 Suadiye Mahallesi Kadıköy İstanbul 34740` | `Suadiye Mah. Bağdat Cad. No:350 K:5 D:12 34740 Kadıköy/İstanbul` |
| `Yıldız Sit. B Blk. Kat 2 D:5, Çankaya / ANKARA` | `Yıldız Sitesi B Blok K:2 D:5 Çankaya/Ankara` |
| `Kadikoy Caferaga Mah Moda Cad 12/3` | `Caferağa Mah. Moda Cad. No:12 D:3 Kadıköy/İstanbul` |
| `Levent Mah. Büyükdere Cad. No:185 Besiktas/Istnbul` | `Levent Mah. Büyükdere Cad. No:185 Beşiktaş/İstanbul` |
| `Afyon Merkez Dumlupınar Mah. Kurtuluş Cad. No:7` | `Dumlupınar Mah. Kurtuluş Cad. No:7 Afyonkarahisar/Afyonkarahisar` |
| `Mahalle: Caferağa, Cadde: Moda, No: 5, İlçe: Kadıköy, İl: İstanbul` | `Caferağa Mah. Moda Cad. No:5 Kadıköy/İstanbul` |
| `Bostanlı Mah. 1203/2 Sk. No:6 D:3 Karşıyaka İzmir` | `Bostanlı Mah. 1203/2 Sk. No:6 D:3 Karşıyaka/İzmir` |
| `Caferağa Mah. Moda Cad. No:12 Migros karşısı Kadıköy` | `Caferağa Mah. Moda Cad. No:12 Migros karşısı Kadıköy/İstanbul` |
| `Moda Cad. No:5 34710` | `Moda Cad. No:5 34710 Kadıköy/İstanbul` |
| `100. Yıl Mah. 29 Ekim Cad. No:4 Bağlar/Diyarbakır` | `100. Yıl Mah. 29 Ekim Cad. No:4 Bağlar/Diyarbakır` |
| `Cumhuriyet Mah. Gazi Cad. No:15-A Kat: Zemin Bayraklı İzmir` | `Cumhuriyet Mah. Gazi Cad. No:15/A K:Zemin Bayraklı/İzmir` |
| `Hayrullah Mah. Trabzon Cad. No:20 Onikişubat/K.Maraş` | `Hayrullah Mah. Trabzon Cad. No:20 Onikişubat/Kahramanmaraş` |

`style="resmi"` her türü açık yazar ve ili büyük harfle verir:
`Suadiye Mahallesi, Bağdat Caddesi, No: 350, Kat: 5, İç Kapı No: 12, 34740 Kadıköy/İSTANBUL`.

### Neleri tanır?

- **Kısaltmalar**: `mah. mh. mahallesi`, `cad. cd. caddesi`, `sok. sk. sokak sokağı`,
  `bulv. blv. bulvarı`, `yolu`, `meydanı`, `çıkmazı`, `no: no numara #`, `d: daire iç kapı no`,
  `k: kat`, `apt. ap. apartmanı`, `sit. sitesi konutları evleri rezidans`, `plaza`,
  `iş merkezi`, `iş hanı`, `blk. bl. blok`, `köyü`, `pk posta kodu`; tek harflik yazım hatası
  (`Mahalesi`, `Cadesi`) da kabul edilir.
- **Numaralar**: `No:12/3` (bina 12, daire 3), `No:15/A`, `No:15A`, `No:15-A`, `3. Kat`,
  `Zemin Kat`, `Kat:-1`, `B2 Blok`, `Blok: A-1`, anahtar sözcüksüz `Moda Cad. 12/3`.
- **Sıra ve ayraçlar**: bileşenler herhangi bir sırada, virgüllü/virgülsüz, `/ - ; |` ya da
  satır sonlarıyla ayrılmış olabilir. `İstanbul Kadıköy Caferağa Mah.` gibi bitişik yazılan il
  ve ilçe mahalle adından ayrılır.
- **Türkçe büyük/küçük harf**: `İ/i` ve `I/ı` doğru dönüştürülür (`tr_lower`, `tr_upper`,
  `tr_title`). Tamamen ASCII yazılmış girdide (`Kadikoy`, `BUYUKDERE`) il/ilçe adları ve sık
  kullanılan sözcükler Türkçe yazımlarına geri döndürülür.
- **Yazım hataları**: il ve ilçe adları küçük bir düzenleme uzaklığıyla (Damerau-Levenshtein,
  4–7 harfte 1, 8+ harfte 2) eşleştirilir: `Istnbul`, `Besiktas`, `Cankya`, `Ankra`, `Diyarbakr`.
- **Tutarlılık**: il ile ilçe her zaman birbiriyle uyumludur (`Maltepe Çankaya/Ankara` →
  Maltepe İstanbul'da olduğu için kalan metne bırakılır). Tek ilde bulunan ilçeden il, posta
  kodunun ilk iki hanesinden (plaka) il çıkarılır; çok illi ilçe adları (`Yenişehir`) posta
  koduyla netleşir. `Merkez/Kütahya` → ilçe Kütahya.
- **Takma adlar**: `Afyon`, `K.Maraş`, `Urfa`, `Antep`, `İçel`, `İST`, `Eyüp`, `Kazan`,
  `Karadeniz Ereğli`…

### API

| İşlev | Açıklama |
|---|---|
| `parse(text) -> Adres` | Adresi ayrıştırır |
| `parse_many(iterable) -> list[Adres]` | Toplu ayrıştırma (`None`/NaN boş sonuç verir) |
| `normalize(text, style="kargo") -> str` | Standart tek satır |
| `format(adres, style="kargo"\|"resmi", include_remainder=True)` | `Adres`'i (ya da metni) biçimler |
| `il_bul(ad_veya_plaka)`, `ilce_bul(ad, il=None)`, `ilce_ara(ad, il=None)` | Sözlük araması |
| `plaka(il)`, `is_valid_ilce(il, ilce)`, `merkez_ilce(il)` | Plaka ve doğrulama |
| `posta_kodu_il(kod)`, `posta_kodu_ilceler(kod)` | Posta kodundan il/ilçe |
| `iller()`, `ilceler(il=None)`, `kaynak()` | Tüm liste ve veri kaynağı |
| `tr_lower`, `tr_upper`, `tr_title`, `ascii_fold`, `edit_distance` | Metin yardımcıları |

`Adres` alanları: `il, ilce, mahalle, koy, cadde, cadde_tipi (Cadde/Bulvar/Yol/Meydan),
sokak, sokak_tipi (Sokak/Çıkmaz), site, apartman, blok, bina_no, kat, daire_no, posta_kodu,
il_plaka, confidence (0–1), kalan (ayrıştırılamayan metin), notlar (düzeltme ve uyarılar),
girdi`; ayrıca `to_dict()`, `yol` ve `gecerli` özellikleri.

```python
>>> from tr_adres import il_bul, ilce_bul, ilce_ara, plaka, is_valid_ilce, tr_upper
>>> il_bul("Urfa").ad, il_bul(6).ad, plaka("İzmir")
('Şanlıurfa', 'Ankara', 35)
>>> [str(i) for i in ilce_ara("Yenişehir")]
['Yenişehir/Bursa', 'Yenişehir/Diyarbakır', 'Yenişehir/Mersin']
>>> is_valid_ilce("istanbul", "KADIKOY"), is_valid_ilce("Ankara", "Kadıköy")
(True, False)
>>> tr_upper("istanbul ığdır"), "istanbul ığdır".upper()
('İSTANBUL IĞDIR', 'ISTANBUL IĞDIR')

```

### Komut satırı

```bash
tr-adres parse "Caferağa Mah. Moda Cad. No:12/3 Kadıköy/İstanbul"     # JSON
tr-adres normalize "caferaga mh moda cd no 12 kadikoy"                 # tek satır
tr-adres normalize musteriler.csv --column adres -o temiz.csv          # adres_normalize sütunu eklenir
tr-adres parse musteriler.csv --column adres -o ayrik.csv              # adres_il, adres_ilce, ... sütunları
tr-adres normalize musteriler.csv -o temiz.csv --output-encoding utf-8-sig   # Excel için BOM
cat adresler.txt | tr-adres normalize -                                # satır satır
```

CSV ayırıcısı (`,` `;` sekme `|`) otomatik algılanır; girdi kodlaması varsayılan olarak
`utf-8-sig`'dir. `--style resmi`, `--prefix`, `--output-column`, `--delimiter` seçenekleri
vardır.

### pandas

```python
import pandas as pd
import tr_adres.pandas_ext  # ya da: pandas'tan sonra `import tr_adres`

df = pd.read_csv("musteriler.csv")
parcalar = df["adres"].tradres.parse()          # il, ilce, mahalle, ... sütunlu DataFrame
df["adres_temiz"] = df["adres"].tradres.normalize()
```

### Veri kaynağı

- **İl ve ilçe adları**: T.C. İçişleri Bakanlığı, *Mülki İdare Bölümleri Envanteri* — 81 il,
  973 ilçe. Liste, bu envanteri kaynak gösteren Türkçe Vikipedi tablosundan
  ("Türkiye'nin ilçeleri", revizyon 37373921, erişim 2026-11-08) derlenmiştir. Zonguldak'taki
  ilçe, makale başlığı "Karadeniz Ereğli" yerine resmî adıyla **Ereğli** olarak kaydedilmiştir
  (eski ad takma ad olarak tanınır).
- **Plaka kodları**: kanunla belirlenmiş 01–81.
- **Posta kodları**: PTT posta kodu sorgusundan, her ilçe için en küçük–en büyük aralık.
  İlk iki hanesi plakayla çelişen 5 kayıt (Kemalpaşa/Artvin, Baklan, Yağlıdere, Menderes,
  Arpaçay) atılmıştır; bu ilçelerde posta kodu aralığı boştur.
- Veri `src/tr_adres/data/gazetteer.json` içinde (~30 KB) paketle gelir;
  `tools/build_gazetteer.py` sabitlenmiş revizyondan yeniden üretir. Bunlar resmî idari
  adlar ve kodlardır; kaynak gösterilmesi için `tr_adres.kaynak()` kullanılabilir.

### Doğruluk

Testlerde üç ayrı, tamamen sentetik (gerçek kişi ya da teslimat adresi içermeyen) derlem vardır;
her alan (il, ilçe, mahalle, cadde+tipi, sokak+tipi, site, apartman, blok, bina, kat, daire,
posta kodu, kalan) birebir karşılaştırılır:

| Derlem | Adres | İlk çalıştırma | Şu an |
|---|---|---|---|
| Geliştirme (`tests/corpus.py`) | 191 | 191/191 tam (bir beklenti düzeltildi) | 191/191 |
| Ayrılmış (`HELD_OUT`) | 40 | 40/40 tam | 40/40 |
| Kör (`tests/blind.py`, uygulamaya bakmadan ayrı yazıldı) | 60 | **53/60 tam, alan doğruluğu %98,9** | 60/60 |

Kör derlemdeki 7 hatadan 5'i düzeltildi, 2'si beklenti farkıydı ("Merkez" ilçesini merkez
ilçenin adına çevirme kuralı). İlk çalıştırma sonuçları dürüst tahmindir; derlemler
geliştirmede kullanıldığı için "şu an" sütunu genelleme göstermez. Gerçek veride doğruluk,
yazım biçimine göre daha düşük olabilir.

### Sınırlamalar

- **Mahalle doğrulanmaz.** Açık lisanslı, güncel bir mahalle listesi bulunamadığından mahalle
  yalnızca `Mah./Mahallesi` anahtar sözcüğüyle tanınır ve bir ilçeye ait olup olmadığı
  denetlenmez. Anahtar sözcüksüz yazılan semt/mahalle adları (`Moda`, `Ulus`) `kalan`'a düşer.
- **Aksan geri yükleme sınırlıdır.** ASCII girdide yalnızca il/ilçe adları ve küçük bir sık
  sözcük listesi düzeltilir; `Kayabasi` gibi diğer adlar olduğu gibi kalır.
- **ASCII büyük harf**: Türkçe karakter içermeyen girdide `I` → `i` kabul edilir
  (`BILGI` → `Bilgi`); Türkçe karakter içeren girdide `I` noktasızdır (`KIZILIRMAK` →
  `Kızılırmak`).
- **Bulanık ilçe eşleşmesi** yalnızca il ya da posta koduyla doğrulanırsa kabul edilir
  (`Göztepe`, Kırşehir'in `Boztepe` ilçesine 1 harf uzaktadır).
- **Posta kodu aralıkları** büyükşehirlerde örtüşür; ilçe yalnızca kod tek bir ilçenin
  aralığına düşüyorsa çıkarılır.
- Mahalle adının başındaki il/ilçe adı ayrılır; ilçe başka yerde yazılmamışsa
  `Fatih Sultan Mehmet Mah.` gibi bir ad yine doğru kalır (`Sultan`, `Paşa`, `Bey`... sonrası
  bölünmez), ama her olası çakışma önlenemez.
- `confidence` sezgiseldir (bulunan bileşenler, düzeltmeler, çıkarımlar ve kalan sözcükler);
  olasılık değildir. Saniyede yaklaşık 1000 adres işlenir (tek çekirdek).
- Kural tabanlıdır: posta kutusu, yurt dışı ve serbest tarif adresleri ("caminin arkası")
  kapsam dışıdır.

---

## English

`tr-adres` turns free-text Turkish addresses from checkout forms, shipping labels and CRM
exports into structured fields and a canonical one-line form. Pure Python, no runtime
dependencies, ships a gazetteer of all **81 provinces (il) with plate codes and 973 districts
(ilçe)** with PTT postal-code ranges.

```python
>>> import tr_adres
>>> a = tr_adres.parse("ISTANBUL BESIKTAS LEVENT MAH. BUYUKDERE CAD NO 185")
>>> a.il, a.ilce, a.mahalle, a.cadde, a.bina_no
('İstanbul', 'Beşiktaş', 'Levent', 'Büyükdere', '185')
>>> tr_adres.normalize("Kızılay Mah. Atatürk Bulvarı No:115 Kat:4 Çankaya/ANKARA", style="resmi")
'Kızılay Mahallesi, Atatürk Bulvarı, No: 115, Kat: 4, Çankaya/ANKARA'

```

**What it handles**: all common abbreviations (`mah. mh. cad. cd. sok. sk. bulv. blv. no: d:
k: kat apt. ap. blk. sit.` …), Turkish casing (`İ/i`, `I/ı` — `tr_lower`, `tr_upper`,
`tr_title`), ASCII-folded input (`Kadikoy` → Kadıköy), typos in province/district names
(bounded Damerau-Levenshtein), arbitrary component order, missing commas, `/` separators
(`No:12/3` = building 12, flat 3), İzmir-style numbered streets (`1203/2 Sk.`), floors
(`3. Kat`, `Zemin Kat`, `Kat:-1`), blocks (`B2 Blok`), labelled forms (`İl: … İlçe: …`),
postal codes (province from the first two digits, district from PTT ranges when unambiguous),
and colloquial names (`Afyon`, `K.Maraş`, `Urfa`, `Antep`, `İçel`). Province and district are
always mutually consistent; a district that does not belong to the province is left in the
remainder. Anything not understood goes to `kalan`, and every correction or inference is
recorded in `notlar`.

**API** — `parse`, `parse_many`, `normalize`, `format(adres, style="kargo"|"resmi")`,
`il_bul`, `ilce_bul`, `ilce_ara`, `plaka`, `is_valid_ilce`, `merkez_ilce`, `posta_kodu_il`,
`posta_kodu_ilceler`, `iller`, `ilceler`, `kaynak`, `tr_lower`, `tr_upper`, `tr_title`,
`ascii_fold`, `edit_distance`. See the Turkish section above for the field list.

**CLI** — `tr-adres parse "…"` prints JSON; `tr-adres normalize file.csv --column adres -o
out.csv` adds an `adres_normalize` column (`tr-adres parse file.csv` adds `adres_il`,
`adres_ilce`, …). Delimiters are sniffed; use `--output-encoding utf-8-sig` for Excel.

**pandas** — `pip install "tr-adres[pandas]"`, then `import tr_adres.pandas_ext` (or import
`tr_adres` after pandas) and use `df["adres"].tradres.parse()` / `.normalize()`.

**Data** — district names from the Ministry of Interior inventory (via the Turkish Wikipedia
table that cites it, pinned revision 37373921), plate codes 01–81, PTT postal-code envelopes;
counts verified by the test suite (81 / 973, unique plates). The Zonguldak district is stored
under its official name *Ereğli* ("Karadeniz Ereğli" is accepted as an alias).

**Accuracy** — on a blind, synthetic 60-address set written without access to the code, the
first run scored 53/60 fully correct addresses and 98.9 % field accuracy; all three test
corpora (291 addresses) now pass. These are synthetic addresses, so expect lower accuracy on
real-world data.

**Limitations** — neighbourhoods (mahalle) are recognised by keyword only and are *not*
validated (no permissively licensed mahalle list was available); diacritics are restored for
province/district names and a small common-word list only; in all-ASCII input `I` is read as
`i`; a typo-matched district is accepted only when confirmed by the province or the postal
code; postal-code ranges overlap in metropolitan areas; `confidence` is a heuristic score,
not a probability. Throughput is about 1,000 addresses per second on one core.

## Lisans / License

MIT © Yusuf Eminoğlu. Bkz. / see [LICENSE](LICENSE) ve atıf için / and for citation
[CITATION.cff](CITATION.cff).
