---
title: 'Veri Okuma ve Yazma'
source: 'https://academia.sh/tr/kurslar/python-ile-analiz/veri-okuma-ve-yazma'
course: 'Python ile Veri Analizi'
language: tr
updated: '2026-08-17T18:08:43+00:00'
license: 'CC BY-SA 4.0'
---

# Veri Okuma ve Yazma

3.199 satır ve 7 sütunluk 139.238 baytlık bir dosya tek çağrıyla okunuyor ve tip çıkarımı 7 sütunun 3'ünü yanlış tiple veriyor: abone numarası tam sayı olduğu için ortalaması 10.690,5 diye hesaplanabiliyor, tarife basamağının ortalaması 27,4 m³ çıkıyor, endeks sütunundaki 3.008 sayısal değer 80 'yok' yüzünden metne dönüyor ve sütunun en büyük değeri 'yok' oluyor. Çıkarım bütün satırlar yerine ilk 100 satırdan yapılınca geçiş sayısı 22.393'ten 700'e iner, ama sayaç numarası tam sayıya kayar: 2.077. satırda başlayan harfli kodların 1.123 hücresi hiçbir uyarı üretmeden eksiğe döner ve gidiş dönüşte 3.199 hücre değişir. Biçim kaybı her iki çıkarımda da 422 hücrededir.

Bir önceki derste küme kod içinde üretildi ve her sütunun tipi yazıldığı yerde belliydi. Gerçek bir
küme ise dosyadan gelir ve ayırıcıyla bölünmüş bir dosyada tip diye bir şey yoktur: her hücre bir
metin parçasıdır. `10001` ile `"10001"` arasındaki farkı dosya taşımaz, okuyan taraf **üretir**.

Bu üretim işine tip çıkarımı denir ve bir veri çerçevesi arayüzünün en sessiz kararıdır. Tek bir
okuma çağrısı yazılır, karşılığında sütunları tiplenmiş bir çerçeve gelir ve hangi sütunun neden o
tipe düştüğü hiçbir yerde görünmez. Veri Toplama ve Hazırlama kursunun yapısal ayrıştırma dersi
alanın **yerinin** kaymasını ölçmüştü; orada seçici yanlış alandan okuyordu. Burada alan yerindedir
ve kayan şey **tiptir**. Bu dersin ölçüsü iki sayıdır: çıkarım kaç sütunu yanlış tiple verdi ve
aynı dosya okunup geri yazıldığında kaç hücre değişti.

- **VC6.** Dosya biçimi tip taşımaz. Ayırıcıyla bölünmüş bir dosyada her hücre metindir; tip,
  okuma anında **kararla** üretilir. Küme kurgudur ve `python` ile, yalnız standart kitaplıkla
  modellenir.
- **VC7. Tip çıkarımı**, bir sütunun bütün değerleri aynı tipe ayrışıyorsa o tipi seçmektir. Tek
  bir değer ayrışmazsa sütunun tamamı metne düşer; kural sütun genelindedir, hücre başına değildir.
- **VC8. Yanlış tip** burada işlemsel tanımlanır: tip hatası vermeyen ama anlamsız bir sonuç
  üreten ya da meşru bir işlemi sessizce boş döndüren tip yanlıştır.
- **VC9. Gidiş dönüş denetimi**, aynı dosyanın okunup geri yazılmasıdır. Değişen hücre sayısı,
  okuma kararının kaybettiği bilgiyi doğrudan sayar.

## Dosyada Tip Yoktur

