---
title: 'Veri Mühendisliğine Giriş'
source: 'https://academia.sh/tr/kurslar/veri-muhendisligine-giris'
language: tr
updated: '2026-08-17T04:40:15+00:00'
license: 'CC BY-SA 4.0'
lessons: 10
outcomes:
    - 'Veri mühendisliği yaşam döngüsünün aşamalarını kurgu bir hat üzerinde kurar ve her aşamanın düşürdüğü satırı sayar'
    - 'Toplu ve akış işleme arasındaki seçimi tazelik ile yakalama gecikmesinin takası olarak gerekçelendirir'
    - 'Veri kalitesi boyutlarını çalışan denetimlere çevirir ve denetimin kendi kusurunu ölçer'
---

# Veri Mühendisliğine Giriş

Beş aşamalı kurgu bir hattın kurulması ve altı bozulma sınıfının kaç aşama sonra yakalandığının sayılması: hattın 2400 satırı aşama aşama 5 sunulan değere indirmesi ve her sunulan değerin arkasında kaç satır durduğu, her aşamanın kendi bozulmaya açıklığı, aynı bozulmanın veri mühendisiyle model kuranın ölçüsüne farklı yansıması ve birim değişiminin ikisinde de görünmemesi, kaynak sınıfı başına bozulma profili ve yakalama gecikmesi, toplu işlemede bozulmanın bir dönem sonra akışta aynı dönemde görünmesi ile akışın büyüttüğü aşama sayısı, kalite boyutlarının denetime çevrilmesi ve bölüm denetiminin dönemi görmediği için yeşil kalması, şema değişiminin düşürdüğü satır ile sözleşmenin yakaladığı aşama, yinelemeye dayanıklı olmayan hatta yeniden işlemenin satırı çoğaltması ve sunulan sayının değişmemesi, soyağacının bozulmuş bir değerden kaynağa gitmek için bakılan aşamayı kısaltması, beş aşamanın kaçının sahip taşıdığı ve saklama politikasının geriye dönük denetlenemez kıldığı dönem sayısı.

## Alan ve Yaşam Döngüsü

Kurgu bir veri hattının beş aşamayla kurulması ve her aşamanın ne kadarını taşıdığının sayılması: 2400 kaynak satırın ayıklama, tekilleştirme, tüketim, özet ve sunma aşamalarından geçerek 5 sunulan değere inmesi ve her sunulan değerin arkasında kaç satır durduğu, altı bozulma sınıfının hangi aşamada etkisini gösterdiği ve hangilerinin sunulan sayıyı hiç değiştirmediği, aynı bozulmanın veri mühendisiyle model kuranın ölçüsüne farklı yansıması ve birim değişiminin ikisinde de görünmemesi, uygulama veritabanı, günlük ve dış servis kaynak sınıflarının taşıdığı bozulma profili ile her birinin yakalama gecikmesi, toplu işlemede bozulmanın bir dönem sonra akış işlemede aynı dönemde görünmesi ve karşılığında akışın büyüttüğü aşama sayısı ile durum taşıma.

### Veri Mühendisliği Nedir

Source: https://academia.sh/tr/kurslar/veri-muhendisligine-giris/veri-muhendisligi-nedir

Kurgu bir ölçüm ağının 2400 sayaç okumasının beş aşamalı bir hattan geçirilip her aşamanın taşıdığı satırın sayılması: ayıklama 2400, tekilleştirme 2400, tüketim 2200, özet 55 ve sunma 5. Tüketim aşamasının düşürdüğü satır sıfır olduğu hâlde çıkışı iki yüz satır daha az, çünkü ilk dönemin farkı tanım gereği yoktur. Beş sunulan değerin arkasında 2400 kaynak satırı duruyor, değer başına ortalama 480; en büyük değer 588 kaynak satırının, en küçüğü 372 satırın üzerinde. Hat belirlenimci olduğu için bu sayıların hepsi koşum yapılmadan da türetilebilir. Denetimsiz hatta yakalanan bozulma sıfır ve yakalama gecikmesi tanımsızdır, çünkü ölçen hiçbir şey yoktur.

Yapay Zekâ Mühendisliğine Giriş kursuyla açılan müfredat, yüz otuz iki derste düzeneği modelin
çevresine kurdu ve her parçanın bedelini ayrı ayrı yazdı. Bu derslerin tamamında modele giren
verinin **hazır olduğu** varsayıldı: bir tablo vardı, okundu; bir öznitelik vardı, hesaplandı. O
tablonun nasıl dolduğu, üretimde çalışır tutulmasının ne gerektirdiği ve **bozulduğunda kimin
haberi olacağı** hiç sorulmadı.

Bu müfredatın konusu o hattır ve hattın kendine özgü bir kusuru bütün ölçüyü belirler: **bir hat
çöktüğünde haber verir, yanlış veri taşıdığında hiçbir şey söylemez.** Çöken hat bir hata kaydı
bırakır, bir uyarı tetikler, bir kuyruk biriktirir; birileri bakar. Yanlış satır taşıyan hat ise
koşar, biter, tabloyu yazar ve gösterge yeşil kalır. Bu dersin işi o hattı kurmak ve **saymaktır**:
kaç satır giriyor, her aşama kaçını taşıyor, kaçı sunulan sayıya ulaşıyor.

- **YD1.** Kod bir **kurgudur**. Gerçek bir veri deposu, dosya biçimi, orkestratör ya da çalışma
  zamanı çağrılmaz; hat düz kitaplıkla yazılmıştır. Kaynak, bir ölçüm ağının **kurgu** sayaç
  okumalarıdır ve tohum **20260218**'dir.
- **YD2.** Kaynak **200 abone x 12 dönem = 2400 okuma**. Her okuma bir endeks değeri taşır, endeks
  artandır ve birim `m3`'tür. Tüketim endeks farkından hesaplanır.
- **YD3.** Hat **beş aşamalıdır**: `ayikla`, `tekille`, `tuketim`, `ozet`, `sun`. Her aşama kendi
  düşürdüğü satırı **sayar**; sayılmayan düşüş yoktur.
- **YD4. Toplama, temizleme, eksik değer ve aykırı değer yordamı tekrarlanmaz.** Veri Toplama ve
  Hazırlama kursunda ölçüldü. Buraya eklenen tek şey **aşama sayacıdır**: bir satırın hangi adımda
  ve neden düştüğü yazılır.
- **YD5. Şema, kısıt ve normalleştirme yordamı tekrarlanmaz.** Veri Modelleme ve İlişkisel Kuram
  kursunda ölçüldü. Bu derste şema yalnız "zorunlu alan var mı" sorusuna indirgenir.
- **YD6.** Hat **belirlenimcidir**. `random` kullanılmaz, üreteç ders içinde yazılır; aynı tohum
  aynı 2400 satırı verir. Bu yüzden ölçüler koşumdan bağımsızdır ve aşağıdaki sayıların hepsi
  koşum yapılmadan da türetilebilir.
- **YD7.** Hiçbir ürün, satıcı ya da biçim adı yazılmaz; depolar ve işleyiciler **türleriyle**
  anılır. Süre ham olarak yazılmaz; gecikme **aşama** ya da **dönem** olarak yazılır.
- **YD8.** Kümenin çözünürlüğü ölçüm bandının alt sınırıdır: 2400 satırda satır oranının en küçük
  adımı **0,0004**, 55 özet hücresinde **0,0182**, 5 sunum değerinde **0,2000**. **Sunum üzerinde
  oran basılmaz**, değerin kendisi yazılır.

#### Hattın Beş Aşaması

Veri mühendisliğinin sorumluluğu tek cümleyle şudur: bir yerde üretilmiş veriyi, başka bir yerde
kullanılabilir hâle getirmek. Bu cümledeki "kullanılabilir" sözcüğü bir dizi adımı gizler ve
adımlar sayılmadıkça sorumluluk da ölçülemez. Aşağıdaki hat o adımları beş parçaya ayırır: zorunlu
alanı olmayan satır **ayıklanır**, aynı abone-dönem çifti **tekilleştirilir**, endeks farkından
**tüketim** hesaplanır, bölge ve dönem başına **özet** çıkarılır, bölge başına dönem ortalaması
**sunulur**.

Bu beşlinin sırası keyfî değildir ve iki bağı kodun kendisi zorlar. `tekille` aşaması satırın
`abone` ve `donem` alanlarını doğrudan okur; bu alanlardan biri yoksa aşama hatayla durur. Yani
**ayıklama tekilleştirmeden önce gelmek zorundadır**, yoksa hat çöker. İkinci bağ daha sessizdir:
`tuketim` aşaması her giriş satırı için bir çıkış satırı üretir, `ozet` aşaması da bu satırları
toplar. Çift kayıt tekilleştirilmeden tüketime girseydi aynı abone-dönem farkı iki çıkış satırı
üretir ve özet hücresine **iki kez** eklenirdi. Birinci bağ ihlal edildiğinde hat durur ve haber
verir; ikinci bağ ihlal edildiğinde hat çalışır, biter ve **yanlış sayı yazar**. Bir hattın aşama
sırası, hangi hataların gürültülü hangilerinin sessiz olacağını belirleyen ilk karardır.

```python
# KURGU hat -- gercek bir depo, bicim ya da orkestrator cagrilmaz.
# Kaynak bir olcum aginin KURGU sayac okumalaridir, tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


# Her donem her abone icin bir okuma. Birim m3 , endeks artan.
def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    """Zorunlu alanlari tasimayan satir dusurulur."""
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    """Sunum: bolge basina donem ortalamasi."""
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]


def hat(kayit):
    iz, veri = [], kayit
    for ad, f in ASAMA:
        veri, dusen = f(veri)
        n = len(veri) if hasattr(veri, "__len__") else 0
        iz.append({"asama": ad, "kalan": n, "dusen": dusen})
    return veri, iz


kaynak = kaynak_uret()
sunum, iz = hat(kaynak)
print("kaynak satiri", len(kaynak), " abone", len(ABONE), " donem", len(DONEM))
print("asama      kalan   dusen")
for a in iz:
    print(f"{a['asama']:<10}{a['kalan']:>6}{a['dusen']:>8}")
print("sunum")
for b, v in sorted(sunum.items(), key=lambda x: -x[1]):
    print(f"  {b:<8}{v:>9.2f}")
```

```
kaynak satiri 2400  abone 200  donem 12
asama      kalan   dusen
ayikla      2400       0
tekille     2400       0
tuketim     2200       0
ozet          55       0
sun            5       0
sunum
  merkez    1321.45
  kuzey     1212.55
  guney     1065.55
  dogu       881.36
  bati       833.09
```

İz tablosunun ilk iki satırı sıfır düşüşle geçiyor: kaynak sağlam olduğu için ayıklanacak eksik
alanlı satır yok, tekilleştirilecek çift kayıt yok. Üçüncü satır ise okumanın kilit noktasıdır.
`tuketim` aşamasının **düşürdüğü satır sıfır**, buna karşılık çıkışı **iki yüz satır daha az**.
Bu fark bir kayıp değil, tanımın kendisidir: tüketim iki dönemin endeks farkıdır ve her abonenin
**ilk döneminin farkı yoktur**. İki yüz abonenin ilk dönemi hesaplanamaz, ama bunlar bozuk satır
değildir. Bir hattın izinde "kalan" ile "düşen" sütunlarının ayrı tutulmasının nedeni budur:
**tanım gereği çıkmayan satır ile hata yüzünden çıkarılan satır aynı sütuna yazılamaz.**

Bu sayıların hiçbiri koşuma bağlı değildir. Hat belirlenimci olduğu için tablo koşum yapılmadan da
türetilir: 200 abone x 12 dönem = 2400; her abone bir dönem kaybettiği için 200 x 11 = 2200; 5
bölge x 11 dönem = 55 hücre; 5 bölge = 5 değer. Aynı tohumla ikinci bir koşum aynı tabloyu verir,
bu yüzden aşağıdaki karşılaştırmalarda görülen her fark bozulmanın kendisinden gelir, oynaklıktan
değil.

#### Sunulan Değerin Arkasındaki Satır

Bir hattın en çok bakılan yeri son aşamasıdır, çünkü rapor oradan okunur. Bu hattın son aşaması
**beş sayı** üretiyor. O beş sayının arkasında kaç satır durduğu, hattın ne kadarını gizlediğini
söyler.

```python
# Her sunulan degerin arkasinda kac satir duruyor
v1, _ = a1_ayikla(kaynak)
v2, _ = a2_tekille(v1)
v3, _ = a3_tuketim(v2)
ozet, _ = a4_ozet(v3)
abone, satir, hucre = {}, {}, {}
for a in ABONE.values():
    abone[a["bolge"]] = abone.get(a["bolge"], 0) + 1
for k in v3:
    satir[k["bolge"]] = satir.get(k["bolge"], 0) + 1
for b, d in ozet:
    hucre[b] = hucre.get(b, 0) + 1
print("bolge     sunulan  ozet  tuketim  kaynak  abone")
for b, v in sorted(sunum.items(), key=lambda x: -x[1]):
    print(f"{b:<9}{v:>9.2f}{hucre[b]:>6}{satir[b]:>9}{abone[b] * 12:>8}{abone[b]:>7}")
print(f"{'toplam':<9}{len(sunum):>9}{len(ozet):>6}{len(v3):>9}{len(kaynak):>8}{len(ABONE):>7}")
```

```
bolge     sunulan  ozet  tuketim  kaynak  abone
merkez     1321.45    11      539     588     49
kuzey      1212.55    11      506     552     46
guney      1065.55    11      440     480     40
dogu        881.36    11      374     408     34
bati        833.09    11      341     372     31
toplam           5    55     2200    2400    200
```

Tablo hattın **sıkıştırma oranını** açıkça yazıyor. `merkez` için raporda görünen tek sayı,
**1321,45**, kırk dokuz abonenin on iki dönemlik **588 kaynak satırının**, bunlardan hesaplanan
**539 tüketim satırının** ve on bir **özet hücresinin** üzerinde duruyor. En küçük değer olan
`bati`, **372 kaynak satırının** üzerinde. Beş değer birlikte **2400 satırı** temsil ediyor, yani
değer başına ortalama **480 kaynak satırı**.

Bu sıkıştırmanın iki yüzü var. Yararlı yüzü açıktır: kimse iki bin dört yüz satıra bakarak karar
veremez, özetin işi zaten budur. Bedeli ise ölçünün konusudur. Bir kaynak satırı bozulduğunda o
bozulma sunulan sayıya **480'de bir ağırlıkla** girer; `merkez` için 588'de bir. Tüketim
katmanının kaynağa oranı **0,9167**'dir ve bu oranın en küçük adımı 0,0004 olduğuna göre üç
haneli okunabilir; ama sunum katmanında oran yazmanın anlamı yoktur, çünkü orada yalnız beş değer
vardır ve bir değerin oransal ağırlığı 0,2000'dir. Sunum katmanında yazılacak şey **değerin
kendisidir**.

Buradan hattın ilk yapısal sonucu çıkar: **tek bir satırın bozulması sunulan sayıda görünmeyecek
kadar seyrelir, ama yüz satırın aynı yönde bozulması görünmeyecek kadar seyrelmez.** Hangi
bozulmanın hangi eşikte göründüğü bu hattın ölçülecek asıl niceliğidir ve bu ders onu henüz
ölçmüyor, yalnız zeminini kuruyor.

Sıkıştırmanın satır sayısında görünmeyen bir yanı daha var: hat yalnız satır düşürmüyor, **ayırt
edici boyut** da düşürüyor. İlk üç aşamanın çıkışı `abone` ile `donem` çiftiyle anahtarlıdır ve
tabloda iki yüz ayrı abone durur. `ozet` aşamasının çıkışı ise `bolge` ile `donem` çiftiyle
anahtarlıdır; abone sütunu orada **kalmaz**. Elli beş hücrenin hiçbirinden hangi abonelerin
toplandığı okunamaz. `sun` aşaması aynı şeyi dönem eksenine yapar: beş değerin hiçbirinden hangi
dönemin ne kadar eklediği okunamaz. Bu, bir kayıp değil özetin tanımıdır, ama sonucu üretimde
ağırdır. Sunulan bir sayı şüpheli göründüğünde geriye doğru yürümek, ara çıktıları **okumakla**
yapılamaz; hattın ilk üç aşamasının **yeniden koşturulması** gerekir. Bir hattın hangi aşamada
hangi boyutu bıraktığını yazmak, bozulma araştırmasının maliyetini önceden yazmakla aynı şeydir.

#### Denetimsiz Hattın Sayısı

Yukarıdaki hat çalışıyor, bitiyor ve beş sayı yazıyor. Bu hat hakkında bilinen tek şey **kaç satır
taşıdığıdır**. Taşıdığı satırların **doğru** olup olmadığı hakkında hiçbir şey bilinmiyor, çünkü
hatta bunu soran hiçbir adım yok.

Müfredatın her dersinde bundan sonra **üç sayı yan yana** duracak:

1. **Denetimsiz hat.** Hiçbir denetim açık değil. Yakalanan bozulma **0**, yakalama gecikmesi
   **tanımsız**. Bu dersteki hat tam olarak budur.
2. **Denetimli hat.** Aynı iş yükü, denetim eklenmiş. Yakalanan bozulma ve **yakalama gecikmesi**
   aşama numarası olarak yazılır.
3. **Bedel.** Yakalama gecikmesi, yanlış engellenen iyi satır, yeniden işlenen satır ve eklenen
   aşama.

Üçüncü maddedeki kalemler bu müfredatın en sık atlanan yanıdır. Denetim eklemek bedava bir iyileşme
değildir: bir denetim iyi satırı da engelleyebilir, hattı gereksiz yere durdurabilir, yeniden
işleme sırasında satır çoğaltabilir ve **kendisi kusurlu olabilir**. Bu yüzden denetimli hat
yazılırken yalnız yakalanan bozulma değil, denetimin **kendi bedeli** de aynı tabloya girer.
Yakalanan bozulma sayısı tek başına yazıldığında iyileşme abartılmış olur.

Birinci maddedeki "tanımsız" sözcüğü dikkatle okunmalıdır. Denetimsiz hattın yakalama gecikmesi
**büyük** değildir, **yoktur**; ölçen hiçbir şey olmadığı için gecikme diye bir nicelik oluşmaz.
Bu, sıfır ile tanımsız arasındaki farktır ve raporlamada sık karıştırılır: "hiç bozulma
yakalanmadı" cümlesi, denetim yokken bir başarı bildirimi değil, **bir ölçüm yapılmadığının**
bildirimidir.

Buradan müfredatın kuralı gelir ve altı kurs boyunca sürer: **bir veri hattının sayısı taşıdığı
satır değil, yanlış satırın kaç adım sonra yakalandığıdır. Bozulmayı yakalayan denetimi yazılmayan
hat ölçülmemiş sayılır.** Yukarıdaki hattın izi kusursuz görünüyor, çünkü izin gösterdiği tek şey
aşamaların birbirine kaç satır devrettiğidir. Devredilen satırın içeriği hakkında iz sessizdir ve
sessizliği yeşil renkte yazılır.

#### Özet

- Veri mühendisliğinin sorumluluğu bir yerde üretilmiş veriyi başka bir yerde kullanılabilir
  kılmaktır; bu sorumluluk ancak hattın aşamaları ve her aşamanın taşıdığı satır sayıldığında
  ölçülebilir hâle gelir.
- Bir hat çöktüğünde haber verir, yanlış veri taşıdığında hiçbir şey söylemez; bu müfredatın ölçüsü
  ikinci durumdur.
- Beş aşamalı hat 2400 kaynak satırını 2400, 2400, 2200, 55 ve 5 olarak taşıyor; `tuketim`
  aşamasının düşürdüğü satır sıfır olduğu hâlde çıkışı iki yüz satır azdır, çünkü ilk dönemin farkı
  tanım gereği yoktur.
- Sunulan beş değerin arkasında 2400 kaynak satırı duruyor: değer başına ortalama 480, en büyüğü
  588, en küçüğü 372. Bir satırın bozulması sunulan sayıya bu ağırlıkla seyrelerek girer.
- Denetimsiz hatta yakalanan bozulma 0 ve yakalama gecikmesi **tanımsızdır**; "hiç bozulma
  yakalanmadı" cümlesi ölçüm yapılmadığının bildirimidir.
- Müfredatın kuralı: bir veri hattının sayısı yanlış satırın kaç adım sonra yakalandığıdır;
  bozulmayı yakalayan denetimi yazılmayan hat ölçülmemiş sayılır.

#### Sonraki Adım

Bu dersin hattı sağlam bir kaynakla koştu ve iz tablosu her aşamada beklenen sayıyı verdi. Kaynağın
sağlam olması bir varsayımdı; üretimde kaynak sağlam gelmez. Bir dönemin kayıtları geç gelir, aynı
kayıt iki kez yazılır, bir alanın adı değişir, bir okuma başka bir birimle gönderilir, bir bölgenin
bölümü hiç yazılmaz, sayı alanı metne döner. Bu kazaların hiçbiri saldırı değildir ve hepsi üretici
tarafında olur. Sonraki ders bu altı sınıfı adlarıyla tanımlar ve aynı hattan geçirir; ölçülecek
şey hangi bozulmanın **hangi aşamada** etkisini gösterdiğidir. Tablonun en rahatsız edici sütunu
sonuncusu olacak: altı sınıfın üçü, hattın sunduğu beş değeri **birebir aynı** bırakıyor.

### Yaşam Döngüsü

Source: https://academia.sh/tr/kurslar/veri-muhendisligine-giris/yasam-dongusu