```python
# kume.py — MODELDIR: bir onceki dersin uzun bicimli okuma tablosu ayni tohumla
# yeniden uretilir. Kume kurgudur; uretecin ayrintisi konunun ilk dersindedir.
import math

M32 = 0xFFFFFFFF
TOHUM, DONEM = 20260218, ["2026-01", "2026-02", "2026-03"]
BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26),
         ("bati", 0.14, 14), ("merkez", 0.18, 23)]

def uretec(tohum: int):
    s = ((tohum ^ (tohum >> 16)) * 2246822507) & M32
    s = ((s ^ (s >> 13)) * 3266489909) & M32
    s ^= s >> 16
    def sonraki() -> float:
        nonlocal s
        s = (s * 1664525 + 1013904223) & M32
        return s / 4294967296
    return sonraki

def ayrik(u: float, agirlik: list[float]) -> int:
    t = 0.0
    for i, p in enumerate(agirlik):
        t += p
        if u < t:
            return i
    return len(agirlik) - 1

abone, elenen = [], []
for i in range(1400):
    r = uretec(TOHUM + i)
    ad, _, taban = BOLGE[ayrik(r(), [b[1] for b in BOLGE])]
    r(); r()  # hane ve memnuniyet cekilisleri bu derste kullanilmiyor
    (elenen if r() < 0.046 else abone).append(
        {"abone_no": 10001 + i, "bolge": ad, "taban": taban})

gecerli = {k["abone_no"] for k in abone}
cift: dict[tuple[int, str], float] = {}
for k in abone + elenen:
    r = uretec(TOHUM + 7000 + k["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(k["taban"] * math.exp(
            (r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100 + 0.5) / 100
        if k["abone_no"] in gecerli:
            cift.setdefault((k["abone_no"], DONEM[d]), t)
        r()
UZUN = [{"abone_no": a, "donem": d, "tuketim_m3": t} for (a, d), t in cift.items()]

import csv, os

SUTUN = ["abone_no", "sayac_no", "donem", "endeks", "tuketim_m3", "tarife_kodu", "bolge"]
bolge_of = {k["abone_no"]: k["bolge"] for k in abone}

def sayac_kodu(no: int) -> str:
    """Numaralandirma semasi 10900'uncu aboneden sonra harfli koda gecti."""
    return f"A{no - 9000:05d}" if no >= 10900 else f"{no - 9000:06d}"

def endeks_hucresi(no: int, d: int, kumul: float) -> str:
    """Saha formundan gelen endeks: bos birakilmis ya da 'yok' yazilmis olabilir."""
    u = uretec(TOHUM + 9100 + no * 4 + d)()
    return "" if u < 0.033 else "yok" if u < 0.055 else str(100000 + (no - 10000) * 37 + int(kumul))

satirlar, kumul = [], {}
for o in UZUN:
    no, t = o["abone_no"], o["tuketim_m3"]
    kumul[no] = kumul.get(no, 0.0) + t
    satirlar.append([str(no), sayac_kodu(no), o["donem"],
                     endeks_hucresi(no, DONEM.index(o["donem"]), kumul[no]), f"{t:.2f}",
                     "10" if t <= 10 else "25" if t <= 25 else "40", bolge_of[no]])

with open("okuma.csv", "w", newline="") as f:
    yazici = csv.writer(f)
    yazici.writerow(SUTUN)
    yazici.writerows(satirlar)
print(f"okuma.csv: {len(satirlar)} satir x {len(SUTUN)} sutun = "
      f"{len(satirlar) * len(SUTUN)} hucre, {os.path.getsize('okuma.csv')} bayt")
print(f"endeks sutunu: {sum(1 for s in satirlar if s[3] == '')} bos, "
      f"{sum(1 for s in satirlar if s[3] == 'yok')} 'yok' yazili")
ilk = next(i for i, s in enumerate(satirlar) if s[1].startswith("A"))
print(f"sayac_no sutununda ilk harfli kod {ilk + 1}. satirda: {satirlar[ilk][1]}")
```

```
okuma.csv: 3199 satir x 7 sutun = 22393 hucre, 139238 bayt
endeks sutunu: 111 bos, 80 'yok' yazili
sayac_no sutununda ilk harfli kod 2077. satirda: A01900
```

Dosya yedi sütun taşıyor ve üçü çıkarımı zorlayacak biçimde kurulmuştur. Sayaç numarası sıfırla
doldurulmuş altı haneli bir koddur, ama numaralandırma şeması bir noktada değişmiş ve 10.900'üncü
aboneden sonrası harfle başlayan bir kod almıştır; dosya abone numarasına göre sıralı olduğu için
ilk harfli kod ancak **2.077. satırda** görünür. Endeks sütunu saha formundan gelir ve iki ayrı
eksiklik biçimi taşır: 111 hücre boştur, 80 hücreye elle "yok" yazılmıştır. Tarife basamağı bir
kategoridir ama rakamla kodlanmıştır.

Bunların hiçbiri bozuk veri değildir. Üçü de sahada geçerli, kaynağında anlamlı ve okunabilir
kayıtlardır. Sorun, hepsinin tek bir çıkarım kuralının önünden geçecek olmasıdır.

Yazma tarafı da aynı sessizlikte karar verir. Bir çerçeve dosyaya yazılırken en az dört şey seçilir:
alan ayırıcısı, ondalık ayırıcısı, ayırıcı içeren değerin nasıl tırnaklanacağı ve eksik değerin
hangi metinle temsil edileceği. Bu derste eksik değer boş hücre olarak yazılır; bu, eksikliğin
kaynaktaki bütün biçimlerini tek bir biçime çökerten bir karardır. Okuma bir tip üretir, yazma o
tipi geri metne çevirir ve iki dönüşüm birbirinin tersi olmak zorunda değildir. Aradaki farkı
ölçmenin ucuz ve eksiksiz yolu, sonucu kaynakla karşılaştırmaktır.

## Çıkarımın Verdiği Karar

```python
# cikarim.py — tip cikarimi: bir sutunun butun degerleri ayni tipe ayrisiyorsa o
# tip secilir, ayrismiyorsa sutun metin kalir. Yalniz bos hucre eksik sayilir.
def ayristir(h: str):
    if h == "":
        return None, "eksik"
    try:
        return int(h), "tam"
    except ValueError:
        pass
    try:
        return float(h), "ondalik"
    except ValueError:
        return h, "metin"

def cikarim(satirlar, ornek=None):
    """Sutun tipini ornekten cikarir; ornek None ise butun satirlar taranir."""
    kesit = satirlar if ornek is None else satirlar[:ornek]
    tip, gecis = [], 0
    for j in range(len(SUTUN)):
        gorulen = set()
        for s in kesit:
            gecis += 1
            gorulen.add(ayristir(s[j])[1])
        gorulen.discard("eksik")
        tip.append("tam" if gorulen == {"tam"}
                   else "ondalik" if gorulen and gorulen <= {"tam", "ondalik"} else "metin")
    return tip, gecis

def oku(satirlar, tip):
    """Cikarilan tiplerle okur; sutun tipine ayrismayan hucre sessizce eksige doner."""
    tablo, dusen = [], 0
    for s in satirlar:
        kayit = []
        for j, h in enumerate(s):
            d, t = ayristir(h)
            if d is None or tip[j] == "metin":
                kayit.append(None if d is None else h)
            elif t == "metin" or (tip[j] == "tam" and t == "ondalik"):
                kayit.append(None)
                dusen += 1
            else:
                kayit.append(int(h) if tip[j] == "tam" else float(h))
        tablo.append(kayit)
    return tablo, dusen

def geri_yaz(tablo):
    return [["" if v is None else repr(v) if isinstance(v, float) else str(v)
             for v in k] for k in tablo]

tam_tip, tam_gecis = cikarim(satirlar)
ornek_tip, ornek_gecis = cikarim(satirlar, 100)
tablo, dusen = oku(satirlar, tam_tip)
o_tablo, o_dusen = oku(satirlar, ornek_tip)
donen, o_donen = geri_yaz(tablo), geri_yaz(o_tablo)

EN = [14, 12, 12, 12, 12]
y = lambda h: "".join(str(v).rjust(EN[j]) if j else str(v).ljust(EN[0])
                      for j, v in enumerate(h))
print(y(["sutun", "tum satir", "ilk 100", "donusen", "donusen/100"]))
for j, ad in enumerate(SUTUN):
    d1 = sum(1 for i in range(len(satirlar)) if donen[i][j] != satirlar[i][j])
    d2 = sum(1 for i in range(len(satirlar)) if o_donen[i][j] != satirlar[i][j])
    print(y([ad, tam_tip[j], ornek_tip[j], d1, d2]))
print(f"\ntam tarama {tam_gecis} hucre gecisi, ilk 100 satirdan cikarim {ornek_gecis} gecis")
print(f"tam tiple okumada {dusen} hucre sessizce eksige dondu, "
      f"ornek tiple okumada {o_dusen} hucre")
```

```
sutun            tum satir     ilk 100     donusen donusen/100
abone_no               tam         tam           0           0
sayac_no             metin         tam           0        3199
donem                metin       metin           0           0
endeks               metin       metin           0           0
tuketim_m3         ondalik     ondalik         422         422
tarife_kodu            tam         tam           0           0
bolge                metin       metin           0           0

tam tarama 22393 hucre gecisi, ilk 100 satirdan cikarim 700 gecis
tam tiple okumada 0 hucre sessizce eksige dondu, ornek tiple okumada 1123 hucre
```