Altı bozulma sınıfının aynı beş aşamalı hattan geçirilip her aşamanın kendi bozulmaya açıklığının sayılması: ayıklama yalnız alan adı değişimini, tekilleştirme yalnız çift kaydı görüyor, tüketim aşaması eksik satırın bedelini tam iki katına çıkarıyor (40 satır 80'e, 46 satır 92'ye, 200 satır 400'e), özet aşamasında hücre sayısı 55'ten 45 ve 53'e düşüyor, sunma aşaması ise altı sınıfın hiçbirinde beş değerden başka bir şey üretmiyor. Üç sınıf sunulan sayıyı birebir aynı bırakıyor: yinelenen tekilleştirmede yutuluyor, kayan_tip hattın kendi tür dönüşümüyle sessizce onarılıyor, birim_degisti ise özetin on hücresini yıktığı hâlde iki dönem birbirini götürdüğü için sunulan değeri değiştirmiyor. gec_gelen ve birim_degisti tam olarak aynı sayıda özet hücresini bozuyor, biri sunumu değiştiriyor öbürü değiştirmiyor.

Önceki ders hattı sağlam bir kaynakla koşturdu ve iz tablosu her aşamada beklenen sayıyı verdi:
2400, 2400, 2200, 55, 5. Kaynağın sağlam gelmesi bir varsayımdı ve üretimde bu varsayım tutmaz.
Bir dönemin kayıtları geç gelir, aynı kayıt iki kez yazılır, bir alanın adı değişir, bir okuma
başka bir birimle gönderilir, bir bölgenin bölümü hiç yazılmaz, sayı alanı metne döner.

Bu dersin sorusu şudur: bu kazalar aynı hattan geçtiğinde **hangi aşamada** iz bırakır. Beş aşamanın
her biri belirli bir hataya karşı yapısal olarak duyarlı, geri kalanına yapısal olarak kördür ve bu
körlük hattın tasarımından gelir, bir eksiklikten değil. Ölçülecek şey her aşamanın kendi
**bozulmaya açıklığıdır**.

- **YD9.** Hat, kaynak ve tohum önceki dersle aynıdır. Değişen tek şey **kaynağın kendisidir**:
  bozulma hatta değil, hatta girmeden önce uygulanır.
- **YD10. Altı bozulma sınıfının hiçbiri saldırı değildir.** Hepsi üretici tarafında olan günlük
  kazalardır: bir toplama işi geç bitmiştir, bir yeniden gönderim iki kez yazmıştır, bir alan adı
  yeniden adlandırılmıştır, bir sahadaki dönüştürücü değişmiştir, bir bölümün yazımı atlanmıştır,
  bir dışa aktarım her alanı metin olarak vermiştir.
- **YD11.** Her sınıf **tek bir dönemi** bozar (`2026-07`); kalan on bir dönem sağlam kalır. Bu,
  bozulmanın etkisini yalıtmak içindir: iz tablosundaki her fark tek dönemin farkıdır.
- **YD12.** Sınıfların bozduğu satır sayısı tanım gereğidir: `gec_gelen` bu dönemde ilk kırk
  abonenin kaydını hiç getirmez (**40**), `yinelenen` ilk altmış abonenin kaydını iki kez yazar
  (**60** fazla satır), `sema_degisti` `endeks` alanının adını değiştirir (**200**),
  `birim_degisti` okumayı bine katlar ama **birim etiketini `m3` bırakır** (**200**),
  `eksik_bolum` bir bölgenin bölümünü hiç yazmaz (**46**), `kayan_tip` sayı alanını metne çevirir
  (**200**).
- **YD13. Bu derste hiçbir denetim yoktur.** Taban çizgisi denetimsiz hattır: yakalanan bozulma
  **0**, yakalama gecikmesi **tanımsız**. Ölçülen tek şey hattın kendi izidir.
- **YD14.** Ölçü birimi **satır** ve **özet hücresidir**. Süre yazılmaz. Hat belirlenimci olduğu
  için ikinci koşum aynı tabloyu verir; aşağıdaki her fark bozulmadan gelir, oynaklıktan değil.
- **YD15.** Çözünürlük önceki dersteki gibidir: satır oranında **0,0004**, özet hücresinde
  **0,0182**, sunum değerinde **0,2000**. **Sunum üzerinde oran basılmaz.**

#### Altı Sınıfın İzi

Aşağıdaki blok önceki dersin hattını aynen kurar, üzerine altı bozulma sınıfını ekler ve her sınıfı
ayrı ayrı aynı hattan geçirir. İki tablo basılır: aşamalardan **kaç satır çıktığı** ve her aşamanın
**kaç satır düşürdüğü**.

```python
# KURGU hat ve KURGU bozulma siniflari -- gercek bir depo ya da orkestrator cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


# Hicbiri bir saldiri degildir; hepsi uretici tarafi kazalaridir.
BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":                # bu donemde 40 abone hic gelmedi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":                # 60 kayit iki kez geldi
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000        # m3 yerine litre
            y["birim"] = "m3"                       # ETIKET DEGISMEDI , sessiz bozulma
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":               # bir bolgenin bolumu hic yazilmadi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])          # sayi metne dondu
            sayac += 1
            yeni.append(y)
    return yeni, sayac


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]


def hat(kayit):
    iz, veri = [], kayit
    for ad, f in ASAMA:
        veri, dusen = f(veri)
        n = len(veri) if hasattr(veri, "__len__") else 0
        iz.append({"asama": ad, "kalan": n, "dusen": dusen})
    return veri, iz


kaynak = kaynak_uret()
_, taban_iz = hat(kaynak)
BAS = ("ayikla", "tekille", "tuketim", "ozet", "sun")
print(f"{'sinif':<14}{'bozulan':>8}" + "".join(f"{a:>9}" for a in BAS))
print(f"{'saglam':<14}{0:>8}" + "".join(f"{a['kalan']:>9}" for a in taban_iz))
for s in BOZULMA:
    kb, n = boz(kaynak, s)
    print(f"{s:<14}{n:>8}" + "".join(f"{a['kalan']:>9}" for a in hat(kb)[1]))
print()
print(f"{'sinif':<14}" + "".join(f"{a:>9}" for a in BAS[:3]) + f"{'ikincil':>10}")
for s in BOZULMA:
    kb, n = boz(kaynak, s)
    iz = hat(kb)[1]
    print(f"{s:<14}" + "".join(f"{a['dusen']:>9}" for a in iz[:3])
          + f"{taban_iz[2]['kalan'] - iz[2]['kalan']:>10}")
```

```
sinif          bozulan   ayikla  tekille  tuketim     ozet      sun
saglam               0     2400     2400     2200       55        5
gec_gelen           40     2360     2360     2120       55        5
yinelenen           60     2460     2400     2200       55        5
sema_degisti       200     2200     2200     1800       45        5
birim_degisti      200     2400     2400     2200       55        5
eksik_bolum         46     2354     2354     2108       53        5
kayan_tip          200     2400     2400     2200       55        5

sinif            ayikla  tekille  tuketim   ikincil
gec_gelen             0        0       40        80
yinelenen             0       60        0         0
sema_degisti        200        0      200       400
birim_degisti         0        0        0         0
eksik_bolum           0        0       46        92
kayan_tip             0        0        0         0
```

İkinci tablo her aşamanın **neye açık olduğunu** birebir yazıyor. `ayikla` aşaması yalnız tek bir
sınıfı görüyor: `sema_degisti`, çünkü alan adı değiştiğinde satır zorunlu alan listesini
karşılamıyor. Kalan beş sınıfın hiçbiri bu aşamada bir satır düşürmüyor. `tekille` aşaması da
yalnız bir sınıf görüyor: `yinelenen`, altmış çift kayıt. Bu iki aşamanın açıklığı **dar ve
kesindir**; gördükleri şeyi kaçırmazlar, görmedikleri şeye tamamen kördürler.

`tuketim` aşaması farklı davranıyor. Kendi başına hiçbir bozulmayı tanımıyor, ama üç sınıfın
etkisini **taşıyor**: `gec_gelen` için 40, `sema_degisti` için 200, `eksik_bolum` için 46 satır bu
aşamada düşüyor. Bu satırlar bozuk değildir; önceki dönemleri kaybolduğu için farkları
hesaplanamayan **sağlam** satırlardır. Bir aşamanın düşürdüğü satırın kendi hatası olmayabilir.

Son iki sütun ise okumanın sonucudur. `ozet` aşamasında hücre sayısı yalnız iki sınıfta düşüyor:
`sema_degisti` on hücre kaybettiriyor (45), `eksik_bolum` iki hücre (53). Ve `sun` sütunu **altı
satırın hepsinde 5**. Sunma aşamasının çıktı sayısı bölge sayısına bağlıdır, kaynağın durumuna
değil; bu yüzden hattın en çok bakılan katmanı, bozulmayı satır sayısıyla **hiçbir koşulda** haber
veremez.

#### Bir Satırın İki Satıra Mal Olması

Birinci tablonun `ikincil` sütunu ayrı bir nicelik ölçüyor: bozulma sonucu **tüketim çıkışında**
eksilen satır. `gec_gelen` kaynaktan 40 satır eksiltiyor, tüketim çıkışında **80** satır eksiliyor.
`eksik_bolum` 46 eksiltiyor, çıkışta **92**. `sema_degisti` 200 eksiltiyor, çıkışta **400**.
Katsayı üçünde de tam olarak **iki**.

Bu katsayı bir ölçüm sonucu değil, tanımın sonucudur ve koşum yapılmadan türetilir. Tüketim iki
komşu dönemin endeks farkıdır; `2026-07` kaydı kaybolduğunda o abonenin **hem temmuz farkı hem de
ağustos farkı** hesaplanamaz hâle gelir, çünkü ağustosun önceki dönemi temmuzdur. Bir satırın
kaybı iki çıkış satırını götürür. Aynı gerekçe katsayının **neden ikiden büyük olmadığını** da
söyler: eylülün önceki dönemi ağustostur ve ağustos kaydı yerindedir, zincir orada kapanır.

Bu, veri hatlarında sık karşılaşılan bir yanılgıyı düzeltir. Bir kaynak eksikliği raporlandığında
etkisi genellikle eksilen kaynak satırıyla yazılır. Aşama zinciri satırlar arasında **bağ** kuran
bir dönüşüm içeriyorsa gerçek etki bu sayıdan büyüktür ve büyüme çarpanı hattın tanımından
okunabilir. Burada çarpan iki; komşu üç dönemi birleştiren bir dönüşümde üç olurdu.

#### Özet Bozulurken Sunumun Bozulmaması

Şimdiye kadarki iki tablo yalnız **satır ve hücre sayısına** baktı. Sayı aynı kalırken **değerin**
değişmiş olması hâlâ mümkündür. Aşağıdaki blok özet hücrelerini ve sunulan değerleri sağlam hatla
karşılaştırır.

```python
def son_iki(kayit):
    v1, _ = a1_ayikla(kayit)
    v2, _ = a2_tekille(v1)
    v3, _ = a3_tuketim(v2)
    oz, _ = a4_ozet(v3)
    return oz, a5_sun(oz)[0]


t_ozet, t_sunum = son_iki(kaynak)
print(f"{'sinif':<14}{'hucre':>7}{'degisen':>9}{'eksik':>7}{'sunum ayni':>12}")
for s in BOZULMA:
    oz, sn = son_iki(boz(kaynak, s)[0])
    dg = sum(1 for a in oz if a in t_ozet and oz[a] != t_ozet[a])
    ek = sum(1 for a in t_ozet if a not in oz)
    print(f"{s:<14}{len(oz):>7}{dg:>9}{ek:>7}"
          + f"{('evet' if sn == t_sunum else 'hayir'):>12}")
b_ozet, b_sunum = son_iki(boz(kaynak, "birim_degisti")[0])
print("\nbirim_degisti , kuzey ozet hucreleri")
for d in ("2026-06", "2026-07", "2026-08", "2026-09"):
    print(f"  {d}  taban {t_ozet[('kuzey', d)]:>5}   bozuk {b_ozet[('kuzey', d)]:>12}")
print("  11 hucrenin toplami  taban", sum(v for (b, d), v in t_ozet.items() if b == "kuzey"),
      " bozuk", sum(v for (b, d), v in b_ozet.items() if b == "kuzey"))
print("  sunulan kuzey degeri  taban", t_sunum["kuzey"], " bozuk", b_sunum["kuzey"])
```

```
sinif           hucre  degisen  eksik  sunum ayni
gec_gelen          55       10      0       hayir
yinelenen          55        0      0        evet
sema_degisti       45        0     10       hayir
birim_degisti      55       10      0        evet
eksik_bolum        53        0      2       hayir
kayan_tip          55        0      0        evet

birim_degisti , kuzey ozet hucreleri
  2026-06  taban  1041   bozuk         1041
  2026-07  taban  1251   bozuk   1812946491
  2026-08  taban  1119   bozuk  -1812944121
  2026-09  taban  1300   bozuk         1300
  11 hucrenin toplami  taban 13338  bozuk 13338
  sunulan kuzey degeri  taban 1212.55  bozuk 1212.55
```

Son sütun bu müfredatın en önemli iddiasını taşıyor: **altı sınıfın üçü sunulan beş değeri birebir
aynı bırakıyor.** Üçünün mekanizması da farklıdır. `yinelenen` tekilleştirme aşamasında yutuluyor;
altmış çift kayıt düşüyor ve geriye tam olarak sağlam küme kalıyor. `kayan_tip` hattın kendi tür
dönüşümüyle sessizce onarılıyor; `tuketim` aşaması metni sayıya çeviriyor, dönüşüm başarılı oluyor
ve ne satır ne değer değişiyor. Bu ikinci durum özellikle yanıltıcıdır, çünkü onarım yalnız
rakamdan oluşan metinler için işler; alandan boş ya da biçimsiz bir değer geldiğinde aynı satır
sessizce düşerdi ve yine hiçbir yerde yazılmazdı.

Üçüncüsü, `birim_degisti`, ayrı bir sınıftır. Tabloda **on özet hücresi değişiyor**, yani elli beş
hücrenin 0,1818'i; buna karşılık sunulan beş değerin hiçbiri oynamıyor. Mekanizma alt tabloda
açıkça görülüyor: `kuzey` bölgesinin temmuz toplamı 1251'den **1812946491**'e, ağustos toplamı
1119'dan **eksi 1812944121**'e gidiyor. Temmuz okuması bine katlandığı için temmuz farkı devasa
pozitif, ağustos farkı ise aynı büyüklükte negatif oluyor. On bir hücrenin toplamı iki durumda da
**13338**; sunulan değer iki durumda da **1212,55**. İki bozuk dönem birbirini **tam olarak** iptal
ediyor, çünkü toplam ardışık farklardan oluşuyor ve zincirin uçları bozulmamış.

Tablonun en keskin karşılaştırması ilk ve dördüncü satırdır. `gec_gelen` ile `birim_degisti`
**tam olarak aynı sayıda** özet hücresini bozuyor: onar hücre. Biri sunulan değeri değiştiriyor,
öbürü değiştirmiyor. Bozulan ara hücre sayısı, bozulmanın sunuma ulaşıp ulaşmayacağını
söylemiyor; belirleyen şey bozulmanın **işareti ve dağılımıdır**. Buradan müfredatın üçüncü
iddiası doğrudan çıkar: **çıktıya bakmak yetmez.** Sunulan sayının doğru görünmesi, hattın doğru
çalıştığının değil, yalnız o sayının o bozulmaya duyarsız olduğunun kanıtıdır.

#### Özet

- Altı bozulma sınıfının hiçbiri saldırı değildir; hepsi üretici tarafında olan kazalardır ve her
  biri tek bir dönemi bozar.
- `ayikla` yalnız `sema_degisti`'yi, `tekille` yalnız `yinelenen`'i görüyor; iki aşamanın açıklığı
  dar ve kesindir, geri kalan sınıflara tamamen kördürler.
- `tuketim` aşaması kendi başına hiçbir bozulmayı tanımıyor ama üç sınıfın etkisini taşıyor ve
  eksik satırın bedelini **tam iki katına** çıkarıyor: 40 satır 80'e, 46 satır 92'ye, 200 satır
  400'e. Katsayı tanımdan türetilir, koşumdan değil.
- `sun` aşaması altı sınıfın hiçbirinde 5'ten başka bir sayı üretmiyor; hattın en çok bakılan
  katmanı bozulmayı satır sayısıyla hiçbir koşulda haber veremez.
- Üç sınıf sunulan değeri birebir aynı bırakıyor: `yinelenen` tekilleştirmede yutuluyor,
  `kayan_tip` hattın kendi tür dönüşümüyle onarılıyor, `birim_degisti` ise iki dönem birbirini
  iptal ettiği için sunuma hiç ulaşmıyor.
- `gec_gelen` ve `birim_degisti` aynı sayıda özet hücresini (onar hücre) bozuyor, biri sunumu
  değiştiriyor öbürü değiştirmiyor; bozulan ara hücre sayısı sunuma ulaşmanın ölçüsü değildir.

#### Sonraki Adım

Bu dersin tabloları hattı tek bir gözden okudu: kaç satır girdi, kaç satır çıktı, hangi hücre
değişti. Üretimde bu hattın çevresinde tek bir göz yoktur. Hattın aşamalarını yazan ve iz tablosuna
bakan bir rol vardır; hattın sonundaki tabloyu öznitelik olarak alıp üzerine model kuran ve
başarım sayısına bakan başka bir rol vardır. İki rol aynı hattın farklı yerlerinde durur ve aynı
bozulmayı **farklı ölçülerle** görür. Sonraki ders bu iki ölçüyü yan yana koyar: aynı altı sınıf
için satır izi ile model başarımı ayrı ayrı basılır. Sonuç iki rolün de rahat edemeyeceği bir
tablodur, çünkü bir sınıf **ikisinde de** hiçbir iz bırakmıyor.

### Veri Mühendisliği ve Veri Bilimi Ayrımı

Source: https://academia.sh/tr/kurslar/veri-muhendisligine-giris/veri-muhendisligi-ve-veri-bilimi-ayrimi

Aynı altı bozulma sınıfının iki rolün ölçüsüne yan yana basılması: veri mühendisi beş aşamanın satır izini, model kuran ise hattın sunduğu bölge ortalamasını öznitelik alan bir tahmin kuralının ortalama sapmasını (taban 10,832 m3) görüyor. İki ölçü aynı altı sınıfı farklı sıralıyor: sema_degisti mühendisin izinde en gürültülü sınıf (2200 satır 1800'e iniyor) ama modelin sapmasını yalnız 0,003 oynatıyor; gec_gelen kaynaktan yalnız 40 satır eksiltiyor ama sapmayı 0,105 oynatarak en çok o kıpırdatıyor; eksik_bolum sapmayı 0,003 düşürüyor, yani bozulma ölçüyü iyileştiriyor. birim_degisti ve kayan_tip ikisinde de hiçbir iz bırakmıyor. Birim değişiminin görünmezliği teleskopik bir kimlikten geliyor: bölgenin on bir hücresinin toplamı uç endekslerin farkına eşit ve bozulma uçlara dokunmuyor; aynı tablo dönem ekseninde okunduğunda temmuz ortalaması 26,7 yerine 40592074,02 çıkıyor.

Önceki iki ders hattı tek bir gözden okudu. Üretimde bu hattın çevresinde tek bir göz yoktur.
Aşamaları yazan, iz tablosuna bakan ve bir satır düştüğünde uyarılan bir rol vardır; hattın
sonundaki tabloyu öznitelik olarak alan, üzerine bir kural ya da model kuran ve başarım sayısına
bakan başka bir rol vardır. İkisi aynı hattın **farklı yerlerinde** durur.

Rol ayrımının kendisi bu müfredatın konusu değildir; Veri Analitiğine Giriş, Makine Öğrenmesine
Giriş ve Yapay Zekâ Mühendisliğine Giriş kurslarında sorumluluk sınırları ayrı ayrı çizildi. Bu
dersin eklediği tek şey ayrımı **hattın aşamalarına oturtmaktır**: hangi rolün sayısı hangi
aşamadan okunuyor ve iki sayının arasında kalan aşamayı kim ölçüyor. Bu soru sorulduğunda ortaya
tatsız bir sonuç çıkar: aynı bozulma iki ölçüye farklı yansır, ikisi altı sınıfı farklı sıralar ve
bir sınıf **ikisinde de** hiçbir iz bırakmaz.

- **YD16.** Hat, kaynak, altı bozulma sınıfı ve tohum önceki iki dersle aynıdır.
- **YD17. Rol tanımlarının yordamı tekrarlanmaz**; yukarıdaki üç giriş kursunda ölçüldü. Burada
  ayrım yalnız **ölçünün okunduğu aşama** olarak yazılır.
- **YD18.** Veri mühendisinin ölçüsü **satır izidir**: beş aşamanın kalan sayıları. Bu, ilk dersteki
  tablonun aynısıdır.
- **YD19.** Model kuranın ölçüsü **başarımdır**: hattın sunduğu bölge ortalamasını öznitelik alan
  bir tahmin kuralının **ortalama sapması**, `m3` cinsinden. Kural bölgenin sunulan değerini o
  bölgenin abone sayısına böler ve bunu bölgedeki her aboneye tahmin olarak verir.
- **YD20.** Sınama kümesi `2026-11` ve `2026-12` dönemleridir (**400 satır**). Bu iki dönem altı
  sınıfın hiçbirinde bozulmaz; böylece sapmadaki her fark modelin **öznitelik tarafından** gelir,
  sınama tarafından değil.
- **YD21.** Model kuran öznitelik tablosunu **hattan alır**, kaynağa erişmez. Üretimdeki sınır
  budur: sunulan katman iki rol arasındaki sözleşmedir.
- **YD22. Öznitelik mühendisliği ve model değerlendirme yordamı tekrarlanmaz**; Veri Hazırlama ve
  Öznitelik Mühendisliği ile Model Değerlendirme, Yorumlanabilirlik ve Etik kurslarında ölçüldü.
  Model burada kasten en yalın hâlindedir. Amaç modelin iyi olması değil, **ölçüsünün neye duyarlı
  olduğudur**.
- **YD23.** Denetim yok; taban çizgisi denetimsiz hattır, yakalanan bozulma **0**, gecikme
  **tanımsız**. Hat belirlenimcidir, ikinci koşum aynı tabloyu verir. Süre yazılmaz.

#### İki Ölçü, Aynı Hat

Aşağıdaki blok hattı ve altı bozulma sınıfını aynen kurar, üzerine model kuranın ölçüsünü ekler ve
her sınıf için iki sayıyı yan yana basar: satır izi sağlam hatla aynı mı, ortalama sapma sağlam
hatla aynı mı.

```python
# KURGU hat , KURGU bozulma siniflari ve KURGU bir tahmin kurali.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000        # m3 yerine litre
            y["birim"] = "m3"                       # ETIKET DEGISMEDI
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])
            sayac += 1
            yeni.append(y)
    return yeni, sayac


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]


def hat(kayit):
    iz, veri = [], kayit
    for ad, f in ASAMA:
        veri, dusen = f(veri)
        n = len(veri) if hasattr(veri, "__len__") else 0
        iz.append({"asama": ad, "kalan": n, "dusen": dusen})
    return veri, iz


SINAMA = ["2026-11", "2026-12"]


def model_olcu(kayit):
    """Model kuran , hattin sundugu bolge ortalamasini oznitelik olarak alir."""
    v1, _ = a1_ayikla(kayit)
    v2, _ = a2_tekille(v1)
    t, _ = a3_tuketim(v2)
    oz, _ = a4_ozet(t)
    sun, _ = a5_sun(oz)
    n = {}
    for k in t:
        n.setdefault(k["bolge"], set()).add(k["abone"])
    tahmin = {b: sun[b] / len(n[b]) for b in sun}
    si = [k for k in t if k["donem"] in SINAMA]
    return round(sum(abs(k["m3"] - tahmin[k["bolge"]]) for k in si) / len(si), 3), len(si)


kaynak = kaynak_uret()
t_izi = [a["kalan"] for a in hat(kaynak)[1]]
t_sap, t_n = model_olcu(kaynak)
print("sinama satiri", t_n, " taban sapma", t_sap, " m3")
print(f"{'sinif':<14}{'satir izi':>26}{'satir':>7}{'sapma':>8}{'fark':>8}{'model':>7}")
print(f"{'saglam':<14}{str(t_izi):>26}{'-':>7}{t_sap:>8}{'-':>8}{'-':>7}")
for s in BOZULMA:
    izi = [a["kalan"] for a in hat(boz(kaynak, s)[0])[1]]
    sap, _ = model_olcu(boz(kaynak, s)[0])
    print(f"{s:<14}{str(izi):>26}{('evet' if izi == t_izi else 'hayir'):>7}"
          + f"{sap:>8}{round(sap - t_sap, 3):>8}"
          + f"{('evet' if sap == t_sap else 'hayir'):>7}")
```

```
sinama satiri 400  taban sapma 10.832  m3
sinif                          satir izi  satir   sapma    fark  model
saglam         [2400, 2400, 2200, 55, 5]      -  10.832       -      -
gec_gelen      [2360, 2360, 2120, 55, 5]  hayir  10.937   0.105  hayir
yinelenen      [2460, 2400, 2200, 55, 5]  hayir  10.832     0.0   evet
sema_degisti   [2200, 2200, 1800, 45, 5]  hayir  10.835   0.003  hayir
birim_degisti  [2400, 2400, 2200, 55, 5]   evet  10.832     0.0   evet
eksik_bolum    [2354, 2354, 2108, 53, 5]  hayir  10.829  -0.003  hayir
kayan_tip      [2400, 2400, 2200, 55, 5]   evet  10.832     0.0   evet
```

#### Satır Gören ve Başarım Gören

İki ölçü aynı altı sınıfı farklı sıralıyor ve bu sıralama farkı rol ayrımının asıl sayısıdır.

Veri mühendisinin izinde en gürültülü sınıf `sema_degisti`'dir: tüketim çıkışı 2200'den **1800**'e,
özet 55'ten **45**'e iniyor. Alan adının değişmesi kaynağın altıda birini götürüyor ve bu, bir
gösterge tablosunda gözden kaçması olanaksız bir düşüştür. Aynı sınıf model kuranın ölçüsünde
**0,003** oynatıyor, yani 10,832 üzerinden 0,0003 bağıl fark. Model kuran bu sayıya baktığında
hiçbir şey olmadığını düşünür ve haklıdır: eğitim verisinin altıda biri kaybolmuş olsa bile bölge
ortalaması, kalan dokuz dönemden neredeyse aynı çıkıyor.

Karşı yön daha ilginçtir. `gec_gelen` kaynaktan yalnız **40 satır** eksiltiyor, yani 2400'ün
0,0167'sini (satır oranının en küçük adımı 0,0004'tür, yani bu oran üç haneli okunabilir); bu,
çoğu satır sayısı denetiminin bandının altında kalan bir kayıptır. Buna karşılık
model kuranın sapmasını **0,105** oynatıyor ve altı sınıf içinde onu en çok kıpırdatan sınıf bu.
Nedeni tabloda değil hattın tanımındadır: eksik satırlar rastgele dağılmamış, ilk kırk abonede
yoğunlaşmış ve bölge ortalamalarını topluca aşağı çekmiş. **Az sayıda ama tek yönlü** bir kayıp,
çok sayıda ama dengeli bir kayıptan daha zararlıdır ve satır sayısı bu ayrımı yazmaz.

Üçüncü satır iki rolün arasındaki en temiz ayrımdır. `yinelenen` mühendisin izinde açıkça görünür:
`ayikla` çıkışı 2400 yerine **2460**. Model kuranın ölçüsünde ise fark **tam sıfırdır**, çünkü
tekilleştirme aşaması altmış çift kaydı düşürüyor ve öznitelik tablosuna sağlam küme ulaşıyor. Bu,
hattın işini doğru yaptığı bir durumdur; yine de mühendisin bunu görmesi gerekir, çünkü aynı
üreticiden gelen bir sonraki çift kayıt aynı yerde yakalanmayabilir.

`eksik_bolum` satırı ise ölçü okumanın en tehlikeli yanını gösteriyor. Sapma 10,832'den
**10,829**'a **düşüyor**, yani bozulma modelin ölçüsünü **iyileştiriyor**. İyileşme gerçek değildir;
`kuzey` bölgesinin iki dönemi tamamen kaybolduğu için o bölgenin sunulan ortalaması kalan dokuz
hücreden hesaplanıyor ve rastlantı sonucu sınama dönemlerine biraz daha yakın düşüyor. Sonuç şudur:
**bir ölçünün kötüleşmemesi sağlık kanıtı değildir, çünkü bozulma ölçüyü iyileştirebilir de.**
İşaretin yönü bozulmanın varlığı hakkında hiçbir şey söylemez.

#### İkisinde de Görünmeyen

Tablonun son okuması iki satırdır: `birim_degisti` ve `kayan_tip`. İkisinde de satır izi sağlam
hatla **birebir aynı**, sapma da **birebir aynı**. İki rolün rutin ölçüsünün ikisi de bu iki sınıfa
tamamen kördür.

`kayan_tip` için neden yüzeyseldir: hat metni sayıya çeviriyor, dönüşüm başarılı oluyor ve ne satır
ne değer değişiyor. Bunu gören tek şey satırın **değerine hiç bakmayan** bir tür denetimidir ve
böyle bir denetim hattın sıfırıncı adımında koşabilir. `birim_degisti` için neden yapısaldır ve
aşağıdaki blok onu gösterir.

```python
def bolge_toplam(kayit):
    v1, _ = a1_ayikla(kayit)
    v2, _ = a2_tekille(v1)
    t, _ = a3_tuketim(v2)
    oz, _ = a4_ozet(t)
    top = {}
    for (b, d), v in oz.items():
        top[b] = top.get(b, 0) + v
    return top


def donem_ortalama(kayit):
    v1, _ = a1_ayikla(kayit)
    v2, _ = a2_tekille(v1)
    t, _ = a3_tuketim(v2)
    d = {}
    for k in t:
        d.setdefault(k["donem"], []).append(k["m3"])
    return {a: round(sum(v) / len(v), 2) for a, v in d.items()}


ilk = {k["abone"]: k["endeks"] for k in kaynak if k["donem"] == DONEM[0]}
son = {k["abone"]: k["endeks"] for k in kaynak if k["donem"] == DONEM[-1]}
s_top, b_top = bolge_toplam(kaynak), bolge_toplam(boz(kaynak, "birim_degisti")[0])
print("bolge      uc farki   saglam ozet    bozuk ozet")
for b in sorted(s_top):
    uc = sum(son[a] - ilk[a] for a, v in ABONE.items() if v["bolge"] == b)
    print(f"{b:<9}{uc:>11}{s_top[b]:>14}{b_top[b]:>14}")
s_d, b_d = donem_ortalama(kaynak), donem_ortalama(boz(kaynak, "birim_degisti")[0])
print("donem       saglam          bozuk")
for d in DONEM[5:9]:
    print(f"{d:<10}{s_d[d]:>10}{b_d[d]:>15}")
```

```
bolge      uc farki   saglam ozet    bozuk ozet
bati            9164          9164          9164
dogu            9695          9695          9695
guney          11721         11721         11721
kuzey          13338         13338         13338
merkez         14536         14536         14536
donem       saglam          bozuk
2026-06        24.08          24.08
2026-07         26.7    40592074.02
2026-08        25.55   -40592021.77
2026-09        28.96          28.96
```

Birinci tablonun üç sütunu her bölge için **aynı sayıyı** veriyor. Sol sütun hattan hiç geçmeden,
doğrudan kaynağın uç noktalarından hesaplandı: bölgenin abonelerinin son dönem endeksi eksi ilk
dönem endeksi. Orta ve sağ sütun ise hattın on bir özet hücresinin toplamı, sağlam ve bozuk
kaynakla. Üçünün eşitliği bir ölçüm sonucu değil, bir **kimliktir**: ardışık farkların toplamı
uçların farkına eşittir. `birim_degisti` uçlara dokunmadığı için toplam değişemez ve toplamı on bire
bölen sunum değeri de değişemez. Bu, koşum yapılmadan da bilinen bir sonuçtur; koşum yalnız
doğrulamıştır.

İkinci tablo aynı öznitelik tablosunu **dönem ekseninde** okuyor ve orada bozulma bağırıyor: temmuz
ortalaması 26,7 yerine **40592074,02**, ağustos ortalaması 25,55 yerine **eksi 40592021,77**. Veri
saklamıyor; saklayan şey **iki rolün de dönem eksenini toplaması**. Mühendis satırları aşama başına
topluyor, model kuran değerleri bölge başına topluyor; bozulma tam olarak bu iki toplamanın
altındaki eksende, birbirini götüren iki işaretle yaşıyor. Buradan çıkan kural genel ve
uygulanabilirdir: **bir ölçü, bozulmanın yaşadığı ekseni topluyorsa o bozulmayı göremez.**

Rol ayrımının sayısı da buradan okunur. Mühendisin ölçüsü birinci, ikinci ve üçüncü aşamadan
okunuyor; model kuranın ölçüsü beşinci aşamanın çıktısından. Arada **dördüncü aşama** duruyor ve o
elli beş hücreyi iki rolün de rutin ölçüsü okumuyor. Bozulmanın oturduğu yer tam olarak orası.
Sınır bir görev tanımı sorusu değildir; **hattın hangi aşamasından hangi sayının okunduğu**
sorusudur ve iki rolün ölçüsü arasında ölçülmeyen bir aşama kaldığı sürece sınır bir boşluk
üretir.

#### Özet

- Veri mühendisliği ile veri bilimi arasındaki sınır bir görev tanımı değil, **ölçünün okunduğu
  aşamadır**: mühendis birinci ile üçüncü aşamanın satır izini, model kuran beşinci aşamanın
  çıktısını okur.
- İki ölçü aynı altı sınıfı farklı sıralıyor: `sema_degisti` izde en gürültülü sınıf (2200 satır
  1800'e iniyor) ama sapmayı yalnız 0,003 oynatıyor; `gec_gelen` yalnız 40 satır eksiltiyor ama
  sapmayı 0,105 oynatarak en çok o kıpırdatıyor.
- Az sayıda ama tek yönlü bir kayıp, çok sayıda ama dengeli bir kayıptan daha zararlıdır ve satır
  sayısı bu ayrımı yazmaz.
- `eksik_bolum` sapmayı 10,832'den 10,829'a **düşürüyor**: bir bozulma ölçüyü iyileştirebilir, bu
  yüzden ölçünün kötüleşmemesi sağlık kanıtı değildir.
- `birim_degisti` ve `kayan_tip` iki ölçüde de birebir aynı sonucu veriyor. Birim değişiminin
  görünmezliği teleskopik bir kimlikten gelir: bölgenin on bir hücresinin toplamı uç endekslerin
  farkına eşittir ve bozulma uçlara dokunmaz.
- Aynı tablo dönem ekseninde okunduğunda temmuz ortalaması 26,7 yerine 40592074,02 çıkıyor; bir
  ölçü, bozulmanın yaşadığı ekseni topluyorsa o bozulmayı göremez.

#### Sonraki Adım

Üç ders boyunca hat, verinin **geldiği yerden** başlıyormuş gibi ele alındı. Kaynak `kaynak_uret`
çağrısıyla ortaya çıktı, bozulma da ona dışarıdan uygulandı. Verinin **nereden geldiği** hiç
sorulmadı. Oysa bu dersin son tablosundaki `birim_degisti`, bir sahadaki dönüştürücünün
değişmesiyle olur ve hangi kaynak sınıfının böyle bir kazayı taşıyabileceği, hangisinin
taşıyamayacağı önceden bilinebilir. Bir uygulama veritabanından okuyan hat ile bir günlük akışından
ya da bir dış servisten okuyan hat aynı bozulma sınıflarına açık değildir ve aynı bozulmayı aynı
aşamada yakalamaz. Sonraki ders kaynakları **türleriyle** ayırır ve her kaynak sınıfı için iki sayı
basar: hangi bozulma sınıflarını taşıdığı ve her birinin **yakalama gecikmesi**.

### Kaynak Sistemler

Source: https://academia.sh/tr/kurslar/veri-muhendisligine-giris/kaynak-sistemler

Üç kaynak sınıfının bozulma profilinin çıkarılması ve her bozulma sınıfının kaç aşama sonra yakalandığının sayılması: sağlam teslimde üçü de 2400 satır veriyor ve aynı beş sunum değerini üretiyor, hiçbir denetim ateşlemiyor. Bozulma geldiğinde ayrılıyorlar: uygulama veritabanı altı sınıfın üçünü taşıyor ve toplam gecikmesi 4 aşama, günlük beşini taşıyor ve toplam gecikmesi 8, dış servis altısını da taşıyor ve toplam gecikmesi yine 8. Hoşgörülü ayrıştırma tür kaymasını iki kaynakta da görünmez kılıyor: 200 satır bozuluyor, yakalayan denetim yok, sunulan beş değer birebir aynı kalıyor. Satır sayısı denetimi 0,05 toleransla üç sınıfın hiçbirini yakalamıyor çünkü en büyük satır farkı 60, yani 0,0250; tolerans 0,0100'e indirildiğinde üçünü de yakalıyor ama dayanağı üreticinin kendi beyanı olduğunda hiçbir toleransla yakalamıyor.

Önceki üç ders hattı verinin geldiği yerden değil, **elde olduğu yerden** kurdu: iki bin dört yüz
satır ortada duruyordu, beş aşamadan geçiyordu ve her aşamanın düşürdüğü satır sayılıyordu. O
satırların **nereden geldiği** sorulmadı. Oysa bozulma hattın içinde doğmaz; **üretici tarafında**
doğar ve hatta teslim edilir.

Bir **kaynak sistem** (source system) ölçü açısından bir ürün değil üç şeydir: veriyi hangi biçimde
teslim ettiği, hangi bozulma sınıflarını **üretebildiği** ve hangi denetimin ona dayanacak bir
referans bulabildiği. Üç kaynak sınıfı bu üçlüyle tanımlanır; her biri için kaç bozulma sınıfı
taşındığı ve her sınıfın **kaç aşama sonra yakalandığı** sayılır.

- **YD41.** Kod **kurgudur**; gerçek bir depo, motor ya da orkestratör çağrılmaz. Kaynak bir ölçüm
  ağının kurgu sayaç okumalarıdır: 200 abone, 12 dönem, 2400 satır, tohum 20260218.
- **YD42.** Kaynak sınıfı bir ürün değil bir **güvence kümesidir**; ürün adı yazılmaz, sınıflar
  türleriyle anılır.
- **YD43.** Bir kaynak sınıfının **taşımadığı** bozulma sınıfı koşturulmaz; taşıma kararı
  güvenceden çıkar. Bildirimli sütun türü olan kaynak tür kayması, birincil anahtarı zorlayan kaynak
  yinelenen satır üretemez.
- **YD44.** Bozulma sınıfları saldırı değil üretici tarafı kazasıdır; her biri yalnız 2026-07
  dönemini bozar.
- **YD45.** Yedi denetim her kaynakta açıktır; yok sayılan tek şey **dayanağı olmayan** denetimdir.
- **YD46.** Yakalama gecikmesi **aşama** olarak yazılır; yakalayan yoksa **tanımsızdır** ve toplama
  katılmaz. Satır oranının adımı 1/2400, yani 0,0004; sunum üzerinde oran basılmaz.

#### Üç Kaynak Sınıfı ve Sağlam Teslim

Aşağıdaki blok ortak tanımı kurar — kurgu kaynak, beş aşamalı hat, altı bozulma sınıfı, yedi denetim
— ve üzerine üç bağdaştırıcı ekler. Uygulama veritabanı bildirimli şema ile sütun türü taşır ve
birincil anahtarı zorlar. Günlük eklemeli metin satırıdır: alan adlarını ayrıştırıcı verir, tür
hoşgörülü okunur. Dış servis sayısal alanı metin teslim eder.

```python
# KURGUDUR. Gercek bir depo, motor ya da orkestrator cagrilmaz; olcum aginin
# sayac okumalari kurgudur, tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():                                  # her donem her abone icin bir okuma
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


# ---- Alti bozulma sinifi. Hicbiri saldiri degil; hepsi uretici tarafi kazasi.
BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":                # bu donemde 40 abone hic gelmedi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":                # 60 kayit iki kez geldi
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000        # m3 yerine litre
            y["birim"] = "m3"                       # ETIKET DEGISMEDI , sessiz bozulma
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":               # bir bolgenin bolumu hic yazilmadi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])          # sayi metne dondu
            sayac += 1
            yeni.append(y)
    return yeni, sayac


# ---- HAT: bes asama, her asama kendi dusurdugu satiri sayar.
def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):                                   # bolge basina donem ortalamasi
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]


# ---- Yedi denetim, her biri belirli bir asamadan SONRA kosar.
def d_sema(veri, alanlar=("abone", "donem", "bolge", "endeks", "birim")):
    return any(any(a not in k for a in alanlar) for k in veri)


def d_tip(veri):
    return any(not isinstance(k.get("endeks"), int) for k in veri)


def d_tekillik(veri):
    ad = [(k["abone"], k["donem"]) for k in veri]
    return len(ad) != len(set(ad))


def d_satir_sayisi(veri, beklenen, pay=0.05):
    return abs(len(veri) - beklenen) / beklenen > pay


def d_bolum_tamligi(veri):
    """KUSURLU DENETIM: bolge kumesine bakar , donemi gormez."""
    return set(k["bolge"] for k in veri) != set(BOLGE)


def d_bolum_tamligi_donem(veri):
    gor = set((k["bolge"], k["donem"]) for k in veri)
    return gor != set((b, d) for b in BOLGE for d in DONEM)


def d_dagilim(ozet, taban, pay=0.25):
    for ad, v in ozet.items():
        t = taban.get(ad)
        if t and abs(v - t) / max(abs(t), 1) > pay:
            return True
    return False


DENETIM = [("sema", 0, lambda v, t: d_sema(v)),
           ("tip", 0, lambda v, t: d_tip(v)),
           ("tekillik", 0, lambda v, t: d_tekillik(v)),
           ("satir_sayisi", 0, lambda v, t: d_satir_sayisi(v, t["beklenen_satir"])),
           ("bolum", 0, lambda v, t: d_bolum_tamligi(v)),
           ("bolum_donem", 0, lambda v, t: d_bolum_tamligi_donem(v)),
           ("dagilim", 4, lambda v, t: d_dagilim(v, t["taban_ozet"]))]


def denetimli_hat(kayit, taban, acik):
    """Ilk yakalayan denetimi ve yakalama gecikmesini (asama numarasi) dondurur."""
    veri, yakalayan, gecikme = kayit, None, None
    for adim in range(len(ASAMA) + 1):
        if adim > 0:
            veri, _ = ASAMA[adim - 1][1](veri)
        for dad, dasama, df in DENETIM:
            if dad in acik and dasama == adim and df(veri, taban) and yakalayan is None:
                yakalayan, gecikme = dad, adim
    return veri, yakalayan, gecikme


def taban_kur(teslim):
    v, _ = a1_ayikla(teslim)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    sun, _ = a5_sun(oz)
    return {"beklenen_satir": len(teslim), "taban_ozet": oz, "taban_sunum": sun}


# ---- Uc kaynak sinifi: bagdastirici , tasidigi sinif kumesi , dayanakli denetim kumesi.
ALAN = ("abone", "donem", "bolge", "tip", "endeks", "birim")


def u_veritabani(teslim):
    """Bildirimli sema ve sutun turu ; birincil anahtar zorlanir."""
    tek = {}
    for k in teslim:
        tek[(k.get("abone"), k.get("donem"))] = k
    return list(tek.values())


def u_gunluk(teslim):
    """Eklemeli metin satiri ; alan adini ayristirici verir , tur hosgorulu."""
    satir = ["|".join(str(k.get(a, "")) for a in ALAN) for k in teslim]
    okunan = []
    for s in satir:
        p = dict(zip(ALAN, s.split("|")))
        p["endeks"] = int(p["endeks"]) if p["endeks"].isdigit() else p["endeks"]
        okunan.append(p)
    return okunan


def u_dis_servis(teslim):
    """Sayisal alan metin gelir ; ayristirici hosgorulu cevirir."""
    okunan = []
    for k in teslim:
        y = dict(k)
        if isinstance(y.get("endeks"), int):
            y["endeks"] = str(y["endeks"])          # servisin serilestirme bicimi
        if isinstance(y.get("endeks"), str) and y["endeks"].isdigit():
            y["endeks"] = int(y["endeks"])
        okunan.append(y)
    return okunan


YEDI = ["sema", "tip", "tekillik", "satir_sayisi", "bolum", "bolum_donem", "dagilim"]
KAYNAK = {"uygulama vt": (u_veritabani, ["sema_degisti", "birim_degisti", "eksik_bolum"], YEDI),
          "gunluk": (u_gunluk, ["gec_gelen", "yinelenen", "birim_degisti", "eksik_bolum",
                                "kayan_tip"], [a for a in YEDI if a != "satir_sayisi"]),
          "dis servis": (u_dis_servis, BOZULMA, YEDI)}

ham = kaynak_uret()
print("kaynak", len(ham), "satir =", len(ABONE), "abone x", len(DONEM), "donem")
for ad, (u, tasidigi, acik) in KAYNAK.items():
    teslim = u(ham)
    tb = taban_kur(teslim)
    s, y, g = denetimli_hat(teslim, tb, acik)
    print(f"{ad:<12} teslim {len(teslim)} satir  sunum {s == tb['taban_sunum']}  "
          f"acik denetim {len(acik)}  tasidigi sinif {len(tasidigi)}  yakalayan {y}")
print("saglam sunum", taban_kur(ham)["taban_sunum"])
print("dis servis ham teslimde tip denetimi",
      d_tip([dict(k, endeks=str(k["endeks"])) for k in ham]))
```

```
kaynak 2400 satir = 200 abone x 12 donem
uygulama vt  teslim 2400 satir  sunum True  acik denetim 7  tasidigi sinif 3  yakalayan None
gunluk       teslim 2400 satir  sunum True  acik denetim 6  tasidigi sinif 5  yakalayan None
dis servis   teslim 2400 satir  sunum True  acik denetim 7  tasidigi sinif 6  yakalayan None
saglam sunum {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
dis servis ham teslimde tip denetimi True
```

Sağlam teslimde üç kaynak sınıfı **ayırt edilemez**: üçü de 2400 satır veriyor, aynı beş sunum
değerini üretiyor ve hiçbirinde denetim ateşlemiyor. Görünen tek fark günlükte açık denetim
sayısının yediden altıya inmesidir, çünkü orada beklenen satır sayısı diye bir referans yoktur.

Son satır kaynak sınıfının denetime nasıl karıştığını gösteriyor. Dış servis sayısal alanı metin
teslim ettiği için **hiçbir bozulma yokken**, on iki dönemin hepsinde tür denetimi kırmızıdır.
Sürekli kırmızı bir denetim kapalı bir denetimdir. Onu yeşile döndürmenin tek yolu ayrıştırıcıda
hoşgörülü çevirimdir ve bedeli aşağıda ödenir.

#### Kaynak Sınıfı Başına Bozulma Profili

Aşağıdaki blok her kaynak sınıfı için yalnız **o sınıfın taşıyabildiği** bozulmaları koşturur.

```python
profil, ozet = {}, {}
for ad, (u, tasidigi, acik) in KAYNAK.items():
    tb = taban_kur(u(ham))
    top, yok, dip, sessiz = 0, 0, 0, 0
    for sinif in BOZULMA:
        if sinif not in tasidigi:
            continue
        bozuk, n = boz(ham, sinif)
        teslim = u(bozuk)
        s, y, g = denetimli_hat(teslim, tb, acik)
        profil[(ad, sinif)] = (n, abs(len(teslim) - 2400), y, g, s == tb["taban_sunum"])
        sessiz += s == tb["taban_sunum"]
        if y is None:
            yok += 1
        else:
            top, dip = top + g, dip + (g >= 4)
    ozet[ad] = (len(tasidigi), top, yok, dip, sessiz)

print("bozulma         uygulama vt         gunluk      dis servis")
for sinif in BOZULMA:
    h = []
    for ad in KAYNAK:
        v = profil.get((ad, sinif))
        h.append("." if v is None else
                 ("yakalanmiyor" if v[2] is None else f"{v[2]}/{v[3]}"))
    print(f"{sinif:<14} {h[0]:>13} {h[1]:>14} {h[2]:>15}")

print("\nkaynak sinifi  sinif  bozulan satir  toplam gecikme  yakalanmayan  "
      "4. asamaya inen  sunumu degismeyen")
for ad, v in ozet.items():
    bs = sum(profil[(ad, s)][0] for s in BOZULMA if (ad, s) in profil)
    print(f"{ad:<14} {v[0]:>5} {bs:>14} {v[1]:>15} {v[2]:>13} {v[3]:>16} {v[4]:>18}")

print("\nsatir sayisi denetimi   pay 0.0500 -> 120 satir")
print("sinif          satirfark    oran  pay 0.0500  pay 0.0100  dayanak uretici")
for sinif in ("gec_gelen", "yinelenen", "eksik_bolum"):
    t = boz(ham, sinif)[0]
    f = abs(len(t) - 2400)
    d = ["yakalamaz", "yakalar"]
    print(f"{sinif:<14} {f:>9} {f / 2400:>7.4f} "
          f"{d[d_satir_sayisi(t, 2400)]:>11}{d[d_satir_sayisi(t, 2400, 0.01)]:>12}"
          f"{d[d_satir_sayisi(t, len(t), 0.01)]:>17}")
```

```
bozulma         uygulama vt         gunluk      dis servis
gec_gelen                  .      dagilim/4       dagilim/4
yinelenen                  .     tekillik/0      tekillik/0
sema_degisti          sema/0              .          sema/0
birim_degisti      dagilim/4      dagilim/4       dagilim/4
eksik_bolum    bolum_donem/0  bolum_donem/0   bolum_donem/0
kayan_tip                  .   yakalanmiyor    yakalanmiyor

kaynak sinifi  sinif  bozulan satir  toplam gecikme  yakalanmayan  4. asamaya inen  sunumu degismeyen
uygulama vt        3            446               4             0                1                  1
gunluk             5            546               8             1                2                  3
dis servis         6            746               8             1                2                  3

satir sayisi denetimi   pay 0.0500 -> 120 satir
sinif          satirfark    oran  pay 0.0500  pay 0.0100  dayanak uretici
gec_gelen             40  0.0167   yakalamaz     yakalar        yakalamaz
yinelenen             60  0.0250   yakalamaz     yakalar        yakalamaz
eksik_bolum           46  0.0192   yakalamaz     yakalar        yakalamaz
```

#### Profilin Okunması

Birinci tablo bir kaynak sınıfının **neyi üretemediğini** okutuyor. Uygulama veritabanının
satırındaki üç nokta üç güvencedir: tutarlı okuma geç gelen satırı, birincil anahtar yinelenen
satırı, bildirimli sütun türü tür kaymasını üretici tarafında olanaksız kılar. Bu güvenceler bedava
değildir, ama **hattın ölçmesi gereken sınıf sayısını yarıya indirir**: altıdan üçe, toplam gecikme
8 aşamadan 4 aşamaya.

Günlük ile dış servis aynı toplam gecikmeyi, **8 aşamayı**, farklı sınıflardan topluyor. Günlük şema
değişimi taşımaz, çünkü alan adlarını üretici değil ayrıştırıcı verir; dış servis altı sınıfın
hepsini taşır ve şema değişimini aşama 0'da yakalatır. Tek bir toplam sayı kaynak seçimini
gerekçelendirmez; tablonun satırları gerekçelendirir.

Son satır dersin en sessiz sonucudur. Tür kayması iki hoşgörülü kaynakta da **yakalanmıyor**: 200
satır bozuluyor, ayrıştırıcı metni sayıya çeviriyor, tür denetimi yeşil kalıyor ve sunulan beş değer
birebir aynı çıkıyor. İzi yok, çünkü onu görecek denetimin dayanağını ayrıştırıcı silmiş. Bu iki
kaynakta sunumu değişmeyen sınıf sayısı **üçtür**: yinelenen, birim değişimi ve tür kayması.

#### Dayanağı Üreticiden Gelen Denetim

Üçüncü tablo satır sayısı denetimini tek başına sınıyor. Öntanımlı tolerans **0,05**, yani 120
satırdır. Satır sayısını değiştiren üç sınıfın en büyüğü yalnız 60 satır, yani **0,0250** oynatır:
tolerans, ölçmek istediği en büyük sinyalin iki katıdır. Bu denetim üçünün hiçbirini yakalamaz ve on
iki dönem boyunca yeşil kalır; bir denetimin yeşil olması ölçüldüğü anlamına gelmez.

Toleransı **0,0100**'e indirmek üçünü de aşama 0'da yakalatır ve geç gelen satırın gecikmesini
dörtten sıfıra çeker; yanlış alarm bedeli de yoktur, çünkü sağlam teslim tam 2400 satırdır. Yine de
bu düzeltme üç kaynağın yalnız birinde uygulanabilir: günlükte referans yoktur, dış serviste vardır
ama **üreticinin kendi beyanıdır** — servis kaç satır gönderdiğini söyler, alınan da odur, oran
0,0000 çıkar. Bir denetimin değeri eşiğinden değil **dayanağının nereden geldiğinden** gelir.

#### Özet

- Bir kaynak sınıfı ürün adıyla değil üç şeyle tanımlanır: bağdaştırıcısı, taşıyabildiği bozulma
  sınıfları ve dayanağı olan denetimler. Sağlam teslimde üçü de aynı sayıyı verir; bedeli ancak
  bozulma geldiğinde görünür.
- Üretici tarafındaki güvence taşınan sınıf sayısını düşürür: bildirimli tür, birincil anahtar ve
  tutarlı okuma uygulama veritabanını altı sınıftan üçe indirir, toplam gecikmesi 8 yerine 4 olur.
- Günlük ile dış servis aynı toplam gecikmeyi farklı sınıflardan toplar; kaynak seçimi toplam
  sayıyla değil profilin satırlarıyla gerekçelendirilir.
- Hoşgörülü ayrıştırma tür denetiminin dayanağını siler: 200 satır bozulur, yakalayan olmaz, sunulan
  beş değer aynı kalır; sunumu değiştirmeyen sınıf sayısı üçe çıkar.
- Satır sayısı denetimi 0,05 toleransla hiçbir sınıfı yakalamaz, çünkü en büyük satır farkı 0,0250
  oynatır; dayanak üreticinin beyanıysa hiçbir tolerans yakalatmaz.

#### Sonraki Adım

Bu dersin profili on iki dönemin **hepsi elde varken** çıkarıldı: bozulmuş dönem hattın içinde
duruyordu ve denetimler onu kalan on bir dönemle karşılaştırabiliyordu. Bir hat böyle çalışmaz; bir
çizelgeye bağlıdır ve çizelge bozulmuş dönemin ne zaman görüneceğini belirler. Sonraki ders aynı
hattı iki kipte koşturur: on iki dönemi tek seferde işleyen toplu kip ile dönemi geldiğinde işleyen
akış kipi. Ölçülecek takas şudur: toplu kipte bozulma **bir dönem sonra** görünür, akışta **aynı
dönemde**; karşılığında akış hattın aşama sayısını ve taşınan durumu büyütür.

### Toplu ve Akış İşleme

Source: https://academia.sh/tr/kurslar/veri-muhendisligine-giris/toplu-ve-akis-isleme

Aynı hattın iki kipte koşturulması ve tazelik ile yakalama gecikmesinin takasının sayılması: toplu kipte bozulma bir dönem sonra, 2026-08 koşumunda görünüyor, akış kipinde aynı dönemde, 2026-07 penceresinde. Karşılığında akış aşama sayısını 5'ten 8'e, taşınan durumu 0'dan 2600 girdiye çıkarıyor; buna karşılık okunan satır 13200'den 2400'e iniyor. Dönem penceresi kusurlu bölüm denetimini düzeltiyor: toplu kipte hiç yakalanmayan eksik bölüm akışta aşama 0'da yakalanıyor. Geç gelen satırda takas tersine dönüyor: toplu kip satırı sonraki koşumda geri alıp 55 özet hücresinin hepsini doğru bırakırken akış 40 satırı ilerleme damgasınde düşürüyor ve 10 hücreyi kalıcı olarak kirletiyor, ama sunulan beş değer iki kipte de birebir aynı kalıyor.

Önceki ders üç kaynak sınıfının profilini on iki dönemin **hepsi elde varken** çıkardı: bozulmuş
dönem hattın içinde duruyordu ve kalan on bir dönemle karşılaştırılabiliyordu. Bir hat böyle
çalışmaz; bir çizelgeye bağlıdır ve çizelge bozulmuş döneme **ne zaman bakılacağını** belirler.

Bu ders aynı hattı iki kipte koşturur. **Toplu işleme** (batch processing) dönem kapandıktan sonra
çalışır ve o ana kadar gelmiş bütün satırları baştan işler; **akış işleme** (stream processing)
satırı geldiği dönemde işler ve sonraki döneme durum taşır. İkisi aynı beş aşamayı, aynı altı
bozulma sınıfını ve aynı denetimleri kullanır; tek farkları **penceredir**.

- **YD47.** Kod **kurgudur**; gerçek bir akış sistemi, kuyruk ürünü ya da orkestratör çağrılmaz.
  Kaynak önceki dersin kurgu sayaç okumalarıdır: 200 abone, 12 dönem, 2400 satır, tohum 20260218.
- **YD48. Kuyruk, yeniden deneme ve gecikme yordamı tekrarlanmaz**; M16'nın Önbellekleme, Kuyruklar
  ve Eşzamansız İşleme kursunda ve M19'un Uygulama Katmanı ve Servis Etkileşimi kursunda ölçüldü.
  Buraya eklenen tek şey bozulmanın **hangi dönemde görüldüğüdür**.
- **YD49.** Toplu kipte koşum, o ana kadar **gelmiş** bütün satırları baştan işler; akış kipinde
  satır **geldiği dönemde** işlenir ve ilerleme damgası (watermark) penceresi kapanmış satırı düşürür.
- **YD50.** Geç gelen satır bu derste "hiç gelmedi" değil **bir dönem sonra geldi** sayılır. Bozulma
  sınıfı aynıdır; eklenen tek şey satırın **geliş dönemidir**.
- **YD51.** İki kipte de aynı beş denetim açıktır: şema, tür, tekillik, bölüm ve dağılım. Bölüm
  denetimi kusurlu olanıdır — bölge kümesine bakar, dönemi görmez.
- **YD52.** Gecikme iki birimde yazılır: **dönem** ve **aşama**. Özet hücresi oranının adımı 1/55,
  yani 0,0182.

#### İki Kip, Tek Hat

Aşağıdaki blok ortak tanımı kurar ve iki koşucu ekler. `toplu` her dönem başında, o ana kadar gelmiş
satırları **baştan** işler ve hiçbir şey taşımaz. `akis` her dönemi kendi penceresinde işler ve iki
şey taşır: görülen anahtar kümesi ile abone başına son endeks — tekilleştirme ve tüketim
aşamalarının toplu kipte bedava bulduğu bilgi.

```python
# KURGUDUR. Gercek bir akis sistemi, kuyruk ya da orkestrator cagrilmaz; olcum
# aginin sayac okumalari kurgudur, tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():                                  # her donem her abone icin bir okuma
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":                # bu donemde 40 abone hic gelmedi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":                # 60 kayit iki kez geldi
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000        # m3 yerine litre
            y["birim"] = "m3"                       # ETIKET DEGISMEDI , sessiz bozulma
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":               # bir bolgenin bolumu hic yazilmadi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])          # sayi metne dondu
            sayac += 1
            yeni.append(y)
    return yeni, sayac


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        top[(k["bolge"], k["donem"])] = top.get((k["bolge"], k["donem"]), 0) + k["m3"]
    return top, 0


def a5_sun(ozet):                                   # bolge basina donem ortalamasi
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]


def d_sema(veri, alanlar=("abone", "donem", "bolge", "endeks", "birim")):
    return any(any(a not in k for a in alanlar) for k in veri)


def d_tip(veri):
    return any(not isinstance(k.get("endeks"), int) for k in veri)


def d_tekillik(veri):
    ad = [(k["abone"], k["donem"]) for k in veri]
    return len(ad) != len(set(ad))


def d_bolum_tamligi(veri):
    """Bolge kumesine bakar , donemi gormez: tum tarihte kusurlu , pencerede degil."""
    return set(k["bolge"] for k in veri) != set(BOLGE)


def d_dagilim(ozet, taban, pay=0.25):
    for ad, v in ozet.items():
        t = taban.get(ad)
        if t and abs(v - t) / max(abs(t), 1) > pay:
            return True
    return False


DENETIM = [("sema", 0, lambda v, t: d_sema(v)),
           ("tip", 0, lambda v, t: d_tip(v)),
           ("tekillik", 0, lambda v, t: d_tekillik(v)),
           ("bolum", 0, lambda v, t: d_bolum_tamligi(v)),
           ("dagilim", 4, lambda v, t: d_dagilim(v, t))]


def tam_hat(kayit):
    v, _ = a1_ayikla(kayit)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    oz, _ = a4_ozet(v)
    return oz


def teslim_et(kayit, sinif):
    """Her satira bir gelis donemi verir ; gec gelen satir bir donem sonra gelir."""
    bozuk, n = boz(kayit, sinif)
    veri = [dict(k, gelis=k["donem"]) for k in bozuk]
    if sinif == "gec_gelen":
        veri += [dict(k, gelis="2026-08") for k in kayit
                 if k["donem"] == "2026-07" and k["abone"] <= "A040"]
    return veri, n


ASAMA_TOPLU = [a for a, _ in ASAMA]
ASAMA_AKIS = ["ilerleme_damgasi"] + ASAMA_TOPLU + ["durum_anahtar", "durum_endeks"]


def toplu(veri, taban):
    """Kosum , bir onceki donemin sonuna kadar GELMIS butun satirlari bastan isler."""
    okunan, yak, asama, donem = 0, None, None, None
    for i, d in enumerate(DONEM):
        gelmis = [k for k in veri if DONEM.index(k["gelis"]) < i]
        okunan += len(gelmis)
        v = gelmis
        for adim in range(6):
            if adim > 0 and v:
                v, _ = ASAMA[adim - 1][1](v)
            for ad, das, df in DENETIM:
                if das == adim and yak is None and v and df(v, taban):
                    yak, asama, donem = ad, adim, d
    return {"okunan": okunan, "yakalayan": yak, "asama": asama, "donem": donem,
            "durum": 0, "gec_dusen": 0, "hucre": tam_hat(veri)}


def akis(veri, taban):
    """Donem penceresi: satir geldigi donemde islenir , durum sonraki doneme tasinir."""
    gorulen, son_endeks, hucre = set(), {}, {}
    okunan, gec_dusen, durum = 0, 0, 0
    yak, asama, donem = None, None, None
    for d in DONEM:
        pencere = [k for k in veri if k["gelis"] == d]
        okunan += len(pencere)
        v = [k for k in pencere if k["donem"] == d]         # su isareti: pencere kapandi
        gec_dusen += len(pencere) - len(v)
        for adim in range(5):
            if adim == 1:
                v, _ = a1_ayikla(v)
            elif adim == 2:
                yeni, g = [], set()
                for k in v:
                    ad = (k["abone"], k["donem"])
                    if ad not in gorulen and ad not in g:
                        g.add(ad)
                        yeni.append(k)
                gorulen |= g                                # durum: gorulen anahtar
                v = yeni
            elif adim == 3:
                c = []
                for k in v:
                    try:
                        c.append(dict(k, m3=int(k["endeks"]) - int(son_endeks[k["abone"]])))
                    except (KeyError, TypeError, ValueError):
                        pass
                for k in v:
                    try:
                        son_endeks[k["abone"]] = int(k["endeks"])   # durum: son endeks
                    except (TypeError, ValueError):
                        pass
                v = c
            elif adim == 4:
                o = {}
                for k in v:
                    o[(k["bolge"], d)] = o.get((k["bolge"], d), 0) + k["m3"]
                hucre.update(o)
                v = o
            for ad, das, df in DENETIM:
                if das == adim and yak is None and v and df(v, taban):
                    yak, asama, donem = ad, adim, d
        durum = max(durum, len(gorulen) + len(son_endeks))
    return {"okunan": okunan, "yakalayan": yak, "asama": asama, "donem": donem,
            "durum": durum, "gec_dusen": gec_dusen, "hucre": hucre}


ham = kaynak_uret()
TABAN = tam_hat(ham)
saglam = [dict(k, gelis=k["donem"]) for k in ham]
st, sa = toplu(saglam, TABAN), akis(saglam, TABAN)
print("taban ozet", len(TABAN), "hucre , sunum", a5_sun(TABAN)[0])
print("kip    asama  okunan satir  tasinan durum  yakalayan")
for ad, r, n in (("toplu", st, len(ASAMA_TOPLU)), ("akis", sa, len(ASAMA_AKIS))):
    print(f"{ad:<6} {n:>6} {r['okunan']:>13} {r['durum']:>14}  {r['yakalayan']}")
```

```
taban ozet 55 hucre , sunum {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
kip    asama  okunan satir  tasinan durum  yakalayan
toplu       5         13200              0  None
akis        8          2400           2600  None
```

Sağlam koşumda iki kip de aynı 55 özet hücresini ve aynı beş sunum değerini üretiyor, ikisinde de
hiçbir denetim ateşlemiyor. Bedel satırı burada okunuyor. Toplu kip on iki koşumda **13200 satır**
okuyor, çünkü her koşum birikmiş tarihi baştan işliyor; akış her satırı bir kez, **2400 satır**
okuyor. Buna karşılık akış hattın aşamasını **5'ten 8'e** çıkarıyor — ilerleme damgası, görülen anahtarın
ve son endeksin saklanması — ve en çok **2600 girdi** taşıyor: 2400 anahtar ile 200 abone endeksi.
Toplu kipin taşıdığı durum sıfırdır.

#### Yakalama Dönemi

Aşağıdaki blok altı bozulma sınıfını iki kipte koşturur ve her birinin hangi dönemde, hangi
denetimle yakalandığını yazar.

```python
sonuc = {}
for sinif in BOZULMA:
    veri, n = teslim_et(ham, sinif)
    sonuc[sinif] = (n, toplu(veri, TABAN), akis(veri, TABAN))


def etiket(r):
    if r["yakalayan"] is None:
        return "yakalanmiyor"
    return f"{r['yakalayan']}/{r['donem']}/{DONEM.index(r['donem']) - DONEM.index('2026-07')}"


def kirli(h):
    return sum(1 for ad in set(h) | set(TABAN) if h.get(ad) != TABAN.get(ad))


print("bozulma        bozulan  toplu: denetim/donem/gecikme  akis: denetim/donem/gecikme")
for s, (n, t, a) in sonuc.items():
    print(f"{s:<14} {n:>7}  {etiket(t):<28} {etiket(a)}")

print("\nbozulma         kirli hucre/55   sunum degisti   akista")
print("                toplu    akis    toplu   akis    dusen satir")
for s, (n, t, a) in sonuc.items():
    e = ["hayir", "evet"]
    print(f"{s:<14} {kirli(t['hucre']):>7} {kirli(a['hucre']):>7} "
          f"{e[a5_sun(t['hucre'])[0] != a5_sun(TABAN)[0]]:>8}"
          f"{e[a5_sun(a['hucre'])[0] != a5_sun(TABAN)[0]]:>7} {a['gec_dusen']:>10}")
```

```
bozulma        bozulan  toplu: denetim/donem/gecikme  akis: denetim/donem/gecikme
gec_gelen           40  dagilim/2026-08/1            dagilim/2026-07/0
yinelenen           60  tekillik/2026-08/1           tekillik/2026-07/0
sema_degisti       200  sema/2026-08/1               sema/2026-07/0
birim_degisti      200  dagilim/2026-08/1            dagilim/2026-07/0
eksik_bolum         46  yakalanmiyor                 bolum/2026-07/0
kayan_tip          200  tip/2026-08/1                tip/2026-07/0

bozulma         kirli hucre/55   sunum degisti   akista
                toplu    akis    toplu   akis    dusen satir
gec_gelen            0      10    hayir  hayir         40
yinelenen            0       0    hayir  hayir          0
sema_degisti        10      10     evet   evet          0
birim_degisti       10      10    hayir  hayir          0
eksik_bolum          2       2     evet   evet          0
kayan_tip            0       0    hayir  hayir          0
```

#### Bir Dönem Erken Bakmak

Birinci tablo takası tek satırda veriyor: yakalanan beş sınıfın hepsinde toplu kip **2026-08**, akış
kipi **2026-07** yazıyor. Dönem gecikmesi toplu kipte **1**, akışta **0**. Aşama gecikmeleri ise
birebir aynı: şema, tür ve tekillik aşama 0'da, dağılım aşama 4'te yakalıyor. Kip, bozulmanın hangi
**aşamada** yakalandığını değiştirmiyor; yalnız hangi **dönemde** bakıldığını değiştiriyor. Tazelik
burada bir sunum özelliği değil, bir denetim özelliğidir.

Bir dönemin ne demek olduğu çıktıda görünür: toplu kipte bozulmuş dönem, yakalandığı ana kadar bir
kez sunulmuştur — 2026-07 kapanmış, özet yazılmış, sunum değeri okunmuştur. Bu fark bir tolerans
ayarıyla kapatılamaz, çünkü toplu kipin gecikmesi denetimden değil **çizelgeden** gelir.

İkinci tablo kipten bağımsız olanı gösteriyor. Yinelenen satır, tür kayması ve birim değişimi
**sunulan beş değeri iki kipte de değiştirmiyor**; ilk ikisi kirli hücre bile bırakmıyor, birim
değişimi on hücreyi kirletiyor ama sunum kusursuz görünüyor. Bu üçünü gören tek şey denetimdir.

#### Pencerenin Denetimi Düzeltmesi

Beşinci satır tablonun en beklenmedik sonucudur. Eksik bölüm toplu kipte **hiç yakalanmıyor**,
akışta **aşama 0'da** yakalanıyor — üstelik iki kipte de **aynı denetim kodu** koşuyor. Bölüm
denetimi bölge kümesine bakar ve dönemi görmez; toplu koşumda önüne on iki dönemin tamamı gelir,
kuzey öbür on bir dönemde göründüğü için küme tamdır ve denetim yeşil kalır. Akış
koşumunda aynı denetimin önüne gelen küme **tek bir dönemin penceresidir**; orada kuzey yoktur ve
denetim ateşler.

Bir denetimin doğruluğu yalnız kodundan değil **önüne konan kümeden** de gelir. Bu, akış kipinin bir
üstünlüğü gibi görünse de bir yan etkidir; toplu kipte aynı sonucu almanın yolu kipi değiştirmek
değil denetimi dönem duyarlı yazmaktır.

#### Durumun Bedeli

İkinci tablonun ilk satırı takası tersine çeviriyor. Geç gelen kırk satır bir dönem sonra
geldiğinde toplu kip birikmiş tarihi baştan işler ve satırları **geri doldurur**: 55 özet hücresinin
tamamı doğru kalır. Akış aynı satırları ilerleme damgasınde düşürür ve pencere kapandığı için bir daha
bakmaz. Sonuç **10 kirli özet hücresidir**, yani hücrelerin 0,1818'i.

Mekanizma taşınan durumdur. Kırk abonenin 2026-07 okuması hiç işlenmediği için son endeks durumu
2026-06'da kalır; 2026-08 geldiğinde tüketim iki dönemlik farktan hesaplanır. Toplu kipte böyle bir
satır **düşer**, çünkü önceki dönem tabloda yoktur; akışta **yanlış hesaplanır**, çünkü durumda eski
bir değer vardır. Eksik satır sayılabilir bir kayıptır, yanlış değer değildir.

Son sütun bu bölümün asıl uyarısıdır. On kirli hücreye rağmen **sunulan beş değer iki kipte de
birebir aynıdır**: temmuzda eksilen ağustosta fazlalanıyor, on iki dönemin ortalaması oynamıyor.
Akış kipi bu bozulmayı 2026-07'de yakalamıştı; yakalamasaydı hiçbir çıktıya bakarak bulunamayacaktı.

#### Özet

- Toplu kip dönem kapandıktan sonra birikmiş tarihi baştan işler, akış satırı geldiği dönemde
  işler; ikisi de aynı beş aşamayı ve aynı beş denetimi kullanır, tek farkları penceredir.
- Yakalanan beş sınıfın hepsinde toplu kipin dönem gecikmesi 1, akışınki 0'dır; aşama gecikmeleri
  birebir aynıdır. Kip, bozulmanın hangi aşamada değil hangi dönemde görüldüğünü değiştirir.
- Akışın bedeli aşama sayısının 5'ten 8'e çıkması ve 2600 girdi taşınmasıdır; karşılığında okunan
  satır 13200'den 2400'e iner.
- Dönem penceresi kusurlu bölüm denetimini düzeltir: aynı kod toplu kipte eksik bölümü hiç
  yakalamaz, akışta aşama 0'da yakalar.
- Geç gelen satırda takas tersine döner: toplu kip 55 hücrenin hepsini doğru bırakır, akış 40 satırı
  düşürüp 10 hücreyi kirletir; buna rağmen sunulan beş değer iki kipte de aynıdır.

#### Sonraki Adım

Beş derste hat kuruldu ve sayıldı: aşamaları belirlendi, her aşamanın düşürdüğü satır yazıldı,
rollerin nerede durduğu ayrıldı, veri kaynak sınıflarına ayrıştırıldı ve hangi çizelgeyle koşacağı
iki kipte ölçüldü. Bir şey hiç sorulmadı: hattın taşıdığı satırın **doğru olup olmadığı**. Sayılan
her şey satırın nereden geldiği, nereye gittiği ve ne zaman bakıldığıdır; satırın kendisine bakan
tek bir ölçüt yazılmadı. Denetimler hazır bulundu, ama **nereden geldikleri** tanımlanmadı. Sonraki
konu bunu yapar: kalite boyutlarını ölçülebilir denetimlere çevirir, her denetimin yakalama
gecikmesini sayar ve denetimin **kendi kusurunu** ölçer.

## Kalite ve İlkeler

Kalite boyutlarının çalışan denetimlere çevrilmesi ve denetimin hem yakaladığının hem kendi kusurunun sayılması: altı bozulma sınıfının yakalama gecikmesi ve kusurlu denetim kümesiyle hiç yakalanmayan üç sınıf, bölüm denetiminin bölge kümesine bakıp dönemi görmediği için bir bölgenin tek döneminin hiç yazılmamasına rağmen yeşil kalması, şema değişiminin düşürdüğü satır ile veri sözleşmesinin yakaladığı aşama ve sözleşmenin kendi bedeli, yinelemeye dayanıklı olmayan hatta yeniden işlemenin satırı çoğaltması ve tekilleştirme aşaması yuttuğu için sunulan sayının değişmemesi, bozulmuş bir sunum değerinden kaynağa gitmek için bakılan aşama ve satır sayısının soyağacı kaydı varken ve yokken karşılaştırılması, beş aşamanın kaçının sahip taşıdığı ve sahipsiz aşamada yakalanan bozulmanın kime gittiği ile saklama politikasının geriye dönük denetlenemez kıldığı dönem sayısı.

### Veri Kalitesi Boyutları

Source: https://academia.sh/tr/kurslar/veri-muhendisligine-giris/veri-kalitesi-boyutlari

Kalite boyutlarının çalışan denetimlere çevrilmesi ve her denetimin hem yakaladığının hem kendi kusurunun sayılması: altı bozulma sınıfının yakalama gecikmesi sırasıyla 4, 0, 0, 4, 0 ve 0 aşamadır; kusurlu denetim kümesiyle üç sınıf hiç yakalanmaz; bölüm denetimi bölge kümesine baktığı için bir bölgenin tek dönemi hiç yazılmadığında yeşil kalır; birim değişimi özetin on hücresini oynatıp sunumu birebir aynı bırakır.

Beş derste hat kuruldu ve sayıldı: hangi aşamanın kaç satır düşürdüğü, toplu ile akış işlemenin
tazeliği nasıl değiştirdiği. O sayıların tamamı tek bir soruya yanıt veriyordu, kaç satır. Hiçbir
aşamada satırın **doğru olup olmadığı** sorulmadı.

Bunlar iki ayrı sorudur ve aralarındaki fark bir veri hattının kendine özgü kusurudur: hat
çöktüğünde haber verir, **yanlış veri taşıdığında hiçbir şey söylemez.** Bu ders kalite boyutlarını
çalışan denetimlere çevirir, altı bozulma sınıfının kaç aşama sonra yakalandığını sayar ve
denetimin kendisinin de kusurlu olabileceğini aynı kümede gösterir.

- **VK1.** Kaynak, beş aşamalı hat, bozulma sınıfları ve denetimler **kurgudur**; gerçek bir
  veritabanı, dosya biçimi ya da orkestratör çağrılmaz. Tohum **20260218**, hat koşumdan
  bağımsızdır.
- **VK2.** **Bozulma sınıfları saldırı değildir**, üretici tarafı kazalarıdır: geciken dosya, iki
  kez gönderilen yük, adı değişen alan, birimi değişen sayaç.
- **VK3.** Taban çizgisi **denetimsiz hattır**: yakalanan bozulma **0**, gecikme **tanımsız**.
- **VK4.** Her bozulma **tek bir dönemi** (`2026-07`) bozar; kalan on bir dönem sağlam kalır.
- **VK5.** Temizleme, eksik ve aykırı değer yordamı Veri Toplama ve Hazırlama kursunda, sızıntı ile
  etiket gürültüsü Veri Hazırlama ve Öznitelik Mühendisliği kursunda ölçüldü ve **tekrarlanmaz**.
- **VK6.** Gecikme **aşama** olarak yazılır, ham süre yazılmaz: aşama 0 kaynağın kendisi, 1'den
  5'e hattın aşamalarıdır.
- **VK7.** Çözünürlük ölçüm bandının alt sınırıdır: satır oranının adımı **0,0004**, özet
  hücresinin **0,0182**, sunum değerinin **0,2000**. **Sunum üzerinde oran basılmaz.**

#### Boyut Bir Cümle Değil Bir Denetimdir

**Veri kalitesi boyutu (data quality dimension)** çoğunlukla bir cümle olarak yazılır: "veri
eksiksiz olmalı", "veri tutarlı olmalı". Bu cümleler ölçülemez, çünkü hangi kümede, hangi aşamada
ve neye karşı bakılacağını söylemezler. Boyut ancak bir **yordama** çevrildiğinde ölçülür: belirli
bir aşamadan sonra koşan ve doğru ya da yanlış döndüren bir işlev.

| Boyut | Yordamın sorduğu | Denetim | Aşama |
|---|---|---|---|
| bütünlük (completeness) | beklenen satırın hepsi geldi mi | `satir_sayisi`, `bolum`, `bolum_donem` | 0 |
| tekillik (uniqueness) | aynı anahtar iki kez var mı | `tekillik` | 0 |
| geçerlilik (validity) | alanlar beklenen adı ve türü taşıyor mu | `sema`, `tip` | 0 |
| tutarlılık (consistency) | değerler önceki dönemlerin düzeninde mi | `dagilim` | 4 |
| doğruluk (accuracy) | değer gerçeği yansıtıyor mu | **yok** | — |

Son satır tabloya bilerek konmuştur. **Doğruluk (accuracy)** hattın içinden ölçülemez: sayacın
gerçekten kaç metreküp geçirdiğini bilmek için hattın dışında bir ölçüm gerekir ve bu hatta öyle
bir kaynak yok. Ölçülebilen şey **tutarlılıktır**; kalite raporunda "doğruluk yüzde doksan dokuz"
yazan satır çoğu kez tutarlılığı ölçüp adına doğruluk demiştir. Aşama sütunu ikinci bir şey söyler:
`dagilim` kaynağa değil dördüncü aşamanın ürettiği özete bakar.

#### Ortak Tanım

Birinci blok kaynağı, beş aşamalı hattı, altı bozulma sınıfını ve yedi denetimi kurar.
`denetimli_hat` her aşamadan sonra açık denetimleri işletir; **ilk yakalayan denetimi** ile
**yakalama gecikmesini** döndürür.

```python
# KURGUDUR. Olcum aginin sayac okumalari, bes asamali hat, bozulma siniflari ve
# denetimler ders icinde tanimlanir; gercek bir depo ya da orkestrator cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


# ---- KURGU kaynak: olcum aginin sayac okumalari, on iki donem
BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


# Her donem her abone icin bir okuma. Birim m3 , endeks artan.
def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


# ---- KURGU bozulma siniflari. Hicbiri bir saldiri degildir; hepsi
# gunluk hayatta olan uretici tarafi kazalaridir.
BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":                # bu donemde 40 abone hic gelmedi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":                # 60 kayit iki kez geldi
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000        # m3 yerine litre
            y["birim"] = "m3"                       # ETIKET DEGISMEDI , sessiz bozulma
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":               # bir bolgenin bolumu hic yazilmadi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])          # sayi metne dondu
            sayac += 1
            yeni.append(y)
    return yeni, sayac


# ---- HAT: bes asama. Her asama kendi cikardigi satiri sayar.
def a1_ayikla(kayit):
    """Zorunlu alanlari tasimayan satir dusurulur."""
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    """Sunum: bolge basina donem ortalamasi."""
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]


def hat(kayit):
    iz, veri = [], kayit
    for ad, f in ASAMA:
        veri, dusen = f(veri)
        iz.append({"asama": ad, "kalan": len(veri), "dusen": dusen})
    return veri, iz


# ---- DENETIMLER: her biri belirli bir asamadan SONRA kosar
def d_sema(veri, alanlar=("abone", "donem", "bolge", "endeks", "birim")):
    return any(any(a not in k for a in alanlar) for k in veri)


def d_tip(veri):
    return any(not isinstance(k.get("endeks"), int) for k in veri)


def d_tekillik(veri):
    ad = [(k["abone"], k["donem"]) for k in veri]
    return len(ad) != len(set(ad))


def d_satir_sayisi(veri, beklenen, pay=0.05):
    return abs(len(veri) - beklenen) / beklenen > pay


def d_bolum_tamligi(veri):
    """KUSURLU DENETIM: bolge kumesine bakar , donemi gormez."""
    return set(k["bolge"] for k in veri) != set(BOLGE)


def d_bolum_tamligi_donem(veri):
    """DOGRU DENETIM: her donemde her bolge var mi."""
    gor = set((k["bolge"], k["donem"]) for k in veri)
    return gor != set((b, d) for b in BOLGE for d in DONEM)


def d_dagilim(ozet, taban, pay=0.25):
    for ad, v in ozet.items():
        t = taban.get(ad)
        if t and abs(v - t) / max(abs(t), 1) > pay:
            return True
    return False


# Denetim kaydi: (ad , asama , islev). Asama 0 = kaynak , 1..5 = ASAMA sirasi.
DENETIM = [
    ("sema", 0, lambda v, t: d_sema(v)),
    ("tip", 0, lambda v, t: d_tip(v)),
    ("tekillik", 0, lambda v, t: d_tekillik(v)),
    ("satir_sayisi", 0, lambda v, t: d_satir_sayisi(v, t["beklenen_satir"])),
    ("bolum", 0, lambda v, t: d_bolum_tamligi(v)),
    ("bolum_donem", 0, lambda v, t: d_bolum_tamligi_donem(v)),
    ("dagilim", 4, lambda v, t: d_dagilim(v, t["taban_ozet"])),
]


def denetimli_hat(kayit, taban, acik=None):
    """Hatti kosturur ve her asamadan sonra acik denetimleri isletir.
    Donen deger: sunum , iz , ilk yakalayan denetim ve yakalama gecikmesi (asama)."""
    acik = [a for a, _, _ in DENETIM] if acik is None else acik
    veri, iz, yakalayan, gecikme = kayit, [], None, None
    for adim in range(len(ASAMA) + 1):
        if adim > 0:
            ad, f = ASAMA[adim - 1]
            veri, dusen = f(veri)
            iz.append({"asama": ad, "kalan": len(veri), "dusen": dusen})
        for dad, dasama, df in DENETIM:
            if dad not in acik or dasama != adim:
                continue
            if df(veri, taban):
                if yakalayan is None:
                    yakalayan, gecikme = dad, adim
    sunum = veri if isinstance(veri, dict) and veri and isinstance(
        list(veri.values())[0], float) else None
    return sunum, iz, yakalayan, gecikme


def taban_kur(kaynak):
    v1, _ = a1_ayikla(kaynak)
    v2, _ = a2_tekille(v1)
    v3, _ = a3_tuketim(v2)
    oz, _ = a4_ozet(v3)
    sun, _ = a5_sun(oz)
    return {"beklenen_satir": len(kaynak), "taban_ozet": oz, "taban_sunum": sun}
```

#### Yakalama Gecikmesi

İkinci blok kuralı sayıya çevirir. Her bozulma sınıfı için hat iki kez koşuyor: bir kez yedi
denetim açıkken, bir kez `["sema", "tip", "tekillik", "bolum"]` kümesiyle. İkinci küme kusurludur,
çünkü dönem duyarlı bütünlük denetimi ile dağılım denetimi kapalıdır. Son sütun sunulan beş değerin
tabanla birebir aynı kalıp kalmadığını yazar.

```python
KAYNAK_KAYIT = kaynak_uret()
TABAN = taban_kur(KAYNAK_KAYIT)
HEPSI = [a for a, _, _ in DENETIM]
KUSURLU = ["sema", "tip", "tekillik", "bolum"]

_, iz = hat(KAYNAK_KAYIT)
print("saglam hat:", " ".join(f"{i['asama']}={i['kalan']}" for i in iz))
print("sunum:", TABAN["taban_sunum"])
print("saglam hatta ateslenen denetim:", denetimli_hat(KAYNAK_KAYIT, TABAN, HEPSI)[2])
print()
print(f"{'bozulma':<15}{'satir':>6}{'yakalayan':>13}{'gecikme':>9}"
      f"{'kusurlu kume':>14}{'sunum':>9}")
for sinif in BOZULMA:
    bozuk, n = boz(KAYNAK_KAYIT, sinif)
    sunum, _, yak, gec = denetimli_hat(bozuk, TABAN, HEPSI)
    _, _, kyak, _ = denetimli_hat(bozuk, TABAN, KUSURLU)
    durum = "ayni" if sunum == TABAN["taban_sunum"] else "degisti"
    print(f"{sinif:<15}{n:>6}{yak:>13}{gec:>9}{str(kyak):>14}{durum:>9}")
```

```
saglam hat: ayikla=2400 tekille=2400 tuketim=2200 ozet=55 sun=5
sunum: {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
saglam hatta ateslenen denetim: None

bozulma         satir    yakalayan  gecikme  kusurlu kume    sunum
gec_gelen          40      dagilim        4          None  degisti
yinelenen          60     tekillik        0      tekillik     ayni
sema_degisti      200         sema        0          sema  degisti
birim_degisti     200      dagilim        4          None     ayni
eksik_bolum        46  bolum_donem        0          None  degisti
kayan_tip         200          tip        0           tip     ayni
```

İlk üç satır tabanı verir: sağlam kaynakta beş aşama 2400'den 5 değere iniyor ve **hiçbir denetim
ateşlemiyor**. Bu, denetim kümesinin iyi satırı yanlış engellemediğinin kanıtıdır.

Gecikme sütunu kuralın kendisidir. Dört sınıf **aşama 0**'da, kaynağın kendisinde yakalanıyor; hat
hiç kirlenmiyor. İki sınıf ise **aşama 4**'te: `gec_gelen` ve `birim_degisti` yalnızca dördüncü
aşamanın özetine bakan dağılım denetimiyle görünür oluyor. Aradaki dört aşama bir maliyet
birimidir, çünkü bozulmuş satır o dört aşama boyunca ayıklanmış, tekilleştirilmiş, tüketime
çevrilmiş ve özete toplanmıştır; düzeltmeden sonra geri doldurulacak olan da bu dört aşamanın
çıktısıdır.

Son sütun kursun üçüncü iddiasını taşır: **üç sınıf sunulan sayıyı birebir aynı bırakıyor.**
`yinelenen`, `birim_degisti` ve `kayan_tip` sonrasında beş sunum değeri tabanla aynıdır. Çıktıya
bakan hiçbir gözlem bu üçünü göremez; tek kanıt denetimin kendisidir. `kayan_tip` en uç örnektir:
endeks metne döndüğünde tüketim aşaması onu yine sayıya çeviriyor, **tek bir satır bile düşmüyor**.

Dördüncü sütun kusurlu kümeyi ölçer: **üç sınıf hiç yakalanmıyor.** `gec_gelen` ile `birim_degisti`
dağılım denetimi kapalı olduğu için, `eksik_bolum` ise açık olan `bolum` denetimi kusurlu olduğu
için. Bu kümeyi kuran ekip dört denetim yazmış, dördü de yeşil yanmış ve hattın altıda üçü
ölçülmemiş kalmıştır.

#### Yeşil Kalan Denetim

Üçüncü blok kusuru açar. `eksik_bolum` bozulmasında kuzey bölgesinin `2026-07` dönemi hiç
yazılmıyor. `d_bolum_tamligi` bölge kümesini beklenen kümeyle karşılaştırır; kuzey öbür on bir
dönemde göründüğü için küme tamdır ve denetim **geçer**. `d_bolum_tamligi_donem` ise bölge ile
dönemin çiftine bakar ve aynı kaynakta ateşler.

```python
def ara_ozet(kayit):
    """Dorduncu asamanin cikisi: bolge ve donem basina toplam."""
    v, _ = a1_ayikla(kayit)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return a4_ozet(v)[0]


bozuk, n = boz(KAYNAK_KAYIT, "eksik_bolum")
print("yazilmayan satir:", n, " kuzeyin 2026-07 satiri:",
      sum(1 for k in bozuk if k["bolge"] == "kuzey" and k["donem"] == "2026-07"))
print("kaynaktaki bolge kumesi:", sorted(set(k["bolge"] for k in bozuk)))
print("kusurlu bolum denetimi:", d_bolum_tamligi(bozuk),
      " donem duyarli denetim:", d_bolum_tamligi_donem(bozuk))
oz1 = ara_ozet(bozuk)
print("ozet hucresi:", len(oz1), " kuzeyin ozetteki donem sayisi:",
      sum(1 for b, d in oz1 if b == "kuzey"))
print("sunum kuzey:", a5_sun(oz1)[0]["kuzey"], " taban:", TABAN["taban_sunum"]["kuzey"])

print()
oz2 = ara_ozet(boz(KAYNAK_KAYIT, "birim_degisti")[0])
for ad in (("kuzey", "2026-06"), ("kuzey", "2026-07"), ("kuzey", "2026-08")):
    print("ozet", ad, "taban:", TABAN["taban_ozet"][ad], " bozuk:", oz2[ad])
print("degisen ozet hucresi:", sum(1 for a in oz2 if oz2[a] != TABAN["taban_ozet"][a]),
      "/", len(oz2))
print("sunum kuzey:", a5_sun(oz2)[0]["kuzey"], " taban:", TABAN["taban_sunum"]["kuzey"])
```

```
yazilmayan satir: 46  kuzeyin 2026-07 satiri: 0
kaynaktaki bolge kumesi: ['bati', 'dogu', 'guney', 'kuzey', 'merkez']
kusurlu bolum denetimi: False  donem duyarli denetim: True
ozet hucresi: 53  kuzeyin ozetteki donem sayisi: 9
sunum kuzey: 1218.67  taban: 1212.55

ozet ('kuzey', '2026-06') taban: 1041  bozuk: 1041
ozet ('kuzey', '2026-07') taban: 1251  bozuk: 1812946491
ozet ('kuzey', '2026-08') taban: 1119  bozuk: -1812944121
degisen ozet hucresi: 10 / 55
sunum kuzey: 1212.55  taban: 1212.55
```

Birinci küme cümleyi kurar: **bir denetimin yeşil olması ölçüldüğü anlamına gelmez.** Bölge kümesi
beş elemanlıdır, kusurlu denetim `False` döndürür. Aynı kaynakta 46 satır hiç gelmemiştir, özet 55
hücreden 53'e düşmüştür ve kuzeyin sunum değeri 1212,55'ten 1218,67'ye kaymıştır — sunum
çözünürlüğü 0,2000 olduğuna göre otuz adımdan büyük, ölçülebilir bir fark, ama bakan yoktur.
Kuzeyin özetteki dönem sayısı **9**'a inmiştir, çünkü bir dönemin hiç yazılmaması **onu izleyen
dönemi de** düşürür: tüketim dönem farkıyla çalışır ve `2026-08` satırlarının önceki dönemi artık
yoktur. Bir aşamanın düşürdüğü satır, bozulmanın doğrudan sildiği satırla aynı sayı değildir.

İkinci küme en sessiz bozulmayı açar. `birim_degisti` sayacı metreküp yerine litre okuyor ama birim
etiketini `m3` bırakıyor. Özette kuzeyin `2026-07` toplamı 1251'den **1812946491**'e çıkıyor,
`2026-08` toplamı 1119'dan **−1812944121**'e düşüyor. Mekanizma iptaldir: bozulan dönemde endeks
bin katına çıktığı için o dönemin farkı şişer, izleyen dönemde endeks eski ölçeğine döndüğü için
aynı büyüklükte eksi bir fark oluşur. Özetin **on hücresi** oynuyor. Bölgenin özetteki on bir
döneminin ortalaması alındığında iki hücre birbirini götürüyor ve sunum değeri **1212,55**'te
birebir kalıyor; sunum kusursuz görünürken iki dönem çöptür.

#### Özet

- Bir kalite boyutu ancak belirli bir aşamadan sonra koşan çalışan bir denetime çevrildiğinde
  ölçülmüş sayılır; **doğruluk** bu hatta ölçülemez, onun yerine ölçülen şey **tutarlılıktır**.
- Sağlam kaynakta beş aşama 2400 satırı 5 sunum değerine indiriyor ve yedi denetimin hiçbiri
  ateşlemiyor; iyi satır yanlış engellenmiyor.
- Altı bozulma sınıfının yakalama gecikmesi sırasıyla 4, 0, 0, 4, 0 ve 0 aşamadır; dört aşama, ara
  tabloları kirlenmiş ve geri doldurulması gereken bir hat demektir.
- `["sema", "tip", "tekillik", "bolum"]` kümesiyle üç sınıf hiç yakalanmıyor: `gec_gelen`,
  `birim_degisti` ve `eksik_bolum`. `bolum` bölge kümesine bakıp dönemi görmediği için 46 satırın
  hiç yazılmadığı kaynakta yeşil kalıyor.
- Üç sınıf sunulan beş değeri birebir aynı bırakıyor; `birim_degisti` özetin on hücresini
  1812946491 ile −1812944121 arasına taşıdığı hâlde ortalama 1212,55'te kalıyor.

#### Sonraki Adım

Bu dersin en hızlı yakalanan bozulması `sema_degisti` oldu: alan adı değiştiğinde `sema` denetimi
aşama 0'da ateşledi. Ama denetimin baktığı alan listesi kodun içine gömülüydü ve o listeyi kimin
yazdığı hiç sorulmadı. Üretici alanın adını değiştirdiğinde kendi tarafında hiçbir hata almaz;
kırılan şey tüketicidir. Sonraki ders bu listeyi bir **veri sözleşmesine** çevirir, alan adı
değiştiğinde kaç satırın düştüğünü ve sözleşmenin bozulmayı hangi aşamada yakaladığını sayar, sonra
sözleşmenin kendi bedelini ölçer.

### Şema Sözleşmeleri

Source: https://academia.sh/tr/kurslar/veri-muhendisligine-giris/sema-sozlesmeleri

Üretici ile tüketici arasındaki şema sözleşmesinin kimi kırdığının ve kendi bedelinin sayılması: bir alanın adı değiştiğinde üretici tarafında hatalı satır 0 kalırken tüketici tarafında 200 satır ayıklamada, 200 satır daha tüketimde düşüyor ve özet 55 hücreden 45'e iniyor; sözleşme yazıldığında aynı bozulma aşama 0'da yakalanıyor ama çıktı düzelmiyor; alan kümesini birebir sabitleyen katı sözleşme hiçbir bozulma yokken 1200 iyi satırı engelleyip özeti 25 hücreye düşürüyor; yük düzeyi uygulama 2200 iyi satırı engelleyip hiç çıktı vermiyor, bağdaştırma aşaması ise sunumu tabana döndürürken hattı altı aşamaya çıkarıyor.

Önceki dersin en hızlı yakalanan bozulması `sema_degisti` oldu: alan adı değiştiğinde `sema`
denetimi aşama 0'da ateşledi. O denetimin baktığı alan listesi kodun içine gömülüydü ve listeyi
kimin yazdığı sorulmadı. Liste tüketicinin varsayımıdır; üretici ondan habersizdir.

Bu ders o listeyi bir **veri sözleşmesine (data contract)** çevirir. Üç şey ölçülür: alan adı
değiştiğinde kimin tarafında kaç satır düşüyor, sözleşme yazıldığında bozulma hangi aşamada
yakalanıyor ve sözleşmenin kendisi ne kadara mal oluyor. Üçüncü sayı en önemlisidir, çünkü
kursun ikinci iddiasının örneğidir: **denetim eklemek de bozar.**

- **VK8.** Kaynak, hat ve sözleşme **kurgudur**; gerçek bir veritabanı, dosya biçimi ya da
  orkestratör çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **VK9.** İlişkisel şema, normalleştirme, kısıt ve tip yordamı Veritabanları müfredatında,
  Veri Modelleme ve İlişkisel Kuram kursunda ölçüldü ve **tekrarlanmaz**. Buradaki soru şemanın
  nasıl tasarlandığı değil, **değiştiğinde kimin kırıldığıdır**.
- **VK10.** **Şema değişimi bir saldırı değildir.** Üretici alanın adını daha açık olsun diye
  değiştirir ya da yeni bir alan ekler; ikisi de sıradan bakım işidir.
- **VK11.** Sözleşme **aşama 0'da**, kaynak hatta girmeden koşar ve satır düzeyinde uygulanır:
  uymayan satır engellenir, uyan satır geçer.
- **VK12.** Taban çizgisi **sözleşmesiz hattır**: engellenen satır **0**, yakalanan bozulma **0**.
- **VK13.** Çözünürlük: satır oranının adımı **0,0004**, özet hücresinin **0,0182**. **Sunum
  üzerinde oran basılmaz**, değerin kendisi yazılır.

#### Sözleşme Bir Alan Listesi Değildir

Bir alan listesi yalnızca "şu adlar bulunsun" der. Sözleşme üç şeyi birden bildirmek zorundadır:
hangi alanlar **var**, her birinin **türü** ne ve bildirilmeyen bir alan geldiğinde ne olacak.
Üçüncüsü çoğunlukla yazılmaz ve bu dersin en pahalı sayısı oradan çıkar.

İki uygulama arasındaki fark tek satırlıktır. **Kapsayan** sözleşme, bildirilen alanların var
olmasını ve türlerinin tutmasını ister; fazladan gelen alanı serbest bırakır. **Katı** sözleşme
alan kümesinin bildirilenle **birebir eşit** olmasını ister; fazladan gelen alan da ihlaldir. İkisi
de aynı sözleşme metnini okur ve bozulmuş kaynakta aynı davranır; ayrıştıkları yer **zararsız**
değişimdir.

```python
# KURGUDUR. Sayac okumalari, bes asamali hat ve sozlesme ders icinde tanimlanir;
# gercek bir depo, dosya bicimi ya da orkestrator cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


# ---- HAT: bes asama, her biri kendi dusurdugu satiri sayar
def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]


def hat(kayit):
    iz, veri = [], kayit
    for ad, f in ASAMA:
        veri, dusen = f(veri)
        iz.append({"asama": ad, "kalan": len(veri), "dusen": dusen})
    return veri, iz


# ---- Ortak tanimin alti bozulma sinifindan bu ders yalnizca birini kosuyor.
def boz_sema(kayit, donem="2026-07"):
    """Uretici alanin adini degistirdi ; degerin kendisi ayni kaldi."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        y = dict(k)
        y["endeks_degeri"] = y.pop("endeks")
        sayac += 1
        yeni.append(y)
    return yeni, sayac


def alan_ekle(kayit, ilk="2026-07"):
    """Uretici zararsiz bir alan ekledi ; var olan alanlar degismedi."""
    return [dict(k, sayac_seri="S" + k["abone"][1:]) if k["donem"] >= ilk else k
            for k in kayit]


# ---- SOZLESME: ureticinin yazdigini bildirdigi satirin tam semasi
SOZLESME = {"abone": str, "donem": str, "bolge": str, "tip": str,
            "endeks": int, "birim": str}


def kapsayan(k):
    """Bildirilen alanlar var ve turleri dogru ; fazla alan serbest."""
    return all(a in k and isinstance(k[a], t) for a, t in SOZLESME.items())


def kati(k):
    """Alan kumesi bildirilenle birebir esit olmali ; eklenen alan da ihlaldir."""
    return set(k) == set(SOZLESME) and kapsayan(k)


def sozlesmeli_al(kayit, kontrol=None):
    """Sozlesme asama 0'da kosar: uyan satir gecer , uymayan engellenir."""
    if kontrol is None:
        return kayit, 0
    gecen = [k for k in kayit if kontrol(k)]
    return gecen, len(kayit) - len(gecen)
```

#### Şema Değişimi Kimi Kırıyor

İkinci blok altı kurulumu aynı kaynağa uygular. `giren` sütunu hatta giren satırı, `engel` sözleşme
tarafından aşama 0'da engellenen satırı, `ayikla` ile `tuketim` o iki aşamanın düşürdüğü satırı,
`ozet` dördüncü aşamanın hücre sayısını verir. Son sütun sunulan beş değerin tabanla aynı kalıp
kalmadığını yazar.

```python
KAYNAK_KAYIT = kaynak_uret()
TABAN, _ = hat(KAYNAK_KAYIT)


def olc(ad, kayit, kontrol):
    giren, eng = sozlesmeli_al(kayit, kontrol)
    sunum, iz = hat(giren)
    print(f"{ad:<28}{len(giren):>7}{eng:>7}{iz[0]['dusen']:>8}{iz[2]['dusen']:>8}"
          f"{iz[3]['kalan']:>6}{'ayni' if sunum == TABAN else 'degisti':>9}")


bozuk, n = boz_sema(KAYNAK_KAYIT)
eklenmis = alan_ekle(KAYNAK_KAYIT)
print("taban sunum:", TABAN)
print("adi degisen satir:", n, " eklenen alani tasiyan satir:",
      sum(1 for k in eklenmis if "sayac_seri" in k))
print()
print(f"{'kurulum':<28}{'giren':>7}{'engel':>7}{'ayikla':>8}{'tuketim':>8}"
      f"{'ozet':>6}{'sunum':>9}")
olc("saglam , sozlesme yok", KAYNAK_KAYIT, None)
olc("ad degisti , sozlesme yok", bozuk, None)
olc("ad degisti , kapsayan", bozuk, kapsayan)
olc("alan eklendi , sozlesme yok", eklenmis, None)
olc("alan eklendi , kapsayan", eklenmis, kapsayan)
olc("alan eklendi , kati", eklenmis, kati)
print()
URETICI = ("abone", "donem", "bolge", "tip", "endeks_degeri", "birim")
print("ureticinin kendi semasina uymayan satir:",
      sum(1 for k in bozuk if k["donem"] == "2026-07"
          and not all(a in k for a in URETICI)))
print("sozlesmenin bildirdigi alan:", len(SOZLESME), " hattin okudugu alan:", 5)
```

```
taban sunum: {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
adi degisen satir: 200  eklenen alani tasiyan satir: 1200

kurulum                       giren  engel  ayikla tuketim  ozet    sunum
saglam , sozlesme yok          2400      0       0       0    55     ayni
ad degisti , sozlesme yok      2400      0     200     200    45  degisti
ad degisti , kapsayan          2200    200       0     200    45  degisti
alan eklendi , sozlesme yok    2400      0       0       0    55     ayni
alan eklendi , kapsayan        2400      0       0       0    55     ayni
alan eklendi , kati            1200   1200       0       0    25  degisti

ureticinin kendi semasina uymayan satir: 0
sozlesmenin bildirdigi alan: 6  hattin okudugu alan: 5
```

Sondan bir önceki satır sorunun yanıtıdır: **üreticinin kendi şemasına uymayan satır 0'dır.**
Üretici alanın adını değiştirmiş, bütün alanları eksiksiz yazmış, yazma işlemi hatasız
tamamlanmıştır. Onun tarafında ölçülebilen hiçbir kusur yoktur. Kırılan taraf tüketicidir ve bunu
ancak kendi hattında sayabilir.

İkinci satır sözleşmesiz durumu verir. Bozulmuş yük hatta olduğu gibi giriyor: ayıklama aşaması
zorunlu alanı bulamadığı için **200 satır** düşürüyor, tüketim aşaması bir dönem sonrasının önceki
değerini bulamadığı için **200 satır daha** düşürüyor. Toplam 400 satır, kaynağın altıda biri.
Özet 55 hücreden **45**'e iniyor ve sunulan beş değerin hepsi değişiyor. Hiçbir yerde bir hata
kaydı yok; hat başarıyla tamamlandı.

Üçüncü satır sözleşmeli durumu verir ve dikkatle okunmalıdır. Sözleşme aşama 0'da 200 satırı
engelliyor, **yakalama gecikmesi 0**. Ama özet yine **45** hücre, sunum yine değişmiş. Sözleşme
çıktıyı düzeltmedi; yalnızca haber verdi. Düşen satırlar aşamalar arasında dağılmaktan çıkıp tek
bir yerde toplandı: 400 satırın 200'ü artık ayıklama aşamasında değil kapıda duruyor, kalan 200'ü
hâlâ tüketimde düşüyor çünkü bir sonraki dönemin önceki değeri gerçekten yok. **Sözleşmenin
ürettiği şey doğru sayı değil, doğru zamanda gelen sinyaldir.**

#### Sözleşmenin Bedeli

Son üç satır kursun ikinci iddiasını taşır. Üretici `2026-07` döneminden başlayarak satırlara
zararsız bir alan ekliyor: sayaç seri numarası. Var olan alanların hiçbiri değişmiyor, hiçbir tür
kaymıyor, hattın okuduğu beş alan yerinde duruyor.

Sözleşmesiz hat bu değişimden hiç etkilenmiyor: 2400 satır giriyor, 55 hücre, sunum aynı.
**Kapsayan** sözleşme de etkilenmiyor, çünkü fazladan gelen alanı ihlal saymıyor: engellenen satır
**0**, sunum aynı. **Katı** sözleşme ise **1200 satırı** engelliyor. Eklenen alanı taşıyan satır
sayısı da tam 1200'dür, yani katı sözleşme yeni alanı gören her satırı reddediyor. Özet 55
hücreden **25**'e düşüyor, sunulan beş değerin hepsi değişiyor.

Bu satır bir kaza değil, sözleşmenin tanımından çıkan sonuçtur. Bozulma **yok**. Yakalanan bozulma
**0**. Yanlış engellenen iyi satır **1200**. Kaynağın yarısı, hiçbir kalite kusuru taşımadığı hâlde
hatta girmedi. Bir denetimin bedeli yalnızca yazma ve bakım emeği değildir; **yanlış tanımlanmış
bir sözleşme hattı, hiçbir bozulma olmadan durdurabilir.**

Son satır bedelin ikinci yüzünü gösterir. Sözleşme **altı** alan bildiriyor, hat bunların
**beşini** okuyor; `tip` alanı hiçbir aşamada kullanılmıyor. Sözleşme genişledikçe üretici bağlanır
ve tüketicinin hiç okumadığı bir alanın türü değiştiğinde de hat durur. Kapsayan sözleşmenin
katıdan üstünlüğü bu yüzden mutlak değildir: kapsayan sözleşme fazla alanı serbest bırakırken,
bildirilen alanların değişimini de aynı sertlikte engeller. Sözleşme yazmanın kararı hangi alanın
bildirileceğidir ve her bildirilen alan hem bir güvence hem bir kilit üretir.

#### Durdurmak ve Uyarlamak

Sözleşme ihlal edildiğinde ne yapılacağı ayrı bir karardır ve bedeli ayrı ölçülür. Buraya kadarki
bütün satırlar **satır düzeyi** uygulamadır: uymayan satır engellenir, uyan satır geçer. İkinci
seçenek **yük düzeyi** uygulamadır: yükte tek bir uymayan satır varsa tamamı reddedilir. Üçüncü
seçenek engellemek yerine **uyarlamaktır**: tüketici bilinen eski adı sözleşmedeki ada çeviren bir
aşama ekler.

```python
def yuk_duzeyi(kayit, kontrol):
    """Tek satir bile uymuyorsa yukun tamami reddedilir."""
    return ([], len(kayit)) if any(not kontrol(k) for k in kayit) else (kayit, 0)


ESLEME = {"endeks_degeri": "endeks"}


def uyarla(kayit):
    """Ek asama: bilinen eski adi sozlesmedeki ada cevirir."""
    yeni = []
    for k in kayit:
        y = dict(k)
        for eski, ad in ESLEME.items():
            if eski in y:
                y[ad] = y.pop(eski)
        yeni.append(y)
    return yeni


def yaz(ad, giren, eng):
    sunum, iz = hat(giren)
    durum = "ayni" if sunum == TABAN else ("yok" if not sunum else "degisti")
    print(f"{ad:<26}{len(giren):>7}{eng:>7}{iz[3]['kalan']:>6}{durum:>9}")


print(f"{'uygulama':<26}{'giren':>7}{'engel':>7}{'ozet':>6}{'sunum':>9}")
yaz("satir duzeyi , kapsayan", *sozlesmeli_al(bozuk, kapsayan))
yaz("yuk duzeyi , kapsayan", *yuk_duzeyi(bozuk, kapsayan))
yaz("uyarlama + kapsayan", *sozlesmeli_al(uyarla(bozuk), kapsayan))
print()
print("yuk duzeyinde yanlis engellenen iyi satir:", sum(1 for k in bozuk if kapsayan(k)))
print("uyarlamanin ekledigi asama:", 1, " esleme tablosundaki ad:", len(ESLEME))
```

```
uygulama                    giren  engel  ozet    sunum
satir duzeyi , kapsayan      2200    200    45  degisti
yuk duzeyi , kapsayan           0   2400     0      yok
uyarlama + kapsayan          2400      0    55     ayni

yuk duzeyinde yanlis engellenen iyi satir: 2200
uyarlamanin ekledigi asama: 1  esleme tablosundaki ad: 1
```

Üç satır üç ayrı bedel gösterir. Satır düzeyi uygulama yanlış satırı ayıklar ve hattı yürütür;
karşılığında **eksik bir çıktı** üretir ve o çıktı eksik olduğunu kendi üstünde taşımaz. Yük düzeyi
uygulama hiç çıktı üretmez; sunum yoktur. Bu kimi zaman doğru karardır, çünkü yanlış sayı yayımlamak
sayı yayımlamamaktan pahalıdır — ama bedeli açıktır: **2200 iyi satır** yanlış engellenmiştir ve on
bir sağlam dönemin tamamı, tek bir dönemin kusuru yüzünden beklemeye alınmıştır.

Üçüncü satır tek onarım yoludur: bağdaştırma aşaması eski adı yeni ada çevirdiğinde 2400 satırın hepsi
geçiyor, özet **55** hücreye dönüyor ve sunum tabanla **birebir aynı** oluyor. Bedeli iki kalemde
yazılır. Hat artık **altı aşamalıdır**, beş değil; ve eşleme tablosu tüketicinin sonsuza dek
taşıyacağı bir yüktür. Bu tablo büyüdükçe sözleşme metni ile hattın gerçekte kabul ettiği şema
birbirinden uzaklaşır: sözleşme `endeks` der, hat iki adı da kabul eder. Üretici bir gün eski adı
başka bir anlamda geri getirirse eşleme sessizce yanlış alanı okur ve bu, bu kursun tanımıyla tam
olarak bir sessiz bozulmadır.

#### Özet

- Bir veri sözleşmesi üç şeyi birden bildirir: hangi alanlar var, türleri ne ve bildirilmeyen bir
  alan geldiğinde ne olacak. Üçüncüsü yazılmadığında bedel katı yorumdan çıkar.
- Alan adı değiştiğinde **üreticinin kendi şemasına uymayan satır 0**'dır; kırılan taraf
  tüketicidir ve 400 satır kaybını yalnızca kendi hattında sayabilir.
- Sözleşmesiz hatta bozulma sessizdir: 200 satır ayıklamada, 200 satır tüketimde düşer, özet 55
  hücreden 45'e iner, sunulan beş değer değişir ve hiçbir hata kaydı üretilmez.
- Sözleşme yazıldığında aynı bozulma **aşama 0'da** yakalanır, ama özet yine 45 hücredir; sözleşme
  çıktıyı düzeltmez, doğru zamanda sinyal üretir.
- Katı sözleşme zararsız bir alan eklemesinde **1200 iyi satırı** engeller, özeti 25 hücreye
  düşürür ve bunu **yakalanan bozulma 0** iken yapar; denetim eklemek de bozar.
- İhlal edildiğinde ne yapılacağı ayrı bir bedeldir: satır düzeyi uygulama eksik çıktı verir, yük
  düzeyi uygulama 2200 iyi satırı engelleyip hiç çıktı vermez, uyarlama sunumu tabana döndürür ama
  hattı altı aşamaya çıkarır ve tüketiciye kalıcı bir eşleme tablosu bırakır.

#### Sonraki Adım

Sözleşme bozulmayı aşama 0'da yakaladı, ama yakalamak düzeltmek değildir: engellenen 200 satır bir
yerde durmalı, üretici düzeltmeli ve o dönem hatta **yeniden** verilmelidir. Bu dersin hiçbir
satırında hat iki kez koşmadı. İkinci koşum hattın en sık yapılan işidir ve kendi sayısını taşır:
aynı dönem yeniden işlendiğinde satır sayısına ne oluyor, hangi aşama fazlalığı yutuyor ve sunulan
değer bunu gösteriyor mu. Sonraki ders hattı **yinelemeye dayanıklı** olan ve olmayan iki biçimde
koşturur, çoğalan satırı sayar ve sunumdaki farkı ölçer.

### Yinelemeye Dayanıklılık ve Yeniden İşleme

Source: https://academia.sh/tr/kurslar/veri-muhendisligine-giris/etkisizlik-ve-yeniden-isleme

Aynı yükün ikinci kez işlenmesinin satırı çoğalttığının ve sunulan sayının bunu göstermediğinin ölçülmesi: ekleyen depoda bir dönemin geri doldurulması ham satırı 2400'den 2600'e, tam yeniden koşum 4800'e çıkarıyor, üç koşumda 7200'e ulaşıyor, ama tekilleştirme aşaması fazlalığı yuttuğu için sunulan değer üç koşumda da 1212,55 kalıyor; aynı aşama yeniden verilen yük bir düzeltme taşıdığında düzeltmeyi çöpe atıp değeri 794,36'da bırakıyor; tekilleştirme anahtar yerine satırın tamamına baktığında ise aynı yeniden koşumlar değeri 2425,09'a ve 3637,64'e taşıyor.

Önceki ders sözleşmenin bozulmayı aşama 0'da yakaladığını gösterdi, ama yakalamak düzeltmek
değildir: engellenen 200 satır bir yerde bekler, üretici düzeltir ve o dönem hatta **yeniden**
verilir. Buraya kadarki hiçbir ölçümde hat aynı veriyi iki kez görmedi.

İkinci koşum bir istisna değil, hattın en sık yaptığı iştir. Üretici bir yükü düzeltir, bir aşama
hata verip yeniden denenir, geçmiş bir dönem **geri doldurulur**. Bu ders aynı yükü iki kez işleyen
bir hattın satır sayısına ne olduğunu, hangi aşamanın fazlalığı yuttuğunu ve sunulan değerin bunu
gösterip göstermediğini sayar. Yanıt kursun üçüncü iddiasının en temiz örneğidir.

- **VK14.** Kaynak, hat ve depo **kurgudur**; gerçek bir depo, dosya biçimi ya da orkestratör
  çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **VK15.** `idempotent` karşılığı bu müfredatta **yinelemeye dayanıklıdır**. Kavramın kendisi
  Kabuk Programlama kursunda, yeniden deneme ile kuyruk yordamı Önbellekleme, Kuyruklar ve
  Eşzamansız İşleme kursunda ölçüldü ve **tekrarlanmaz**. Buradaki soru yalnızca **yeniden
  işlemenin satırı çoğaltıp çoğaltmadığıdır**.
- **VK16.** Yeniden işleme bir kaza değildir: düzeltilmiş yük, yeniden denenen aşama ve geri
  doldurulan dönem sıradan işlerdir.
- **VK17.** Yeniden verilen satırın **değeri aynı kalır**, yalnızca yükleme damgası değişir; tek
  istisna düzeltme taşıyan yüktür. Bu ayrıntı son iki ölçümün tamamını belirler.
- **VK18.** Taban çizgisi **tek koşumdur**: ham satır 2400, çoğalan satır **0**.
- **VK19.** Sayılan birimler: yeniden işlenen satır, çoğalan satır, tekilleştirmenin düşürdüğü
  satır ve sunumdaki fark. **Ham süre yazılmaz**, ölçü **koşum** olarak yazılır.
- **VK20.** Çözünürlük: satır oranının adımı **0,0004**, özet hücresinin **0,0182**, sunum
  değerinin **0,2000**. **Sunum üzerinde oran basılmaz.**

#### İki Yazma Biçimi

Bir hattın yinelemeye dayanıklı olup olmadığı tek bir kararla belirlenir: gelen satır depoya
**nasıl** yazılıyor. **Ekleyen** yazma satırı deponun sonuna koyar; aynı satır iki kez geldiğinde
depoda iki kez durur. **Anahtarlayan** yazma satırı bir anahtarın üzerine yazar; aynı anahtar iki
kez geldiğinde depoda bir kez durur. Anahtar burada abone ile dönemin çiftidir, yani hattın zaten
tekillik ölçütü olarak kullandığı şey.

İkinci parça yeniden işlemenin nasıl göründüğüdür. Düzeltilmiş yük geldiğinde satırın **değeri
aynıdır**; değişen tek şey yükleme damgasıdır. Bu, kolay gözden kaçan bir ayrıntıdır ve son ölçümün
tamamı buna dayanır.

```python
# KURGUDUR. Sayac okumalari, bes asamali hat ve depo ders icinde tanimlanir;
# gercek bir depo, dosya bicimi ya da orkestrator cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


# ---- HAT: bes asama, her biri kendi dusurdugu satiri sayar
def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]


def hat(kayit, asamalar=None):
    iz, veri = [], kayit
    for ad, f in (asamalar or ASAMA):
        veri, dusen = f(veri)
        iz.append({"asama": ad, "kalan": len(veri), "dusen": dusen})
    return veri, iz


# ---- VERI ALMA ve YENIDEN ISLEME
def al(kayit, yukleme):
    """Veri alma asamasi: her yuke bir damga dusulur."""
    return [dict(k, yukleme=yukleme) for k in kayit]


def yeniden_ver(kayit, donemler, yukleme):
    """Duzeltilen yuk yeniden gonderiliyor ; damga degisir, deger degismez."""
    return [dict(k, yukleme=yukleme) for k in kayit if k["donem"] in donemler]


def depo_ekle(depo, yeni):
    """Yinelemeye DAYANIKSIZ yazma: gelen satir deponun sonuna eklenir."""
    return depo + yeni


def depo_anahtarla(depo, yeni):
    """Yinelemeye DAYANIKLI yazma: (abone , donem) anahtarinin uzerine yazilir."""
    tablo = {(k["abone"], k["donem"]): k for k in depo}
    for k in yeni:
        tablo[(k["abone"], k["donem"])] = k
    return list(tablo.values())


def a2_tekille_tam(kayit):
    """KUSURLU TEKILLESTIRME: anahtara degil satirin tamamina bakar."""
    gor, tut = set(), []
    for k in kayit:
        im = tuple(sorted((a, str(v)) for a, v in k.items()))
        if im in gor:
            continue
        gor.add(im)
        tut.append(k)
    return tut, len(kayit) - len(tut)


KUSURLU = [("ayikla", a1_ayikla), ("tekille", a2_tekille_tam), ("tuketim", a3_tuketim),
           ("ozet", a4_ozet), ("sun", a5_sun)]


def d_tekillik(veri):
    return len(veri) != len(set((k["abone"], k["donem"]) for k in veri))


def d_satir_sayisi(veri, beklenen=2400, pay=0.05):
    return abs(len(veri) - beklenen) / beklenen > pay
```

#### Çoğalan Satır ve Değişmeyen Sunum

İkinci blok beş kurulumu aynı kaynağa uygular. `ham` sütunu depodaki satır sayısını, `tekille` o
aşamanın düşürdüğü satırı, `ozet` dördüncü aşamanın hücre sayısını, `kuzey` sunulan beş değerden
birini verir.

```python
KAYNAK_KAYIT = kaynak_uret()
ILK = al(KAYNAK_KAYIT, 1)
TABAN, _ = hat(ILK)


def yaz(ad, depo, asamalar=None):
    sunum, iz = hat(depo, asamalar)
    print(f"{ad:<30}{len(depo):>6}{iz[1]['dusen']:>9}{iz[3]['kalan']:>6}"
          f"{sunum['kuzey']:>11.2f}{'ayni' if sunum == TABAN else 'degisti':>9}")


bir_donem = yeniden_ver(KAYNAK_KAYIT, {"2026-07"}, 2)
tam = yeniden_ver(KAYNAK_KAYIT, set(DONEM), 2)
print("taban sunum:", TABAN)
print("yeniden verilen satir: bir donem", len(bir_donem), " tam kosum", len(tam))
print()
print(f"{'kurulum':<30}{'ham':>6}{'tekille':>9}{'ozet':>6}{'kuzey':>11}{'sunum':>9}")
yaz("tek kosum", ILK)
yaz("ekleyen depo , bir donem", depo_ekle(ILK, bir_donem))
yaz("ekleyen depo , tam kosum", depo_ekle(ILK, tam))
yaz("anahtarlayan depo , tam kosum", depo_anahtarla(ILK, tam))
yaz("kusurlu tekillestirme , tam", depo_ekle(ILK, tam), KUSURLU)
print()
d = depo_ekle(ILK, tam)
print("tekillik denetimi:", d_tekillik(d), " satir sayisi denetimi:", d_satir_sayisi(d))
```

```
taban sunum: {'merkez': 1321.45, 'kuzey': 1212.55, 'dogu': 881.36, 'bati': 833.09, 'guney': 1065.55}
yeniden verilen satir: bir donem 200  tam kosum 2400

kurulum                          ham  tekille  ozet      kuzey    sunum
tek kosum                       2400        0    55    1212.55     ayni
ekleyen depo , bir donem        2600      200    55    1212.55     ayni
ekleyen depo , tam kosum        4800     2400    55    1212.55     ayni
anahtarlayan depo , tam kosum   2400        0    55    1212.55     ayni
kusurlu tekillestirme , tam     4800        0    55    2425.09  degisti

tekillik denetimi: True  satir sayisi denetimi: True
```

İkinci satır dersin başlığıdır: bir dönem geri doldurulduğunda ekleyen depo **2600** satır taşıyor,
yani **200 satır çoğaldı**. Tekilleştirme aşaması tam olarak o 200 satırı düşürüyor ve sunulan değer
**1212,55**'te kalıyor. Üçüncü satır aynı şeyi büyük ölçekte gösterir: tam yeniden koşum deposu
**4800** satıra çıkarıyor, tekilleştirme **2400** satır düşürüyor, sunum yine aynı.

Bu, kursun üçüncü iddiasının en temiz örneğidir. Deponun boyutu iki katına çıkmış, hat aynı işi iki
kez yapmış, ama **sunulan beş değerin hiçbiri kımıldamamıştır**. Çıktıya bakan bir gözlem hattın
yinelemeye dayanıklı olduğunu düşünür. Oysa dayanıklı olan hat değil, **hattın içindeki tek bir
aşamadır**: tekilleştirme fazlalığı yutuyor ve yutarken kimseye haber vermiyor.

Dördüncü satır gerçek dayanıklılığın ne olduğunu gösterir. Anahtarlayan depo ikinci koşumdan sonra
da **2400** satır taşıyor: çoğalan satır **0**, tekilleştirmenin düşürdüğü satır **0**. Fark
depoda değil, hattın ötesindedir; ekleyen depo doğru sayıyı üretirken **iki kat yer** tutar ve
tekilleştirme aşaması olmadan kullanılamaz.

Son iki satır denetimin ne dediğidir. Tekillik denetimi ekleyen depoda ateşliyor, satır sayısı
denetimi de ateşliyor — 4800, beklenen 2400'ün yüzde beşlik bandının çok dışındadır. Yani
bozulmanın yakalama gecikmesi **0**'dır ve bunu yakalayan denetim **sunuma bakan denetim değildir.**

#### Yutulan Düzeltme

Buraya kadar yeniden verilen satırın değeri ilkiyle aynıydı, bu yüzden tekilleştirmenin hangisini
tuttuğu önemsizdi. Gerçek geri doldurmalarda durum tersidir: yük **düzeltildiği için** yeniden
gönderilir. Tekilleştirme aşaması ilk gördüğü satırı tutar, yani **eskisini**. Aşağıdaki kurulumda
üretici son dönemi eksik okumuş, sonra düzeltip yeniden göndermiştir. Bozulmanın son döneme
konması bilinçlidir: dizinin ortasındaki bir kayma izleyen dönemde ters işaretle geri gelir ve
ortalamada birbirini götürür.

```python
def eksik_okuma(kayit, donem="2026-12", fark=100):
    """Uretici son donemi eksik okudu ; sonra duzeltip yeniden gonderecek."""
    return [dict(k, endeks=k["endeks"] - fark) if k["donem"] == donem else k
            for k in kayit]


EKSIK = al(eksik_okuma(KAYNAK_KAYIT), 1)
DUZELTME = yeniden_ver(KAYNAK_KAYIT, {"2026-12"}, 2)
print("duzeltilmis satir:", len(DUZELTME))
print(f"{'kurulum':<32}{'ham':>6}{'tekille':>9}{'kuzey':>10}{'taban':>10}")
for ad, depo in (("eksik yuk , duzeltme yok", EKSIK),
                 ("ekleyen depo , duzeltme geldi", depo_ekle(EKSIK, DUZELTME)),
                 ("anahtarlayan depo , duzeltme", depo_anahtarla(EKSIK, DUZELTME))):
    sunum, iz = hat(depo)
    print(f"{ad:<32}{len(depo):>6}{iz[1]['dusen']:>9}{sunum['kuzey']:>10.2f}"
          f"{TABAN['kuzey']:>10.2f}")
```

```
duzeltilmis satir: 200
kurulum                            ham  tekille     kuzey     taban
eksik yuk , duzeltme yok          2400        0    794.36   1212.55
ekleyen depo , duzeltme geldi     2600      200    794.36   1212.55
anahtarlayan depo , duzeltme      2400        0   1212.55   1212.55
```

İkinci satır dersin en pahalı bulgusudur. Düzeltme hatta geldi, 200 satır yeniden işlendi, depo
2600 satıra çıktı, tekilleştirme 200 satır düşürdü — ve sunulan değer **794,36**'da kaldı, yani
düzeltme öncesiyle **birebir aynı**. Düşürülen 200 satır düzeltilmiş olanlardı. Tekilleştirme
aşaması ikinci koşumu yuttu; burada yutmak **kaybetmektir**.

Bu, önceki bölümün tam tersi bir okumadır ve ikisi aynı davranıştan çıkar. Yeniden verilen satır
ilkiyle aynı olduğunda tekilleştirme hattı kurtarıyor; düzeltilmiş olduğunda aynı tekilleştirme
düzeltmeyi çöpe atıyor. Aşama ikisini ayırt edemez, çünkü elinde yalnızca anahtar vardır ve iki
satırın anahtarı aynıdır. Üçüncü satır çözümü gösterir: anahtarlayan depo yeni satırı eskisinin
üzerine yazdığı için sunum **1212,55**'e, yani tabana döner.

Bir hattın yinelemeye dayanıklı sayılması için aynı yükün iki kez işlenmesinin sonucu
değiştirmemesi yetmez; **düzeltilmiş bir yükün ikinci kez işlenmesinin sonucu düzeltmesi** de
gerekir. Ekleyen depo birinci koşulu sağlıyor, ikincisini sağlamıyor.

#### Anahtar Yanlış Seçildiğinde

Tablonun son satırı hattın en kırılgan varsayımını açar. Tekilleştirme aşaması `(abone, donem)`
anahtarına bakar. Aynı aşama **satırın tamamına** bakacak biçimde yazılsaydı, yeniden verilen satır
yükleme damgası yüzünden farklı görünür ve elenmezdi. Bu, uydurma bir kusur değildir: bir yükün
tümüyle aynı olup olmadığını sormak sezgisel olarak daha güvenli görünür ve tam da bu yüzden yazılır.

```python
ekleyen, anahtarlayan = ILK, ILK
print(f"{'kosum':<6}{'ekleyen ham':>12}{'kuzey':>9}{'kusurlu kuzey':>15}"
      f"{'anahtarlayan ham':>18}{'kuzey':>9}")
for kosum in range(1, 4):
    if kosum > 1:
        yeni = yeniden_ver(KAYNAK_KAYIT, set(DONEM), kosum)
        ekleyen = depo_ekle(ekleyen, yeni)
        anahtarlayan = depo_anahtarla(anahtarlayan, yeni)
    sun_ekle, _ = hat(ekleyen)
    sun_kusur, _ = hat(ekleyen, KUSURLU)
    sun_anahtar, _ = hat(anahtarlayan)
    print(f"{kosum:<6}{len(ekleyen):>12}{sun_ekle['kuzey']:>9.2f}"
          f"{sun_kusur['kuzey']:>15.2f}{len(anahtarlayan):>18}"
          f"{sun_anahtar['kuzey']:>9.2f}")
print()
oz, _ = a4_ozet(a3_tuketim(a2_tekille_tam(a1_ayikla(ekleyen)[0])[0])[0])
ozt, _ = a4_ozet(a3_tuketim(a2_tekille(a1_ayikla(ILK)[0])[0])[0])
print("ozet ('kuzey', '2026-07') taban:", ozt[("kuzey", "2026-07")],
      " kusurlu tekillestirme:", oz[("kuzey", "2026-07")])
print("tekillestirmenin tuttugu anahtar:", len(set((k["abone"], k["donem"]) for k in ILK)))
```

```
kosum  ekleyen ham    kuzey  kusurlu kuzey  anahtarlayan ham    kuzey
1             2400  1212.55        1212.55              2400  1212.55
2             4800  1212.55        2425.09              2400  1212.55
3             7200  1212.55        3637.64              2400  1212.55

ozet ('kuzey', '2026-07') taban: 1251  kusurlu tekillestirme: 3753
tekillestirmenin tuttugu anahtar: 2400
```

Üç koşumluk tablo iki cümleyi yan yana koyar. Doğru anahtarla çalışan hatta ham depo 2400'den
4800'e, 7200'e çıkıyor ve sunulan değer **üç koşumda da 1212,55**. Kusurlu anahtarla çalışan hatta
aynı ham depo, aynı yeniden koşumlar, ama sunulan değer **1212,55 → 2425,09 → 3637,64** diye
büyüyor: koşum sayısıyla doğru orantılı. Özet katmanında aynı şey görünür, kuzeyin `2026-07` hücresi
tabanda 1251 iken üçüncü koşumdan sonra **3753**'tür.

İki hat arasındaki tek fark bir aşamanın anahtar seçimidir. Hiçbir bozulma sınıfı yok, hiçbir alan
adı değişmedi, hiçbir tür kaymadı; üretici tarafında olan tek şey aynı doğru yükün ikinci kez
gönderilmesidir. Bu, kursun ikinci iddiasının bu derse düşen biçimidir: **hattın kendi aşaması,
kusurlu bir anahtarla, hiçbir kaynak bozulması olmadan sunulan sayıyı üç katına çıkarabilir.**

Anahtarlayan deponun sütunu bedelin nerede durduğunu söyler. Yinelemeye dayanıklılık ücretsiz
değildir: yazma anında **2400 girişlik** bir anahtar tablosu tutulur ve her satır yazılmadan önce
aranır. Tekilleştirme aşaması da aynı büyüklükte bir küme taşır. Bunun karşılığında depo koşum
sayısından bağımsız olarak 2400 satırda kalır ve hattın doğruluğu tek bir aşamanın anahtarına
bağımlı olmaktan çıkar.

#### Özet

- Yeniden işleme sıradan bir iştir; ekleyen depoda bir dönemin geri doldurulması ham satırı
  2400'den 2600'e, tam yeniden koşum 4800'e, üçüncü koşum 7200'e çıkarır.
- Sunulan beş değer bu büyümenin hiçbirini göstermez: kuzey üç koşumda da 1212,55'tir, çünkü
  tekilleştirme aşaması fazlalığı yutar ve yutarken haber vermez.
- Yinelemeye dayanıklı olan hat değil, hattın içindeki tek bir aşamadır; anahtarlayan yazma
  kullanıldığında çoğalan satır 0'a iner ve depo koşum sayısından bağımsız kalır.
- Yeniden verilen yük bir **düzeltme** taşıdığında aynı tekilleştirme onu çöpe atar: sunulan değer
  794,36'da kalır, oysa anahtarlayan depo aynı düzeltmeyle 1212,55'e döner. Dayanıklılık, ikinci
  koşumun sonucu değiştirmemesi değil, **düzeltmesi gerektiğinde düzeltmesidir**.
- Tekilleştirme `(abone, donem)` yerine satırın tamamına baktığında aynı yeniden koşumlar sunulan
  değeri 1212,55'ten 2425,09'a, sonra 3637,64'e taşır; özet hücresi 1251'den 3753'e çıkar.
- Çoğalmayı yakalayan denetimler tekillik ile satır sayısıdır ve ikisi de aşama 0'da ateşler;
  sunuma bakan hiçbir denetim bunu göremez.

#### Sonraki Adım

Üç derste denetim hep aynı yere baktı: **satırın kendisine**. Alanları var mı, türü doğru mu,
anahtarı benzersiz mi, değeri önceki dönemlerin düzeninde mi. Bu sorular bir sunum değeri
bozulduğunda yanıt vermez, çünkü hiçbiri satırın **nereden geldiğini** söylemez. Kusurlu
tekilleştirmeyle üretilen 3753 rakamına bakan biri, o hücreye hangi yüklerin katkı verdiğini ve
zincirin hangi halkasında hata olduğunu bulmak için hattın tamamını baştan okumak zorundadır.
Sonraki ders bu aramayı sayar: bozulmuş bir sunum değerinden kaynağa gitmek için kaç aşamaya ve kaç
satıra bakmak gerekiyor, kayıt tutulduğunda ve tutulmadığında.

### Veri Soyağacı

Source: https://academia.sh/tr/kurslar/veri-muhendisligine-giris/veri-soyagaci

Bozulmuş bir sunum değerinden kaynağa gitmek için bakılan satır sayısının soyağacı kaydı varken ve yokken sayılması: beş aşamalı hat altı katmanda 9.460 satır taşırken soyağacı kaydı 11.455 bağ tutuyor, çünkü her tüketim satırının iki atası var; kayıt yokken bir sunum değerini açıklamak 9.260 satır okumayı gerektiriyor ve bu sayı hangi değerden başlandığına bağlı değil, kayıtla aynı arama 94 ile 155 satır arasında bitiyor. İki koşum iki ayrı kayıp aşaması veriyor: geç gelen kayıtta kayıp kaynakta, şema değiştiğinde ayıklama aşamasında. Sunum sapmasının büyüklüğü bozulmanın büyüklüğünü vermiyor: beş kat fazla satır bozulduğunda sunum daha az oynuyor ve üç değer yukarı gidiyor.

Önceki üç ders denetimi satırın kendisine baktırdı. Kalite boyutları satırın taşıdığı değeri
ölçtü, şema sözleşmesi satırın alanlarını, yeniden işleme aynı satırın ikinci kez geçmesini.
Üçünde de denetimin baktığı nesne elindeki satırdı. Bir soru hiç sorulmadı: bu satır **nereden
geldi**. Sunumda beş değer duruyor; bir tanesi beklenenden uzaklaştığında o değeri üreten satırlar
hangileridir ve onları bulmak için kaç satır okunur.

Bu dersin ölçtüğü şey bir arama maliyetidir. Bozulmuş bir sunum değerinden kaynağa gitmek iki
biçimde yapılabilir: hattın her katmanını baştan okuyarak ya da hattın koşarken yazdığı **soyağacı
(lineage)** kaydını geriye izleyerek. İkisi de aynı yanıtı bulur; bakılan satır sayısı arasında iki
mertebe fark vardır. Kaydın kendisinin de bir bedeli vardır ve o da sayılır.

- **VK41.** Kaynak, beş aşamalı hat ve bozulma sınıfları **kurgudur** ve ders içinde tanımlanır.
  Hiçbir depo, dosya biçimi, katalog ürünü ya da orkestratör çağrılmaz. Tohum 20260218.
- **VK42.** **Soyağacı kaydı**, her aşamanın kendi çıktı anahtarından o çıktıyı üreten girdi
  anahtarlarına yazdığı **bağ** kümesidir. Satır anahtarı abone ile dönemdir, özet anahtarı bölge
  ile dönemdir, sunum anahtarı bölgedir.
- **VK43.** Arama iki biçimde ölçülür. **Kayıt yokken** sunum değerini açıklamak için altı katmanın
  tamamı baştan okunur. **Kayıt varken** her katmanda yalnız kayıtlı atalar okunur. Sayılan birim
  **bakılan satırdır**; süre yazılmaz.
- **VK44.** Özet hücresinde sapma bandı 0,02 alınmıştır. Özet katmanı 55 hücredir, yani hücre
  oranlarının en küçük adımı 0,0182; band bu adımın hemen üstündedir. **Sunum üzerinde oran
  basılmaz**, değerin kendisi ve farkı yazılır.
- **VK45.** Gözlemlenebilirlik, iz ve günlükleme yordamı Backend Geliştirme müfredatındaki
  Gözlemlenebilirlik ve Güvenilirlik kursunda ve DevOps ve Platform Mühendisliği müfredatındaki
  Gözlemlenebilirlik ve İşletim kursunda ölçüldü; **burada tekrarlanmaz**. İzin nesnesi zaman
  içinde ilerleyen bir istektir; soyağacının nesnesi bir **satırın dayandığı satırlardır**.
- **VK46.** Soyağacı kaydı **olmuş koşumdan** çıkar. Olması gerekeni bilmez; beklenen ata kümesi
  sağlam koşumun kaydından gelir.

#### Soyağacı Kaydı ve Kaç Bağ Tuttuğu

Hattın beş aşaması zaten satır düşürdüğünü sayıyor. Soyağacı bundan fazlasını ister: her çıktı
satırının hangi girdi satırlarından doğduğunu ada ada yazmasını. Kayıt aşama işlevlerinin içine
değil, girdileriyle çıktıları arasına konur; hat aynı hattır.

```python
# KURGUDUR. Olcum aginin sayac okumalari, bes asamali hat ve bozulma siniflari
# ders icinde tanimlanir; hicbir depo, dosya bicimi ya da orkestrator cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def boz(kayit, sinif, donem="2026-07"):
    """Bozulma siniflari. Hicbiri saldiri degil, uretici tarafi kazasidir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":                # bu donemde 40 abone hic gelmedi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        else:
            yeni.append(k)
    return yeni, sayac


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


KATMAN = ["kaynak", "ayikla", "tekille", "tuketim", "ozet", "sun"]


def ad(k):
    return (k["abone"], k["donem"])


def soyagaci(kayit):
    """Hatti kosturur ve her asamada cikti anahtarindan girdi anahtarlarina bag yazar."""
    v1, _ = a1_ayikla(kayit)
    v2, _ = a2_tekille(v1)
    v3, _ = a3_tuketim(v2)
    oz, _ = a4_ozet(v3)
    sun, _ = a5_sun(oz)
    bag = {"ayikla": {ad(k): [ad(k)] for k in v1},
           "tekille": {}, "tuketim": {}, "ozet": {}, "sun": {}}
    for k in v1:
        bag["tekille"].setdefault(ad(k), []).append(ad(k))
    for k in v3:
        i = DONEM.index(k["donem"])
        bag["tuketim"][ad(k)] = [ad(k), (k["abone"], DONEM[i - 1])]
        bag["ozet"].setdefault((k["bolge"], k["donem"]), []).append(ad(k))
    for b, d in oz:
        bag["sun"].setdefault(b, []).append((b, d))
    satir = {"kaynak": len(kayit), "ayikla": len(v1), "tekille": len(v2),
             "tuketim": len(v3), "ozet": len(oz), "sun": len(sun)}
    return sun, oz, bag, satir


kaynak = kaynak_uret()
sunum, ozet, bag, satir = soyagaci(kaynak)
print("katman   ", "  ".join(f"{a:>8}" for a in KATMAN))
print("satir    ", "  ".join(f"{satir[a]:>8}" for a in KATMAN))
print("bag      ", "  ".join(f"{sum(len(v) for v in bag[a].values()) if a in bag else 0:>8}"
                             for a in KATMAN))
toplam_bag = sum(sum(len(v) for v in bag[a].values()) for a in bag)
print(f"\nsatir toplami {sum(satir.values())} , bag toplami {toplam_bag}")
print("\nsunum degeri   deger   ata kaynak satiri")
for b, v in sunum.items():
    ata = {x for c in bag["sun"][b] for k in bag["ozet"][c]
           for t in bag["tuketim"][k] for a2 in bag["tekille"][t] for x in bag["ayikla"][a2]}
    print(f"{b:<13} {v:>7}   {len(ata):>17}")
```

```
katman      kaynak    ayikla   tekille   tuketim      ozet       sun
satir         2400      2400      2400      2200        55         5
bag              0      2400      2400      4400      2200        55

satir toplami 9460 , bag toplami 11455

sunum degeri   deger   ata kaynak satiri
merkez        1321.45                 588
kuzey         1212.55                 552
dogu           881.36                 408
bati           833.09                 372
guney         1065.55                 480
```

Kayıt, hattın taşıdığı veriden **büyüktür**. Altı katman toplam 9.460 satır tutuyor, soyağacı ise
11.455 bağ; satır başına 1,21 bağ. Fazlalığın tamamı tek aşamadan geliyor: `tuketim` 2.200 satır
üretiyor ama 4.400 bağ yazıyor, çünkü bir tüketim satırı iki okumanın **farkıdır** ve iki atası
vardır. Bir aşamanın kaç bağ yazacağını satır sayısı değil, çıktının kaç girdiye dayandığı belirler.

Son tablo soyağacının en doğrudan okumasıdır: sunulan beş değerin her biri kaç kaynak satırının
üstünde duruyor. Merkez 588, kuzey 552, güney 480, doğu 408, batı 372; toplamları 2.400, yani
kaynağın tamamı. Sunum katmanında beş sayı görünüyor; onların altında iki bin dört yüz satır var
ve hangi satırın hangi sayıya gittiği ancak yazıldıysa bilinir.

#### Bozulmuş Değerden Kaynağa: İki Koşum

Arama şöyle işler. Şüpheli sunum değerinin kayıtlı ataları okunur; sağlam koşumun karşılık gelen
hücrelerinden sapan ya da hiç bulunmayan özet hücreleri seçilir; yalnız onların ataları bir alt
katmanda okunur. Her katmanda okunan satır sayısı ayrı sayılır. Kayıt yoksa böyle bir daraltma
yoktur; sunum değerini açıklamak için altı katmanın tamamı okunur.

```python
# Onceki blogun tanimlari surer: soyagaci, boz, sunum, ozet, bag, satir.
def geri_yuru(hedef, bag_s, ozet_s, kaynak_s, pay=0.02):
    """Sunum degerinden kaynaga: her katmanda yalniz kayitli atalar okunur."""
    bakilan, kayip = {"sun": 1}, None
    aday = bag["sun"][hedef]
    bakilan["ozet"] = len(aday)
    supheli = [c for c in aday if c not in bag_s["ozet"]
               or abs(ozet_s[c] - ozet[c]) / max(abs(ozet[c]), 1) > pay]
    for ust, alt in (("ozet", "tuketim"), ("tuketim", "tekille"), ("tekille", "ayikla")):
        aday = sorted({x for c in supheli for x in bag[ust][c]})
        bakilan[alt] = len(aday)
        yok = [x for x in aday if x not in bag_s[alt]]
        supheli, kayip = (yok, alt) if yok else (aday, kayip)
    aday = sorted({x for c in supheli for x in bag["ayikla"][c]})
    bakilan["kaynak"] = len(aday)
    hic = [x for x in aday if x not in kaynak_s]
    return bakilan, supheli, ("kaynak" if hic else kayip)


for sinif in ("gec_gelen", "sema_degisti"):
    bozuk, n = boz(kaynak, sinif)
    sunum_s, ozet_s, bag_s, satir_s = soyagaci(bozuk)
    kaynak_s = {ad(k) for k in bozuk}
    kayitsiz = sum(satir_s.values())
    print(f"== {sinif} , bozulan kaynak satiri {n} , kayit yokken bakilan {kayitsiz} satir")
    print("sunum      deger     fark    sun ozet tuketim tekille ayikla kaynak"
          "  toplam  kat  koken  kayip asamasi")
    for b in BOLGE:
        bak, sup, asama = geri_yuru(b, bag_s, ozet_s, kaynak_s)
        t = sum(bak.values())
        print(f"{b:<9} {sunum_s[b]:>8.2f} {sunum_s[b] - sunum[b]:>8.2f}"
              f" {bak['sun']:>6} {bak['ozet']:>4} {bak['tuketim']:>7} {bak['tekille']:>7}"
              f" {bak['ayikla']:>6} {bak['kaynak']:>6} {t:>7} {kayitsiz / t:>4.0f}"
              f" {len(sup):>6}  {asama}")
    print()
```

```
== gec_gelen , bozulan kaynak satiri 40 , kayit yokken bakilan 9260 satir
sunum      deger     fark    sun ozet tuketim tekille ayikla kaynak  toplam  kat  koken  kayip asamasi
dogu        837.18   -44.18      1   11      68      27      9      9     125   74      9  kaynak
bati        811.18   -21.91      1   11      62      12      4      4      94   99      4  kaynak
kuzey      1185.09   -27.46      1   11      92      21      7      7     139   67      7  kaynak
guney      1011.27   -54.28      1   11      80      33     11     11     147   63     11  kaynak
merkez     1284.00   -37.45      1   11      98      27      9      9     155   60      9  kaynak

== sema_degisti , bozulan kaynak satiri 200 , kayit yokken bakilan 8650 satir
sunum      deger     fark    sun ozet tuketim tekille ayikla kaynak  toplam  kat  koken  kayip asamasi
dogu        878.11    -3.25      1   11      68     102     34     34     250   35     34  ayikla
bati        845.89    12.80      1   11      62      93     31     31     229   38     31  ayikla
kuzey      1218.67     6.12      1   11      92     138     46     46     334   26     46  ayikla
guney      1072.56     7.01      1   11      80     120     40     40     292   30     40  ayikla
merkez     1318.56    -2.89      1   11      98     147     49     49     355   24     49  ayikla
```

Kayıt yokken bakılan satır sayısı **9.260**'tır ve bu sayı beş sunum değerinin hangisinden
başlandığına bağlı değildir. Aynı şey ikinci koşumda da geçerlidir: orada da tek bir sayı vardır,
8.650. Kayıtsız aramanın maliyeti hedefe göre değişmez, çünkü daraltacak bir şey yoktur; hattın o
koşumda tuttuğu her satır okunur. Soyağacıyla aynı arama ilk koşumda 94 ile 155 satır arasında
bitiyor, ikincisinde 229 ile 355 arasında. Güney değeri için oran 63 kat, batı için 99 kat.

Bağ sayısının nasıl daraldığı sütunlarda görülüyor. Özet katmanında her zaman **11** hücre okunuyor;
on iki dönem var ama tüketim ilk dönemi düşürdüğü için bölge başına on bir özet hücresi kalıyor.
Tüketim katmanında okunan satır bölgenin abone sayısının iki katıdır: merkez 98, doğu 68. Bunlar
sapan iki özet hücresinin, yani `2026-07` ile `2026-08`'in ataları. Bozulma tek dönemi bozuyor ama
tüketim bir farktır ve bozulan dönem **iki** dönemin farkına girer.

İki koşumun asıl ayrıldığı yer son sütundur. `gec_gelen` koşumunda kayıp aşaması **kaynak**: şüpheli
anahtarlar hattın hiçbir katmanında yok, kaynak kaydında da yok, yani o satırlar hiç gelmedi.
`sema_degisti` koşumunda kayıp aşaması **ayikla**: aynı anahtarlar kaynakta duruyor, ama ayıklama
aşamasından sonra bağı yok. Kayıt bu iki durumu birbirinden ayırır. Kayıtsız arama ikisinde de aynı
9.260 ya da 8.650 satırı okur ve sonunda yalnız "eksik" der.

Sunum sapmasının büyüklüğü bozulmanın büyüklüğünü vermiyor. `sema_degisti` beş kat fazla satır
bozuyor (200 satır, 40 yerine) ama sunumu daha az oynatıyor: en büyük fark 12,80, ilk koşumda 54,28
idi. Dahası üç değer **yukarı** gidiyor. Bunun nedeni ikinci koşumda bozulan satırların dönem
ortalamasından tamamen çıkması, ilk koşumda ise ortalamaya eksik bir toplamla girmesidir. Sunuma
bakarak bozulmanın çapını okumak iki koşumda da yanlış sonuç verir.

#### Soyağacının Söylemediği

Kayıt bir arama alanı daraltır; bir yanıt vermez. Bunun en açık kanıtı `gec_gelen` koşumunun kaynak
sütunudur. Orada dokuz, on bir, dört anahtar okunuyor ve hepsi bulunamıyor; ama bu anahtarların
listesi bozuk koşumun kaydından çıkmadı, çünkü orada onlara ait **hiçbir bağ yok**. Liste sağlam
koşumun kaydından geldi. Soyağacı olmuş olanı yazar; olmamış bir satırı ancak beklenen ata kümesiyle
karşılaştırma görünür kılar. Beklenen küme yoksa kayıt yalnız var olanı gösterir ve eksik satır
kayıtta da sessizdir.

Kaydın ikinci sınırı bedelidir. 11.455 bağ her koşumda yeniden yazılır; yeniden işlenen bir dönem
için hat ikinci kez koştuğunda kayıt da ikinci kez yazılır. Bir önceki derste yeniden işlemenin
satırı çoğalttığı ölçülmüştü; soyağacı bu çoğalmayı da taşır ve hangi koşumun bağı olduğu ayrıca
yazılmazsa iki koşumun bağları birbirine karışır.

Üçüncü sınır bu dersin dışına çıkar. Kayıp aşaması `kaynak` çıktığında hat kendi içinde temizdir:
hiçbir aşama satır düşürmedi, satır hiç gelmedi. Soyağacı bunu doğru söyler ve orada durur. Kırk
satırın neden gelmediğini, kimin göndermesi gerektiğini ve bulgunun kime iletileceğini bilmez.
Kaynak, hattın **sahibi olmadığı** bir yerdir.

#### Özet

- Soyağacı kaydı, her aşamanın çıktı anahtarından girdi anahtarlarına yazdığı bağ kümesidir; hat
  altı katmanda 9.460 satır taşırken kayıt 11.455 bağ tutar, satır başına 1,21.
- Bağ sayısını satır sayısı değil çıktının kaç girdiye dayandığı belirler: `tuketim` 2.200 satır
  üretir, 4.400 bağ yazar, çünkü bir tüketim satırı iki okumanın farkıdır.
- Kayıt yokken bir sunum değerini açıklamak 9.260 satır okumak demektir ve bu sayı hangi değerden
  başlandığına bağlı değildir; kayıtla aynı arama 94 ile 155 satır arasında biter.
- İki koşum iki ayrı kayıp aşaması verir: `gec_gelen` için kaynak, `sema_degisti` için ayıklama.
  Kayıtsız arama ikisini birbirinden ayıramaz.
- Sunum sapmasının büyüklüğü bozulmanın büyüklüğünü vermez: beş kat fazla satır bozulduğunda en
  büyük fark 54,28'den 12,80'e düşer ve üç değer yukarı gider.
- Soyağacı hiç gelmemiş satıra bağ yazamaz; eksik satırı ancak sağlam koşumun beklenen ata kümesiyle
  karşılaştırma görünür kılar.

#### Sonraki Adım

Bu derste arama kaynağa kadar indi ve orada durdu. Kayıp aşaması `kaynak` çıktığında yanıtlanan soru
"hangi satırlar" oldu; yanıtlanmayan soru "kim" idi. Hattın beş aşaması var, kaynak da sayılırsa
altı; bunların kaçı bir sahip taşıyor, sahibi olmayan bir aşamada yakalanan bozulmanın bulgusu kime
gidiyor ve o kişi düzeltebileceği aşamadan kaç adım uzakta duruyor. Aynı soru geriye doğru da
sorulur: soyağacı bir dönemin satırlarına bağ yazmış olsa bile, o satırlar saklama süresi dolduğu
için silinmişse bağ nereye işaret eder. Sonraki ders sahipliği, erişimi ve saklamayı üç sayıya
çevirir ve kursu kapatır.

### Veri Yönetişimi

Source: https://academia.sh/tr/kurslar/veri-muhendisligine-giris/veri-yonetisimi

Sahiplik boşluğunun ve saklama politikasının sayılması: kaynağıyla birlikte altı konum taşıyan hatta beş aşamanın üçü bir sahip taşıyor, ama altı bozulmanın altısı da sahipsiz bir konumda yakalanıyor, çünkü denetimler yalnız kaynakta ve özet katmanında koşuyor. Bulgu aşağıya yürüyerek bir sahibe ulaşıyor ve altı bulgunun üçü onarabilecek sahibe gidiyor; kalan üçünde alıcı onarım yerinden bir ile beş aşama uzakta ve kanıta erişimi yok. Onarım yeri sahipsiz olan üç sınıf, kusurlu denetim kümesiyle hiç yakalanmayan üç sınıfla birebir aynı. Altı dönemlik saklama penceresi altı dönem siliyor ama geriye dönük denetlenemez dönem yediye çıkıyor, özet 55 hücreden 25'e iniyor, soyağacının 4400 tüketim bağının 2200'ü silinmiş satıra işaret ediyor ve doğru bölüm denetimi sağlam veride yanlış alarm veriyor. Ders kursun kapanışıdır.

Bir önceki derste arama kaynağa kadar indi ve orada durdu. Soyağacı hangi satırların eksik
olduğunu söyledi, kayıp aşamasının `kaynak` olduğunu yazdı ve sustu. Kırk satırın neden gelmediği
ve bulgunun kime iletileceği kaydın söyleyemeyeceği şeylerdi. Hattın beş aşaması var, kaynak da
sayılırsa altı; bunların kaçı bir sahip taşıyor.

Bu dersin ölçtüğü şey bir **boşluktur**: bir bozulmanın yakalandığı yer ile onarılabileceği yer
arasındaki mesafe. Boşluk üç sayıyla okunur — bulgu kime gidiyor, o rol onarımı yapabiliyor mu,
kanıta bakabiliyor mu. İkinci ölçü aynı boşluğu zaman ekseninde okur: saklama penceresi
daraldığında kaç dönem geriye dönük denetlenemez hâle geliyor. Bütün sayılar sayımdır; oran
basılmaz.

- **VK47.** Kaynak, beş aşamalı hat, bozulma sınıfları ve sahiplik haritası **kurgudur** ve ders
  içinde tanımlanır. Hiçbir depo, katalog ürünü, orkestratör ya da politika metni çağrılmaz. Tohum
  20260218.
- **VK48.** **Sahip**, bir konumun çıktısını değiştirme yetkisi olan **rol sınıfıdır**; kişi ya da
  kurum değildir. **Erişim**, bir rolün hangi konumun satırlarını okuyabildiğidir.
- **VK49.** Bir bulgu **yakalandığı konumda doğar**. Konumun sahibi yoksa bulgu aşağı doğru ilk
  sahipli konuma yürür; sahipli konum kalmamışsa bulgu kimseye gitmez.
- **VK50.** **Onarım yeri**, bozulmanın giderilebileceği **en erken konumdur** ve bulgunun alındığı
  konumla aynı olmak zorunda değildir. **Uzaklık** ikisi arasındaki aşama sayısıdır.
- **VK51.** Yakalayan denetim ve yakalama gecikmesi bu kursun birinci dersinde ölçüldü; burada
  **yeniden ölçülmez**, girdi olarak alınır. Erişim denetimi, saklama politikası ve kişisel veri
  yordamı Siber Güvenlik müfredatındaki Kimlik, Erişim ve Kriptografi ile Yönetişim, Risk ve
  Uyumluluk kurslarında ölçüldü; **tekrarlanmaz**.
- **VK52.** **Saklama penceresi** son N dönemi tutar, öncesini siler. Bir dönem, tüketim satırı
  kalan kaynaktan yeniden hesaplanabiliyorsa **geriye dönük denetlenebilir** sayılır.

#### Sahiplik Haritası ve Bulgunun Yürüdüğü Yol

Sahiplik bir belge değil bir eşlemedir: hangi konumun çıktısını kim değiştirebilir. Aşağıdaki
harita iki rol tanır, kalan konumlar boştur. Boşluk uydurma değil hattın doğal sonucudur; ara
katmanlar kimsenin ürünü sayılmaz.

```python
# KURGUDUR. Sahiplik, erisim ve onarim haritasi ders icinde tanimlanir; hicbir
# duzenleme metni, kurum, katalog urunu ya da orkestrator cagrilmaz.
KONUM = ["kaynak", "ayikla", "tekille", "tuketim", "ozet", "sun"]
# Sahip bir kisi degil bir rol sinifidir; sahipsiz konumun degeri None'dir.
SAHIP = {"kaynak": None, "ayikla": "hat ekibi", "tekille": "hat ekibi",
         "tuketim": None, "ozet": None, "sun": "raporlama ekibi"}
ERISIM = {"hat ekibi": {"ayikla", "tekille", "tuketim", "ozet"},
          "raporlama ekibi": {"ozet", "sun"}}
# Yakalayan denetim ve yakalama gecikmesi (asama) bu kursun birinci dersinde olculdu.
YAKALAMA = {"gec_gelen": ("dagilim", 4), "yinelenen": ("tekillik", 0),
            "sema_degisti": ("sema", 0), "birim_degisti": ("dagilim", 4),
            "eksik_bolum": ("bolum_donem", 0), "kayan_tip": ("tip", 0)}
# Bozulmanin gercekten onarilabilecegi en erken konum.
ONARIM = {"gec_gelen": "kaynak", "yinelenen": "tekille", "sema_degisti": "ayikla",
          "birim_degisti": "kaynak", "eksik_bolum": "kaynak", "kayan_tip": "ayikla"}
# Kusurlu denetim kumesiyle hic yakalanmayan siniflar, yine birinci dersten.
KACAN = {"gec_gelen", "birim_degisti", "eksik_bolum"}


def yonlendir(i):
    """Bulgu yakalandigi konumda dogar; konum sahipsizse ilk sahipli konuma yurur."""
    for j in range(i, len(KONUM)):
        if SAHIP[KONUM[j]]:
            return j, SAHIP[KONUM[j]]
    return None, None


def eh(x):
    return "evet" if x else "hayir"


print(f"bes asamanin {sum(1 for a in KONUM[1:] if SAHIP[a])} tanesi sahipli ;"
      " kaynak konumu hattin disinda ve sahipsiz")
print("\nbozulma         yakalayan  gecikme  konum sahibi  bulgu kime        onarim"
      "  uzaklik  yetki  erisim  kusurlu kumede")
yetkili = erisen = 0
for s, (yak, g) in YAKALAMA.items():
    j, alici = yonlendir(g)
    o = ONARIM[s]
    yetki, eris = SAHIP[o] == alici, o in ERISIM[alici]
    yetkili, erisen = yetkili + yetki, erisen + eris
    print(f"{s:<13} {yak:>11} {g:>8}  {SAHIP[KONUM[g]] or 'yok':>12}  {alici:<17}"
          f" {o:<7} {abs(KONUM.index(o) - j):>7}  {eh(yetki):>5}  {eh(eris):>6}"
          f"  {'kaciyor' if s in KACAN else 'yakalaniyor':>14}")
sahipsiz = {s for s in ONARIM if SAHIP[ONARIM[s]] is None}
print(f"\nbulguyu alan sahip {yetkili} bozulmada onarabiliyor ,"
      f" {erisen} bozulmada kanita erisimi var")
print(f"onarim yeri sahipsiz olan {len(sahipsiz)} sinif , kusurlu kumeyle hic yakalanmayan"
      f" {len(KACAN)} sinif , ayni kume mi: {eh(sahipsiz == KACAN)}")
```

```
bes asamanin 3 tanesi sahipli ; kaynak konumu hattin disinda ve sahipsiz

bozulma         yakalayan  gecikme  konum sahibi  bulgu kime        onarim  uzaklik  yetki  erisim  kusurlu kumede
gec_gelen         dagilim        4           yok  raporlama ekibi   kaynak        5  hayir   hayir         kaciyor
yinelenen        tekillik        0           yok  hat ekibi         tekille       1   evet    evet     yakalaniyor
sema_degisti         sema        0           yok  hat ekibi         ayikla        0   evet    evet     yakalaniyor
birim_degisti     dagilim        4           yok  raporlama ekibi   kaynak        5  hayir   hayir         kaciyor
eksik_bolum   bolum_donem        0           yok  hat ekibi         kaynak        1  hayir   hayir         kaciyor
kayan_tip             tip        0           yok  hat ekibi         ayikla        0   evet    evet     yakalaniyor

bulguyu alan sahip 3 bozulmada onarabiliyor , 3 bozulmada kanita erisimi var
onarim yeri sahipsiz olan 3 sinif , kusurlu kumeyle hic yakalanmayan 3 sinif , ayni kume mi: evet
```

Tablonun en sert sütunu dördüncüsüdür ve tek bir değer taşır: **yok**. Beş aşamanın üçü sahipli
olduğu hâlde altı bozulmanın hiçbiri sahipli bir konumda yakalanmıyor. Nedeni denetimlerin
yerleşimidir: yedi denetimin altısı kaynakta, biri özet katmanında koşuyor ve iki konum da
sahipsiz. Sahiplik hattın **ürettiği** yere yazılmış, denetimin **baktığı** yere yazılmamıştır. Bir
konumu sahiplendirmek ile orada denetim koşturmak iki ayrı karardır; örtüşmemeleri ölçülebilir bir
kusurdur.

Bulgu yürüdüğü için yine de bir alıcı buluyor, ama alıcı çoğu zaman yanlış roldür. Üç bozulmada hat
ekibi hem yetkili hem erişimlidir: `yinelenen` tekilleştirmede, `sema_degisti` ve `kayan_tip`
ayıklamada onarılır. Kalan üçünde onarım yeri `kaynak`tır ve kaynağın sahibi yoktur. `eksik_bolum`
bulgusu bir aşama uzağa düşer; `gec_gelen` ile `birim_degisti` **beş aşama** uzağa, elinde yalnız
sapmış bir sunum değeri olan raporlama ekibine gider.

Son satır bu dersin en dar bulgusudur. Onarım yeri sahipsiz olan üç sınıf, kusurlu denetim
kümesiyle hiç yakalanmayan üç sınıfla **birebir aynıdır**. İki liste bağımsız çıktı: biri
denetimlerin kapsamından, öbürü rol haritasından. Aynı olmaları, en zayıf denetlenen konumun en
zayıf sahiplenilen konum olduğunu gösterir.

#### Saklama Penceresi ve Geriye Dönük Denetim

Sahiplik boşluğunun ikinci yüzü zamandadır. Silinen satır yalnız yer boşaltmaz, geriye dönük
denetimin dayanağını da götürür. Aşağıdaki blok aynı kurgu kaynağı beş saklama penceresiyle
koşturur.

```python
# KURGUDUR. Olcum aginin sayac okumalari ders icinde uretilir. Saglam kaynakta
# ilk iki asama satir dusurmedigi icin tuketim dogrudan kaynaktan hesaplanir.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def ad(k):
    return (k["abone"], k["donem"])


def a3_tuketim(v):
    """Donem farkindan tuketim; onceki donem yoksa satir hesaplanamaz."""
    tablo, cikti = {ad(k): k for k in v}, []
    for k in v:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is not None:
            cikti.append(dict(k, m3=k["endeks"] - onceki["endeks"]))
    return cikti


def d_bolum_donem(v):
    """Dogru bolum denetimi: her donemde her bolge yazilmis mi."""
    return {(k["bolge"], k["donem"]) for k in v} != {(b, d) for b in BOLGE for d in DONEM}


def sakla(kayit, pencere):
    """Saklama politikasi: yalniz son `pencere` donem tutulur, oncesi silinir."""
    tutulan = set(DONEM[len(DONEM) - pencere:])
    return [k for k in kayit if k["donem"] in tutulan], tutulan


kaynak = kaynak_uret()
# Her tuketim satirinin iki atasi vardir: kendi donemi ve bir onceki donem.
BAG = [x for k in a3_tuketim(kaynak)
       for x in (ad(k), (k["abone"], DONEM[DONEM.index(k["donem"]) - 1]))]
print(f"kaynak {len(kaynak)} satir , tuketim satiri {len(a3_tuketim(kaynak))} ,"
      f" tuketim bagi {len(BAG)}")
print("\npencere  silinen  kalan  yeniden uretilebilen  denetlenemez"
      "          2026-07  alarm  askida")
for p in (12, 9, 6, 3, 1):
    kalan, tutulan = sakla(kaynak, p)
    uretilebilen = sum(1 for i, d in enumerate(DONEM)
                       if i and d in tutulan and DONEM[i - 1] in tutulan)
    anahtar = {ad(k) for k in kalan}
    durum = ("silinmis" if "2026-07" not in tutulan else
             "atasi silinmis" if "2026-06" not in tutulan else "denetlenebilir")
    print(f"{p:>7}  {12 - p:>7}  {len(kalan):>5}  {uretilebilen:>20}"
          f"  {12 - uretilebilen:>12}  {durum:>15}"
          f"  {eh(d_bolum_donem(kalan)):>5}  {sum(1 for b in BAG if b not in anahtar):>6}")
```

```
kaynak 2400 satir , tuketim satiri 2200 , tuketim bagi 4400

pencere  silinen  kalan  yeniden uretilebilen  denetlenemez          2026-07  alarm  askida
     12        0   2400                    11             1   denetlenebilir  hayir       0
      9        3   1800                     8             4   denetlenebilir   evet    1000
      6        6   1200                     5             7   atasi silinmis   evet    2200
      3        9    600                     2            10         silinmis   evet    3400
      1       11    200                     0            12         silinmis   evet    4200
```

Saklama politikası sildiğinden **bir fazla** dönemi denetlenemez kılıyor: altı dönem silindiğinde
denetlenemez dönem yedi, dokuz silindiğinde on. Fazladan gelen dönem her zaman **pencerenin en
eskisidir**; satırları duruyor ama tüketim bir farktır ve o farkın öteki ucu silinmiştir. Altı
dönemlik pencerede `2026-07` satırları hâlâ yerinde, buna karşın yeniden hesaplanamıyor ve özet
katmanı 25 hücreye iniyor. Bir saklama süresi "son altı dönem denetlenebilir" diye okunursa yanlış
okunmuştur; denetlenebilir olan beş dönemdir.

Son sütun bir önceki dersin bıraktığı soruyu yanıtlar. Soyağacı 4.400 tüketim bağı yazmıştı; altı
dönemlik pencerede bunun **2.200'ü** artık var olmayan bir satıra işaret ediyor ve 200 tanesi tam
da sınır döneminin atalarıdır. Kaydın korunması izlenebilirliği korumaz; bağın gösterdiği satır
silinmişse kayıt yalnız kaybın yerini bildirir.

Alarm sütunu ikinci bedeldir. Dönem duyarlı bölüm denetimi bu kursta **doğru** denetim olarak
kuruldu. Saklama uygulandığında beklediği dönem kümesi gerçekleşmiş kümeden büyük kalıyor ve
denetim sağlam veride ateşliyor: bozulma yok, bulgu var. Doğru bir denetim, bakması gereken alan
daraltıldığında yanlış alarm üreticisine dönüşüyor, çünkü beklenen dönem kümesini politikayla
birlikte güncelleyen olmadı. Bunun kimin işi olduğu ilk bölümün sorusudur: politikanın uygulandığı
konum `kaynak`tır ve sahibi yoktur.

#### Özet

- Sahiplik, bir konumun çıktısını değiştirme yetkisidir; beş aşamanın üçü sahipli olsa da altı
  bozulmanın altısı **sahipsiz** bir konumda yakalanır, çünkü denetimler kaynakta ve özet
  katmanında koşar, sahiplik ise üretilen katmanlara yazılmıştır.
- Bulgu aşağı yürüyerek alıcı bulur ama üç bozulmada alıcı onarımı yapamaz; `gec_gelen` ile
  `birim_degisti` onarım yerinden **beş aşama** uzaktaki bir role gider ve o rolün kanıta erişimi
  yoktur.
- Onarım yeri sahipsiz olan üç sınıf, kusurlu denetim kümesiyle hiç yakalanmayan üç sınıfla birebir
  aynıdır: en zayıf denetlenen konum en zayıf sahiplenilen konumdur.
- Saklama politikası sildiğinden bir fazla dönemi denetlenemez kılar; altı dönem silindiğinde
  denetlenemez dönem 7 olur, özet 25 hücreye iner ve pencerenin en eski dönemi satırları dururken
  yeniden hesaplanamaz.
- Kayıt saklamadan bağımsız değildir: 4.400 tüketim bağının 2.200'ü silinmiş satıra işaret eder ve
  doğru bölüm denetimi yanlış alarma dönüşür.

#### Kurs Kapanışı

| Ders | Denetimsiz hat | Denetimli hat | Bedel |
|---|---|---|---|
| `veri-yasam-dongusu/01` — yakalanan bozulma | 0 yakalama, gecikme **tanımsız** | **ölçülmez** — ders yalnız ölçü eksenini kurar, denetim koşturmaz | iz 2400/2400/2200/55/5; değer başına 480 satır (588 / 372) |
| `veri-yasam-dongusu/02` — bozulmayı gören aşama (6 sınıf) | `sun` her sınıfta 5 değer: **0** | `ayikla` 1, `tekille` 1 (**+2**) | `tuketim` eksik satırı ikiye katlar: 40→80, 46→92, 200→400 |
| `veri-yasam-dongusu/03` — iki ölçüt: satır ve sapma (m3) | satır: `sema_degisti` 2200→1800, en gürültülü | sapma: aynı sınıf 0,003, `gec_gelen` **0,105** — sıralama tersine döner | `eksik_bolum` sapmayı 10,832 → 10,829 düşürür |
| `veri-yasam-dongusu/04` — kaynak sınıfının taşıdığı bozulma sınıfı | hoşgörülü dış servis 6 sınıf, gecikme 8 aşama | bildirimli tür, birincil anahtar, tutarlı okuma 3 sınıf (**−3**), 4 aşama (**−4**) | hoşgörülü ayrıştırmada 200 satır bozulur, yakalayan olmaz; satır sayısı denetimi 0,05 payla hiçbirini yakalamaz |
| `veri-yasam-dongusu/05` — bozulmanın görüldüğü dönem (kip) | toplu: dönem gecikmesi 1, 5 aşama, 13200 satır | akış: 0 (**−1**), 8 aşama (**+3**), 2400 satır (**−10800**) | akış 2600 girdi taşır; geç gelen satırda toplu 55 hücreyi doğru bırakır, akış 10'unu kirletir |
| `veri-kalitesi/01` — yakalama gecikmesi (aşama) | 0 yakalama, gecikme **tanımsız** | altı sınıfın altısı yakalanır (**+6**), gecikme 4/0/0/4/0/0 | kusurlu küme üç sınıfı hiç yakalamaz; `bolum` 46 satır eksikken yeşil kalır |
| `veri-kalitesi/02` — kaybın yakalandığı aşama | sözleşmesiz: 200 + 200 satır sessizce düşer, özet 55→45 | sözleşmeli: aşama **0**, özet yine 45 — çıktı düzelmez | katı sözleşme 1200 iyi satır engeller (özet 25); yük düzeyi 2200 engeller, çıktı yok |
| `veri-kalitesi/03` — ham satır ve sunulan değer | ekleyen depo 2400 → 4800 → 7200 | anahtarlayan yazma 2400 sabit, çoğalan satır **0** (**−4800**) | kuzey üç koşumda da 1212,55; kusurlu tekilleştirmede 2425,09 ve 3637,64 |
| `veri-kalitesi/04` — kaynağa giden aramada bakılan satır | kayıtsız 9260 satır, hedeften bağımsız | soyağacıyla 94–155 satır (**60–99 kat az**) | 11455 bağ, satır başına 1,21; gelmemiş satıra bağ yazılamaz |
| `veri-kalitesi/05` — sahiplik boşluğu ve denetlenemez dönem | sahiplik yazılmamış: bulgunun alıcısı yok, denetlenemez dönem 1 | beş aşamanın 3'ü sahipli, altı bulgunun 3'ü onarabilen sahibe gider (**+3**) | altı yakalama konumunun **0'ı** sahipli; altı dönemlik saklama denetlenemez dönemi 7'ye çıkarır (**+6**), askıda bağ 2200 |

Tablo tek bir kuralın on kez yazılmasıdır. **Bir veri hattının sayısı taşıdığı satır değil, yanlış
satırın kaç adım sonra yakalandığıdır; bozulmayı yakalayan denetimi yazılmayan hat ölçülmemiş
sayılır.** Denetimsiz sütunundaki hiçbir sayı kötü bir kodun sonucu değildir; hepsi, taşınan satır
sayıldığı hâlde doğru olup olmadığının sorulmadığı bir düzenin sayılarıdır.

**İkinci iddia da aynı tablodan okunur: eklenen denetim de bozar.** Adıyla yazan satırlar şunlardır.
`veri-kalitesi/02`'de katı sözleşme zararsız bir alan eklemesinde 1200 iyi satırı engeller ve özeti
25 hücreye düşürür — üstelik yakalanan bozulma sıfırken; aynı derste yük düzeyinde uygulama 2200
iyi satırı engelleyip hiç çıktı vermez. `veri-kalitesi/01`'de `bolum` denetimi 46 satır
yazılmamışken yeşil kalır: yanlış güvence denetimsizlikten pahalıdır. `veri-yasam-dongusu/04`'te
satır sayısı denetimi 0,05 payla hiçbir sınıfı yakalamaz. `veri-yasam-dongusu/05`'te akış kipi geç
gelen satırda 10 özet hücresini kalıcı kirletir, toplu kip aynı bozulmada 55 hücreyi doğru bırakır.
`veri-kalitesi/03`'te tekilleştirme düzeltilmiş yükü yutar. `veri-kalitesi/05`'te saklama
politikası doğru bölüm denetimini yanlış alarma çevirir.

**Üçüncü iddia sunulan sayıya bakmanın neden yetmediğidir** ve üç bozulma adıyla sayılır.
`yinelenen` beş değeri birebir aynı bırakır, çünkü tekilleştirme fazlalığı yutar;
`veri-yasam-dongusu/02`, `veri-yasam-dongusu/04` ve `veri-kalitesi/01` bunu ayrı ayrı ölçtü.
`kayan_tip` aynı şeyi tür dönüşümünde yapar. `birim_degisti` en sessizidir: özetin on hücresini
1812946491 ile −1812944121 arasına taşır, iki dönem birbirini iptal eder ve kuzeyin değeri
1212,55'te kalır. Üçü de sunuma bakan hiçbir gözlemle görülemez.

On derste hattın taşıdığı satır sayıldı ve bozulmanın kaç aşama sonra yakalandığı ölçüldü. Bir şey
hiç sorulmadı: bu satırların **nerede durduğu**. Kaynak 2400 satır, özet 55 hücre, bağ 11455 kayıt
olarak anıldı — ama hangi düzende yazıldıkları, bölümlere ayrılıp ayrılmadıkları ve o düzenin bir
sorguyu **kaça katladığı** bir kez bile sayılmadı. Sonraki kurs bu düzeni bir ölçüm nesnesine
dönüştürür.