Tabloda iki ayrı olay var. Birincisi son sütunlarda: sayaç numarası bütün satırlar tarandığında
metin, ilk 100 satır tarandığında tam sayı çıkıyor. Çıkarım yanlış yapmıyor; ilk 100 satırda o
sütunun bütün değerleri gerçekten tam sayıya ayrışıyor. Örneklemin gördüğü şey doğrudur, kümenin
tamamı için yanlıştır ve aradaki farkı yalnız 2.077. satır bilir.

Bu kayışın bedeli iki sayıda görünüyor. Sayaç numarası tam sayı okunduğunda gidiş dönüşte
**3.199 hücrenin tamamı** değişir: 2.076 hücre baştaki sıfırlarını kaybeder, harfli 1.123 hücre
hiçbir tipe ayrışmadığı için **sessizce eksiğe döner**. Bir hata satırı yoktur; sütun okunmuş,
çerçeve kurulmuş, satır sayısı 3.199 kalmıştır. Kaybın tek izi, o abonelerin sayaç numarasının
artık boş olmasıdır.

İkinci olay bütün çıkarımlarda ortaktır: tüketim sütununda 422 hücre gidiş dönüşte değişiyor.
Kaynakta değerler iki ondalıklı sabit biçimle yazılmıştır; okunduğunda sayıya döner ve geri
yazıldığında sayının kendi kısa gösterimini alır. `0.00` hücresi `0.0` olur, `20.10` hücresi
`20.1` olur. Değer aynıdır, metin değildir. Bir gidiş dönüş denetimi bunu yakalar ve tipin
kendisinden değil **biçiminden** kaynaklandığını söyler; ikisini ayırmak, hangi kaybın önemli
olduğuna karar vermenin ilk adımıdır.

## Kazanç ve Gizlenen Karar

```python
# belirti.py — yanlis tipin belirtisi: hata vermeyen ama anlamsiz ya da sessizce
# bos donen bir islem. Bayt olcusu gidis donusteki dosya boyutundan alinir.
import io

sut = lambda j: [k[j] for k in tablo]
print(f"abone_no tam sayi okundu   : ortalama abone numarasi "
      f"{round(sum(sut(0)) / len(satirlar), 1)}")
print(f"tarife_kodu tam sayi okundu: ortalama tarife basamagi "
      f"{round(sum(sut(5)) / len(satirlar), 1)} m3")
endeks = [v for v in sut(3) if v is not None]
print(f"endeks metin okundu        : {sum(1 for v in endeks if v != 'yok')} sayisal deger "
      f"metne dondu, sutunun en buyugu {max(endeks)!r}")

def bayt(kayitlar) -> int:
    b = io.StringIO()
    yazici = csv.writer(b)
    yazici.writerow(SUTUN)
    yazici.writerows(kayitlar)
    return len(b.getvalue().encode())

print(f"\nbayt   : kaynak {bayt(satirlar)}, tam cikarimla geri yazim {bayt(donen)}, "
      f"ornek cikarimla {bayt(o_donen)}")
print(f"gecis  : tam tarama {tam_gecis}, ilk 100 satir {ornek_gecis}")
print(f"kod    : sutun basina elle donusum 7 kural, cikarimli okuma 1 cagri")
yanlis = sum(1 for j in (0, 3, 5) if tam_tip[j] in ("tam", "metin"))
print(f"sonuc  : {len(SUTUN)} sutunun {yanlis} tanesi yanlis tiple okundu; ornek "
      f"100 satirdan alininca {yanlis + 1} oldu ve {o_dusen} hucre sessizce eksige dondu")
```

```
abone_no tam sayi okundu   : ortalama abone numarasi 10690.5
tarife_kodu tam sayi okundu: ortalama tarife basamagi 27.4 m3
endeks metin okundu        : 3008 sayisal deger metne dondu, sutunun en buyugu 'yok'

bayt   : kaynak 139238, tam cikarimla geri yazim 138816, ornek cikarimla 127926
gecis  : tam tarama 22393, ilk 100 satir 700
kod    : sutun basina elle donusum 7 kural, cikarimli okuma 1 cagri
sonuc  : 7 sutunun 3 tanesi yanlis tiple okundu; ornek 100 satirdan alininca 4 oldu ve 1123 hucre sessizce eksige dondu
```

İlk üç satır yanlış tipin belirtisini gösteriyor ve üçünde de bir hata mesajı yoktur. Abone
numarasının ortalaması **10.690,5** çıkar; bu bir sayıdır, hesaplanabilir, raporlanabilir ve hiçbir
şey ifade etmez. Tarife basamağının ortalaması **27,4 m³** çıkar; basamaklar 10, 25 ve 40'tır,
27,4 diye bir basamak yoktur. Endeks sütununun **3.008 sayısal değeri** metne dönmüştür ve
sütunun en büyük değeri `'yok'` olur, çünkü metin karşılaştırması harfi rakamdan büyük sayar.

Üçünün ortak yanı, yanlışlığın **çerçevede** değil kullanımda ortaya çıkmasıdır. Çerçeve
kurulduğunda hiçbir şey bozuk görünmez: satır sayısı doğrudur, sütun adları yerindedir, hücreler
doludur. Yanlış tip ancak o sütuna bir işlem uygulandığında konuşur ve konuştuğunda da hata değil
sayı üretir. Bu yüzden tipin denetimi, çerçeve kurulduktan sonra değil, okuma kararının yanında
yapılır.

Kazanç tarafı açık: yedi sütun için ayrı ayrı dönüşüm kuralı yazmak yerine tek bir okuma çağrısı
yazılır ve çerçeve tiplenmiş gelir. Tam tarama 22.393 hücre geçişi tutar; çıkarım ilk 100 satırla
sınırlanırsa geçiş 700'e, yani otuz ikide bire iner. Bayt tarafında da kazanç görünür: tam
çıkarımla geri yazılan dosya 138.816 bayttır, örnek çıkarımla 127.926 bayta düşer.

Gizlenen karar tam da o bayt düşüşünün içindedir. 10.890 baytlık tasarruf bir sıkıştırma değil,
bir **kayıptır**: 1.123 sayaç numarası yazılmamıştır. Örneklem büyüklüğü hiçbir yerde yazılmadan
seçilmiş, çıkarım o seçime göre karar vermiş, okuma hiçbir uyarı üretmemiştir. Varsayılanı
yazılmayan bir okuma çağrısı, kaç satıra baktığını da söylemeyen bir çağrıdır ve bu kursun kuralı
gereği ölçülmemiş sayılır.

## Özet

- Ayırıcıyla bölünmüş bir dosya tip taşımaz; tip okuma anında çıkarımla üretilir ve kural sütun
  genelindedir: tek bir ayrışmayan değer sütunun tamamını metne düşürür.
- Bütün satırlar tarandığında 7 sütunun 3'ü yanlış tiple gelir: abone numarasının ortalaması
  10.690,5, tarife basamağının ortalaması 27,4 m³ diye hesaplanabilir, endeksin 3.008 sayısal
  değeri metne döner ve sütunun en büyüğü `'yok'` olur.
- Çıkarım ilk 100 satırdan yapılınca geçiş 22.393'ten 700'e iner, ama sayaç numarası tam sayıya
  kayar: 2.077. satırda başlayan harfli kodların 1.123 hücresi uyarısız eksiğe döner.
- Gidiş dönüş denetimi kaybı doğrudan sayar: örnek çıkarımla 3.199 hücre değişir, dosya 139.238
  bayttan 127.926 bayta iner ve düşüşün tamamı yazılmamış sayaç numarasıdır.
- Biçim kaybı tipten ayrı bir kalemdir: iki ondalıklı sabit yazımın kısa gösterime dönmesi her iki
  çıkarımda da 422 hücreyi değiştirir ve hiçbir değeri bozmaz.

## Sonraki Adım

Küme artık bir çerçevededir ve sütunlarının tipi bilinerek okunmuştur. Sıradaki soru satıra nasıl
erişileceğidir. Bir çerçeveden "10.100 ile 10.200 arasındaki aboneler" istendiğinde iki ayrı yol
vardır: etiket dizinine göre adla erişmek ve sıraya göre konumla erişmek. İki yol aynı aralığı
tarif ediyormuş gibi yazılır, aynı adı taşır, yan yana durur. Sonraki ders aynı dilimi iki erişim
biçiminde çalıştırır ve tek bir sayıyı sorar: her biri kaç satır döndürüyor ve aralığın son ucu
sonuca dahil mi.
