---
title: 'Görselleştirme, Raporlama ve Ölçekli Analiz'
source: 'https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama'
language: tr
updated: '2026-08-17T04:40:09+00:00'
license: 'CC BY-SA 4.0'
lessons: 18
outcomes:
    - 'Veriye ve soruya uygun gösterimi kodlama kanalının geri okuma hatasıyla seçer'
    - 'Gösterge tablosunu ve ölçütleri karar destekleyecek biçimde kurar ve oynatılabilirliğini sınar'
    - 'Bulguyu hedef kitleye taşır ve raporu yeniden üretilebilir kılar'
    - 'Ölçek büyüdüğünde raporun hangi kısmının bölünebildiğini sayar'
---

# Görselleştirme, Raporlama ve Ölçekli Analiz

Bir bulgunun okuyucuya taşınırken ne kadarının sağ kaldığının ölçülmesi: kodlama kanalının geri okuttuğu sayı ile kaynaktaki sayı arasındaki fark, dağılım ve kategorik gösterimlerin özetten geri veremediği biçim, ilişki ve zaman gösterimlerinin taşıdığı eğilim, alan ve renk kanallarının farkı büyütüp küçültmesi, tek kanala yaslanan gösterimin kaç seriyi ayırt edilemez bıraktığı, gösterge tablosundan geri okunabilen ölçüt sayısı, ölçütün ölçtüğü şeyi oynatmadan oynatılabilmesi, raporun yeniden üretilme adımı, ve raporun ölçeğe dayanması: eşle-indirge geçişleri, sütunlu düzenin okuttuğu değer ve bölünemeyen payın koyduğu tavan.

## Grafik Tasarımı

Sayının kodlama kanalından geçerken ne kadarının okuyucuya ulaştığının ölçülmesi: karşılaştırma, dağılım, bileşim ve ilişki sorularının hangi kanalı gerektirdiği, histogram aralığı ile kutu özetinin dağılımdan geri veremediği biçim, yığılmış ve gruplu gösterimlerin taban çizgisi olmayan dilimlerde ürettiği okuma hatası, saçılım ile ısı haritasının ilişkiden taşıdığı, zaman ekseninde eğilim ile mevsimselliğin ayrılması, alan kanalının oranı karesiyle büyütmesi, eksen kırpmanın ötesindeki çarpıtmalar ve renk tek başına kaldığında ayırt edilemez kalan seri sayısı.

### Grafik Türü Seçimi

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/grafik-turu-secimi

Aynı beş bölge ortalaması beş kodlama kanalından geçirilip geri okunuyor ve ortalama geri okuma hatası şöyle çıkıyor: konum 0,070 m³, uzunluk 0,183 m³, açı 0,543 m³, renk basamağı 0,931 m³, alan 2,468 m³. Doğu ile kuzey arasındaki 4,463 m³'lük fark alan kanalından 2,500 m³, açı kanalından 5,426 m³ olarak geri okunuyor ve tek bir okuma değişmiyor. Renk basamağı beş bölgeden dördünü ayırıyor, doğu ile merkez aynı basamağa düşüyor. Dört soru türünün her biri başka bir şeyin geri okunmasını istiyor ve kanal eşleştirmesi bu istekten çıkıyor.

Bir önceki kurs on üç derste her sayının yanına oynama payını yazdı ve şu soruyla kapandı: bütün
bu sayılar analistin ekranında, çıktı bloğu olarak duruyor. Kararı verecek kişi ne payı gördü ne
de sayıyı. Sayı ekrandan çıkıp bir karar vericiye giderken bir **gösterime** biniyor ve o
gösterimde ne olduğu hiç sorulmadı.

Bu kurs o soruyla açılır ve ölçüyü değiştirir. Önceki beş kursta sayı analistin elinde oynuyordu;
burada sayı **okuyucuya giderken** oynuyor. Bir gösterim bir **kodlama kanalıdır**: sayı bir
uzunluğa, konuma, açıya, alana ya da renk basamağına çevrilir, okuyucu da onu geri çevirir. Bu
derste hiçbir resim üretilmez; her kanal iki işlevle kurulur ve ikisi de koşar. Dersin sayısı,
aynı beş bölge ortalamasının beş kanaldan geçtikten sonra geri okunan halidir.

- **GT1. Bir gösterim bir kodlama kanalıdır.** Kanal iki işlevle tanımlanır: **kodlayıcı** sayıyı
  kanala çevirir, **çözücü** kanaldan sayıyı geri okur. İkisi de kodda durur ve koşar.
- **GT2. Okuma çözünürlüğü bir sayıdır.** Okuyucu bir kanalı sonsuz duyarlıkla okumaz; her kanalda
  ayırt edebildiği bir **basamak sayısı** vardır. Bu sayılar modelin varsayımıdır ve çıktının
  altında yazılıdır; ölçülen şey basamak sayısı değil, ondan çıkan farktır.
- **GT3. Ölçü farktır.** `çözücü(kodlayıcı(x))` ile `x` arasındaki fark **geri okuma hatası**dır.
  Kaynaktaki sayı hiç değişmez; oynayan şey okuyucuya ulaşan sayıdır.
- **GT4. Alan kanalının çözücüsü alanı okumaz.** Okuyucu şekli taşıyan doğrusal ölçüyü okur ve
  değeri ona oranla bildirir. Bu, çözünürlükten bağımsız, kanalın kendi ürettiği bir sapmadır.
- **GT5. Renk sürekli görünür, basamaklıdır.** Değer bir gösterge çizelgesinin sınırlı sayıdaki
  basamağından birine düşer ve o basamağın orta değeri olarak geri okunur. Renk burada bir ad
  değil, bir **basamak numarasıdır**.
- **GT6. Kanalın başarısı soruya bağlıdır.** Aynı kanal mutlak değeri kötü, payı iyi geri
  okutabilir. Hangi sorunun sorulduğu yazılmadan kanal seçilemez.
- **GT7. Karar eşiği yazılır.** Bir geri okuma hatası ancak bir eşikle karşılaştırıldığında büyük
  ya da küçüktür ve eşik raporun vereceği karardan gelir.

#### Kanal, Kodlayıcı ve Çözücü

```python
# kanal.py — MODELDIR: K03-K05'in kurgu olcum agi ayni tohumla yeniden uretilir
# (3.199 satirlik uzun bicimli okuma tablosu, 1.329 abone, uc donem, bes bolge).
# Grafik cizilmez: her gosterim bir kodlayici ile bir cozucuden ibarettir.
import math

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


def uretec(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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


_k, _e = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (_e if r() < 0.046 else _k).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {a["abone_no"] for a in _k}

OKUMA = []
for a in _k + _e:
    r = uretec(TOHUM + 7000 + a["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            a["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        if a["abone_no"] not in GECERLI:
            r()
            continue
        OKUMA.append({"abone_no": a["abone_no"], "bolge": a["bolge"],
                      "donem": DONEM[d], "tuketim_m3": t})
        r()

AD = [b[0] for b in BOLGE]
ORT, GOZLEM = {}, {}
for ad in AD:
    v = [o["tuketim_m3"] for o in OKUMA if o["bolge"] == ad]
    ORT[ad], GOZLEM[ad] = sum(v) / len(v), len(v)
TOPLAM = sum(ORT.values())
ALT, UST = 0.0, 25.0

# Okuma cozunurlugu: okuyucunun o kanalda ayirt edebildigi basamak sayisi.
# Sayilar modelin varsayimidir; olculen sey bu varsayimdan cikan farktir.
COZ = {"konum": 100, "uzunluk": 40, "aci": 36, "alan": 20, "renk": 7}


def izgara(t, n):
    return round(t * n) / n


def kodla_konum(v):
    return (v - ALT) / (UST - ALT)


def coz_konum(p):
    return ALT + (UST - ALT) * izgara(p, COZ["konum"])


def kodla_uzunluk(v):
    return (v - ALT) / (UST - ALT)


def coz_uzunluk(u):
    return ALT + (UST - ALT) * izgara(u, COZ["uzunluk"])


def kodla_aci(v):
    return 360.0 * v / TOPLAM


def coz_aci(a):
    return TOPLAM * izgara(a / 360.0, COZ["aci"])


def kodla_alan(v):
    return (v - ALT) / (UST - ALT)


def coz_alan(al):
    # Okuyucu alani degil, sekli tasiyan dogrusal olcuyu okur: yaricap.
    return ALT + (UST - ALT) * izgara(math.sqrt(al), COZ["alan"])


def kodla_renk(v):
    return (v - ALT) / (UST - ALT)


def coz_renk(t):
    n = COZ["renk"]
    b = min(n - 1, int(t * n))
    return ALT + (UST - ALT) * (b + 0.5) / n


KANAL = [("konum", kodla_konum, coz_konum), ("uzunluk", kodla_uzunluk, coz_uzunluk),
         ("aci", kodla_aci, coz_aci), ("alan", kodla_alan, coz_alan),
         ("renk", kodla_renk, coz_renk)]

print(f"kurgu kume: {len(OKUMA)} okuma, {len(GECERLI)} abone, {len(AD)} bolge")
print("bolge ortalamalari: " + ", ".join(f"{ad} {ORT[ad]:.3f}" for ad in AD) + " m3")
print(f"toplam {TOPLAM:.3f} m3, deger ekseni {ALT:.0f}-{UST:.0f} m3")
print("okuma cozunurlugu (basamak): " + ", ".join(f"{k} {v}" for k, v in COZ.items()))
```

```
kurgu kume: 3199 okuma, 1329 abone, 5 bolge
bolge ortalamalari: kuzey 20.210, guney 16.602, dogu 24.674, bati 13.778, merkez 22.396 m3
toplam 97.659 m3, deger ekseni 0-25 m3
okuma cozunurlugu (basamak): konum 100, uzunluk 40, aci 36, alan 20, renk 7
```

Küme kurgudur ve önceki üç kursun kullandığı ölçüm ağının aynısıdır: aynı tohum, aynı 3.199 satır,
aynı 1.329 abone. Bölge ortalamaları da aynı sayılardır; doğunun 24,674 m³'ü ile kuzeyin 20,210
m³'ü bir önceki kursta hesaplanmıştı ve burada girdi olarak alınır.

Beş kanalın çözünürlüğü şu gerekçelerle yazıldı. **Konum** ortak bir eksende okunur ve okuyucunun
yanında bir ölçek durur; eksen üzerinde 100 basamak ayırt edilebilir kabul edildi. **Uzunluk**
kendi tabanından ölçülür ve karşılaştırma ölçeğe değil komşu çubuğa yapılır; 40 basamak yazıldı.
**Açı** bir dik açıya ve onun bölümlerine göre okunur, dokuzda biri kadar bir duyarlık varsayıldı
ve daire 36 basamağa bölündü. **Alan** için basamak sayısı 20'dir ama asıl sorun orada değildir:
çözücü alanı değil, alanı taşıyan doğrusal ölçüyü okur. **Renk** bir gösterge çizelgesinden
okunur ve çizelgede birbirinden ayırt edilebilen basamak sayısı 7 kabul edildi.

Bu beş sayı tartışılabilir ve okuyucudan okuyucuya değişir. Dersin iddiası sayıların kendisi
değil, **sıralamanın** onlardan çıkmasıdır: aynı değer bu beş kanaldan geçtiğinde geri okunan
sayılar birbirinden ayrılır ve ayrılmanın yönü çözünürlük seçiminden bağımsız olarak aynı kalır.

#### Aynı Sayı Beş Kanaldan Geçiyor

```python
# olcum.py — ayni bes sayi bes kanaldan gecer ve geri okunur. Olcu,
# cozucu(kodlayici(x)) ile x arasindaki farktir.
G = 40


def cubuk(v):
    return "#" * round(G * (v - ALT) / (UST - ALT))


print("uzunluk kanali, taban cizgisinden 40 basamak (okuyucunun gordugu):")
for ad in AD:
    print(f"{ad:8s}{ORT[ad]:7.3f} |{cubuk(ORT[ad]):<40s}| "
          f"geri okunan {coz_uzunluk(kodla_uzunluk(ORT[ad])):6.3f}")

print(f"\n{'bolge':8s}{'gozlem':>7s}{'kaynak':>9s}"
      + "".join(f"{k[0]:>10s}" for k in KANAL))
hata = {k[0]: [] for k in KANAL}
for ad in AD:
    x = ORT[ad]
    satir = f"{ad:8s}{GOZLEM[ad]:7d}{x:9.3f}"
    for k, kod, coz in KANAL:
        g = coz(kod(x))
        hata[k].append(abs(g - x))
        satir += f"{g:10.3f}"
    print(satir)
print(f"{'|hata|':8s}{sum(GOZLEM.values()):7d}{'':9s}"
      + "".join(f"{sum(hata[k[0]]) / len(AD):10.3f}" for k in KANAL))
```

```
uzunluk kanali, taban cizgisinden 40 basamak (okuyucunun gordugu):
kuzey    20.210 |################################        | geri okunan 20.000
guney    16.602 |###########################             | geri okunan 16.875
dogu     24.674 |####################################### | geri okunan 24.375
bati     13.778 |######################                  | geri okunan 13.750
merkez   22.396 |####################################    | geri okunan 22.500

bolge    gozlem   kaynak     konum   uzunluk       aci      alan      renk
kuzey       921   20.210    20.250    20.000    18.989    22.500    19.643
guney       645   16.602    16.500    16.875    16.277    20.000    16.071
dogu        557   24.674    24.750    24.375    24.415    25.000    23.214
bati        476   13.778    13.750    13.750    13.564    18.750    12.500
merkez      600   22.396    22.500    22.500    21.702    23.750    23.214
|hata|     3199              0.070     0.183     0.543     2.468     0.931
```

Çubuklar dersin bütün gösterim aygıtıdır: okuyucu kırk basamaklık bir çizgide bir uç görür ve o
uçtan bir sayı çıkarır. Kuzeyin 20,210 m³'ü kırk basamağın otuz ikisini doldurur ve otuz iki
basamak 20,000 m³ olarak geri okunur; aradaki 0,210 m³ kaybolmuştur çünkü otuz ikinci ile otuz
üçüncü basamak arasında bir yer yoktur.

Beş kanalın ortalama geri okuma hatası 0,070 ile 2,468 m³ arasında değişir; en iyi kanal ile en
kötüsü arasında otuz beş kat vardır. Sıra şudur: konum, uzunluk, açı, renk basamağı, alan. İlk
ikisi arasındaki fark yalnız çözünürlüktendir. Açıdan sonrası ise başka bir şey söyler.

**Alan kanalı** tek başına ele alınmayı hak eder, çünkü hatası çözünürlüğü artırarak
küçültülemez. Batının 13,778 m³'ü 18,750 olarak geri okunur, yani 4,972 m³ yukarı çıkar;
doğunun 24,674 m³'ü ise neredeyse yerinde kalır. Sapma her değerde aynı yöndedir ve küçük değerler
olduklarından büyük görünür. Sebebi GT4'tedir: alan değerle orantılı büyür, okuyucu ise alanı
değil onu taşıyan doğrusal ölçüyü okur. Bu, kanalın kullanım hatası değil kendisidir.

**Renk basamağı** başka bir biçimde takılır. Doğu 23,214, merkez de 23,214 okunur — iki bölge aynı
basamağa düşmüştür ve okuyucu için artık iki ayrı sayı değil tek bir sayıdır. Yedi basamaklı bir
çizelgede 25 m³'lük eksen 3,571 m³'lük dilimlere bölünür ve bu dilimden dar olan her fark yok olur.

Bir önceki kursun başlık sayısı burada sınanır. Doğu ile kuzey arasındaki fark 4,463 m³'tü ve
oynama payıyla birlikte raporlanmıştı. Aynı fark alan kanalından **2,500 m³** olarak, açı
kanalından **5,426 m³** olarak geri okunur. Kaynaktaki tek bir okuma değişmemiştir; değişen tek şey
sayının hangi kanala bindiğidir. M26/K02'nin `tablo-ozet/04` dersi aynı türden bir oynamayı eksen
kırpmasıyla ölçmüştü ve orada ölçülen şey **gösterimin meşruluğuydu**: bu grafik bu ölçekte olur
mu. Buradaki ölçü başkadır ve gösterim tamamen meşruyken de çalışır — pasta da, kabarcık da,
ısı haritası da kurallara uygundur, yine de sayıyı oynatırlar.

#### Soru Türü Kanalı Seçer

```python
# soru.py — dort soru turu, dort ayri geri okuma. Bir kanalin o soruya
# uygunlugu, sorunun istedigi seyin karar esigi icinde geri okunmasidir.
ESIK_FARK, ESIK_PAY = 0.5, 1.0
fark = ORT["dogu"] - ORT["kuzey"]
pay = {ad: ORT[ad] / TOPLAM for ad in AD}
print(f"kaynaktaki fark dogu-kuzey {fark:.3f} m3; karar esigi {ESIK_FARK} m3, "
      f"pay esigi {ESIK_PAY} puan")
print(f"{'kanal':9s}{'geri okunan fark':>18s}{'sapma':>8s}{'ayirt':>8s}"
      f"{'en buyuk pay hatasi':>21s}")
sonuc = {}
for k, kod, coz in KANAL:
    g = {ad: coz(kod(ORT[ad])) for ad in AD}
    gf = g["dogu"] - g["kuzey"]
    t = sum(g.values())
    gp = max(abs(100 * g[ad] / t - 100 * pay[ad]) for ad in AD)
    ayri = len({round(g[ad], 6) for ad in AD})
    sonuc[k] = (abs(gf - fark) <= ESIK_FARK, ayri == len(AD), gp <= ESIK_PAY)
    print(f"{k:9s}{gf:18.3f}{abs(gf - fark):8.3f}{ayri:6d}/5{gp:21.2f}")

SORU = [("karsilastirma", "iki degerin farki"), ("dagilim", "bes degerin sirasi"),
        ("bilesim", "her degerin payi")]
print(f"\n{'soru':16s}{'geri okunmasi gereken':24s}"
      + "".join(f"{k[0]:>9s}" for k in KANAL))
for j, (ad, ne) in enumerate(SORU):
    print(f"{ad:16s}{ne:24s}"
          + "".join(f"{'evet' if sonuc[k[0]][j] else 'hayir':>9s}" for k in KANAL))
print(f"{'iliski':16s}{'iki deger ayni anda':24s}"
      + "".join(f"{'evet' if k[0] == 'konum' else 'hayir':>9s}" for k in KANAL))
```

```
kaynaktaki fark dogu-kuzey 4.463 m3; karar esigi 0.5 m3, pay esigi 1.0 puan
kanal      geri okunan fark   sapma   ayirt  en buyuk pay hatasi
konum                 4.500   0.037     5/5                 0.12
uzunluk               4.375   0.088     5/5                 0.31
aci                   5.426   0.962     5/5                 0.69
alan                  2.500   1.963     5/5                 2.94
renk                  3.571   0.892     4/5                 1.60

soru            geri okunmasi gereken       konum  uzunluk      aci     alan     renk
karsilastirma   iki degerin farki            evet     evet    hayir    hayir    hayir
dagilim         bes degerin sirasi           evet     evet     evet     evet    hayir
bilesim         her degerin payi             evet     evet     evet    hayir    hayir
iliski          iki deger ayni anda          evet    hayir    hayir    hayir    hayir
```

İki eşik yazıldı ve ikisi de rapordan gelir. **0,5 m³**, bölge farkının rapora iki ondalıkla
yazılmasından ve saha ekibinin hangi bölgeye gideceğine bir m³'lük farkla karar verilmesinden
çıkar. **1,0 puan**, payın raporda tam sayı yüzde olarak yazılmasından çıkar: bir puanlık hata
yazılan rakamı değiştirir. Eşik yazılmadan hiçbir geri okuma hatası büyük ya da küçük değildir.

Tablonun ilk satırı beklenendir: karşılaştırma sorusunu yalnız konum ve uzunluk taşır. İkinci
satır kanalların çoğunu geçirir, çünkü sıralamayı korumak bir kanaldan istenebilecek en hafif
iştir; yalnız renk basamağı takılır ve tam olarak yukarıda görülen sebeple, iki bölge aynı
basamağa düştüğü için.

Üçüncü satır beklenmediktir. **Açı kanalı payı geçirir ama farkı geçirmez.** Sebebi kodlayıcıdadır:
açı zaten payı kodlar, mutlak değeri değil. Açının çözünürlüğü paydan yana harcanır ve pay hatası
0,69 puanda kalırken aynı kanalın mutlak fark hatası 0,962 m³'e çıkar. Bunun tersi de doğrudur:
alan kanalı dört sorudan yalnız sıralamayı geçirir, çünkü GT4'teki sapma her değeri aynı yöne
ittiği için sıra korunur, ama hem farkı hem payı bozar ve payı 2,94 puanla en çok bozan kanaldır.

Dördüncü satır ölçülmez, sayılır. İlişki sorusu iki değerin **aynı anda** geri okunmasını ister
ve bunun için kanalın ikinci bir bağımsız kopyası gerekir. Yalnız konumun iki kopyası vardır,
yatay ve düşey eksen; uzunluğun, açının, alanın ve rengin ikinci kopyası yoktur. Bu yüzden ilişki
sorusu bir kanal tercihi değil, bir kanal zorunluluğudur.

Buradan çıkan kural GT6'nın somut halidir: kanal veriden değil sorudan seçilir. Aynı beş sayı,
"hangisi daha büyük" sorusuna dört kanaldan, "ne kadar büyük" sorusuna iki kanaldan, "toplamın ne
kadarı" sorusuna üç kanaldan cevap verir.

#### Özet

- Bir gösterim iki işlevle modellenir: kodlayıcı sayıyı kanala çevirir, çözücü kanaldan sayıyı
  geri okur; ölçü ikisinin bileşimi ile kaynaktaki sayı arasındaki geri okuma hatasıdır.
- Okuma çözünürlüğü bir sayı olarak yazılır — konum 100, uzunluk 40, açı 36, alan 20, renk 7
  basamak — ve bu sayılar modelin varsayımıdır, ölçülen şey onlardan çıkan farktır.
- Aynı beş bölge ortalamasının ortalama geri okuma hatası konumda 0,070, uzunlukta 0,183, açıda
  0,543, renk basamağında 0,931, alanda 2,468 m³ çıkar.
- Alanın hatası çözünürlükle küçültülemez, çünkü çözücü alanı değil doğrusal ölçüyü okur; renk
  basamağı ise beş bölgeden yalnız dördünü ayırır, doğu ile merkez aynı basamağa düşer.
- Doğu ile kuzey arasındaki 4,463 m³'lük fark alandan 2,500 m³, açıdan 5,426 m³ olarak geri okunur
  ve kaynaktaki tek bir okuma değişmez.
- Kanal veriden değil sorudan seçilir: açı payı geçirir ama farkı geçirmez, ilişki sorusu ise
  kanalın ikinci bağımsız kopyasını ister ve bu kopya yalnız konumda vardır.

#### Sonraki Adım

Bu derste her bölge tek bir sayıya indirilmişti ve kanal o tek sayıyı taşıdı. Rapordaki soruların
bir kısmı ise tek sayıyla cevaplanmaz: kuzeyin 921 okuması nasıl dizilmiş, iki tepe mi var yoksa
bir mi, uç değerler nerede duruyor. Sonraki ders aynı ölçüyü bir **dağılıma** uygular ve üç
gösterimi karşılaştırır — aralık sayımı, kutu özeti ve yoğunluk. Geri okunacak şey artık bir sayı
değil bir **biçim özelliğidir** ve ölçü, üç gösterimden hangisinin ikinci tepeyi geri okuttuğudur.
Aralık genişliğinin bir karar olduğu ve bu kararın geri okunan tepe sayısını oynattığı sayıyla
gösterilir.

### Dağılım Görselleştirme

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/dagilim-gorsellestirme

Kuzeyin 921 okuması ile aynı beş sayıyı veren ikiz küme üç gösterimden geçiriliyor ve geri okunan şey bir sayı değil bir biçim özelliği oluyor: aralık sayımı ve yoğunluk ikizin iki tepesini geri okuyor, kutu özeti tepe bilgisi hiç taşımıyor. Uzunluk kanalından geçen sayımlarda üç aralık sıfıra düşüyor ve 7 okuma gösterimden siliniyor, ikizin 45,47 m³'lük uç değeri kayboluyor; aynı uç değer kuzeyde geri okunuyor, çünkü en yüksek çubuk 329 değil 234. Aralık genişliği 12 m³ olduğunda geri okunan tepe sayısı ikide birden bire düşüyor, çekirdek genişliği 0,5 m³ olduğunda kuzeyde olmayan dört tepe görünüyor.

Bir önceki ders her bölgeyi tek bir sayıya indirdi ve kanal o tek sayıyı taşıdı. Rapordaki
soruların bir kısmı tek sayıyla cevaplanmaz: kuzeyin 921 okuması tek bir değer çevresinde mi
toplanıyor yoksa iki ayrı abone grubu mu var, en büyük okumalar gösterimde duruyor mu. Bu sorular
bir **dağılıma** bakar ve dağılım tek bir konuma sığmaz.

Bir önceki kursta bu dağılım sayı olarak kurulmuştu: beş sayı özeti hesaplanmış ve aynı beş sayıyı
veren ikinci bir küme üretilmişti. O hesap tekrarlanmaz, girdi olarak alınır. Bu dersin sorusu bir
adım sonrasıdır: aynı dağılım üç ayrı gösterimle kodlandığında okuyucu **hangi biçim özelliğini**
geri okuyabiliyor.

- **GT8. Dağılım gösterimi de bir kanaldır.** Aralık sayımı sayımları **uzunluk** kanalına, kutu
  özeti ve yoğunluk gösterimi değerleri **konum** kanalına koyar. Önceki dersin çözünürlükleri
  aynen sürer: uzunluk 40, konum 100 basamak.
- **GT9. Geri okunan şey bir biçim özelliğidir.** Üç özellik sayılır: **tepe sayısı**, **çarpıklık
  yönü** ve **uç değerin görünüp görünmediği**.
- **GT10. Tepe kuralı yazılır.** Bir nokta, iki yanındaki kendisinden farklı ilk değerlerin
  ikisinden de büyükse ve en yüksek değerin 0,3 katından aşağı değilse tepedir. Kural üç gösterimde
  de aynıdır ve **geri okunan** değerlere uygulanır.
- **GT11. Aralık genişliği ve çekirdek genişliği birer karardır.** İkisi de kaynağı değil, geri
  okunan biçimi değiştirir.
- **GT12. Uç değerin görünürlüğü uç değere bağlı değildir.** Çubuklar en yüksek sayıma göre
  ölçeklenir; aynı uç değer, komşusu daha yüksek olan bir gösterimde kaybolur.
- **GT13. Kutu özetinin tepe taşımaması kusur değil kapsamdır.** Beş sayı sıralamanın beş noktasını
  bildirir; o noktalar arasındaki diziliş kutuya hiç girmez.

#### Kaynaktaki Biçim

```python
# gosterim.py — MODELDIR: K03-K05'in kurgu olcum agi ayni tohumla uretilir.
# Kuzeyin 921 okumasi ile K05'in ikiz kumesi girdidir; ikisi de burada
# yeniden hesaplanmaz, gosterim kanalindan gecirilir.
import math

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


def uretec(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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


_k, _e = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (_e if r() < 0.046 else _k).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {a["abone_no"] for a in _k}

OKUMA = []
for a in _k + _e:
    r = uretec(TOHUM + 7000 + a["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            a["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        if a["abone_no"] not in GECERLI:
            r()
            continue
        OKUMA.append({"abone_no": a["abone_no"], "bolge": a["bolge"],
                      "donem": DONEM[d], "tuketim_m3": t})
        r()

kuzey = sorted(o["tuketim_m3"] for o in OKUMA if o["bolge"] == "kuzey")


def itele(dilim, uc, p=4):
    a, z = dilim[0], dilim[-1]
    u = [(x - a) / (z - a) for x in dilim]
    return [z - (z - a) * (1 - t) ** p if uc == "ust" else a + (z - a) * t ** p
            for t in u]


C = [0, 230, 460, 690, len(kuzey) - 1]
ikiz = (itele(kuzey[C[0]:C[1] + 1], "ust")[:-1] + itele(kuzey[C[1]:C[2] + 1], "alt")[:-1]
        + itele(kuzey[C[2]:C[3] + 1], "ust")[:-1] + itele(kuzey[C[3]:], "alt"))

# Kanallar: histogram cubugu uzunluk, kutu ve yogunluk ortak eksende konum.
COZ = {"uzunluk": 40, "konum": 100}


def izgara(t, n):
    return round(t * n) / n


def coz_uzunluk(v, ust):
    return izgara(v / ust, COZ["uzunluk"]) * ust if ust > 0 else 0.0


def coz_konum(v, alt, ust):
    return alt + (ust - alt) * izgara((v - alt) / (ust - alt), COZ["konum"])


def yuzdelik(v, p):
    s = sorted(v)
    i = p * (len(s) - 1)
    a = int(i)
    b = min(a + 1, len(s) - 1)
    return s[a] + (s[b] - s[a]) * (i - a)


# Kaynaktaki bicim: tepe sayisi kurgunun kendisinden bilinir (kuzey tek tabandan
# uretildi, ikiz iki uca itildi). Carpiklik yonu ortalama-ortanca isaretidir;
# esik 0,05 m3, cunku rapora iki ondalikla yazilan sayida bundan kucuk fark
# yaziya gecmez. Uc deger: en buyuk gozlemin dustugu bolge.
ESIK_CARP = 0.05
KUME = [("kuzey", kuzey, 1), ("ikiz", ikiz, 2)]


def yon(d):
    return "saga" if d > ESIK_CARP else ("sola" if d < -ESIK_CARP else "simetrik")


KAYNAK = {}
for ad, v, tepe in KUME:
    ort, med = sum(v) / len(v), yuzdelik(v, 0.50)
    KAYNAK[ad] = (tepe, yon(ort - med), max(v))
    print(f"{ad:6s} n={len(v)} ortalama {ort:.3f} ortanca {med:.3f} "
          f"en buyuk {max(v):.2f} m3")
    print(f"       kaynaktaki bicim: {tepe} tepe, {yon(ort - med)} carpik, "
          f"uc deger {max(v):.2f} m3")
```

```
kuzey  n=921 ortalama 20.210 ortanca 19.660 en buyuk 45.47 m3
       kaynaktaki bicim: 1 tepe, saga carpik, uc deger 45.47 m3
ikiz   n=921 ortalama 19.754 ortanca 19.660 en buyuk 45.47 m3
       kaynaktaki bicim: 2 tepe, saga carpik, uc deger 45.47 m3
```

Kaynaktaki tepe sayısı tahmin edilmez, **bilinir**: iki küme de kurgudur ve nasıl kuruldukları
koddadır. Kuzeyin 921 okuması tek bir taban değerden üretildi, ikiz küme kuzeyin sıralı
değerlerinin çeyrek dilimler içinde iki uca itilmesiyle elde edildi — birinde bir, öbüründe iki
toplanma merkezi vardır.

Çarpıklık yönü ortalama ile ortancanın işaretinden okunur; eşik 0,05 m³ yazıldı, çünkü iki
ondalıkla raporlanan bir sayıda bundan küçük fark yazıya geçmez. Kuzeyde fark 0,550 m³, ikizde
0,094 m³'tür ve ikincisi eşiğin hemen üstündedir.

#### Aynı Dağılım Üç Gösterimden Geçiyor

```python
# uc_gosterim.py — ayni dagilim uc gosterimle kodlanir ve uc bicim ozelligi
# geri okunmaya calisilir. Tepe kurali her gosterimde ayni: iki yanindaki
# farkli degerlerden buyuk ve en yuksegin 0,3 katindan asagi olmayan nokta.
GEN, EKSEN, N = 4.0, 48.0, 12
H, M = 2.0, 60


def sayim(v, gen, n):
    c = [0] * n
    for x in v:
        c[min(int(x / gen), n - 1)] += 1
    return c


def tepeler(y, oran=0.3):
    m, t = max(y), []
    for i in range(len(y)):
        if i and y[i] == y[i - 1]:
            continue
        s = next((y[j] for j in range(i - 1, -1, -1) if y[j] != y[i]), None)
        z = next((y[j] for j in range(i + 1, len(y)) if y[j] != y[i]), None)
        if s is not None and z is not None and y[i] > s and y[i] > z and y[i] >= oran * m:
            t.append(i)
    return t


def ozet(x, w):
    t = sum(w)
    ort, k = sum(a * b for a, b in zip(x, w)) / t, 0.0
    for a, b in zip(x, w):
        k += b
        if k >= t / 2:
            return ort, a
    return ort, x[-1]


def gost_aralik(v, gen=GEN):
    n = round(EKSEN / gen)
    c = sayim(v, gen, n)
    g = [round(coz_uzunluk(x, max(c))) for x in c]
    ort, med = ozet([(i + 0.5) * gen for i in range(n)], g)
    return len(tepeler(g)), yon(ort - med), g[min(int(max(v) / gen), n - 1)] > 0, g


def gost_kutu(v):
    b = [min(v), yuzdelik(v, 0.25), yuzdelik(v, 0.50), yuzdelik(v, 0.75), max(v)]
    g = [coz_konum(x, 0.0, EKSEN) for x in b]
    ac = g[3] - g[1]
    a_cit, u_cit = g[1] - 1.5 * ac, g[3] + 1.5 * ac
    disi = [x for x in v if x < a_cit or x > u_cit]
    nokta = len({coz_konum(x, 0.0, EKSEN) for x in disi})
    return None, yon((g[3] - g[2]) - (g[2] - g[1])), nokta > 0, (len(disi), nokta)


def gost_yogunluk(v, h=H):
    x = [EKSEN * i / (M - 1) for i in range(M)]
    y = [sum(math.exp(-0.5 * ((a - t) / h) ** 2) for t in v) for a in x]
    g = [coz_konum(t, 0.0, max(y)) for t in y]
    ort, med = ozet(x, g)
    return (len(tepeler(g)), yon(ort - med),
            g[round((M - 1) * max(v) / EKSEN)] > 0, g)


GOSTERIM = [("aralik sayimi", gost_aralik), ("kutu ozeti", gost_kutu),
            ("yogunluk", gost_yogunluk)]
print(f"aralik genisligi {GEN:.0f} m3, cekirdek genisligi {H:.0f} m3, "
      f"eksen 0-{EKSEN:.0f} m3, {M} ornekleme noktasi")
_c = sayim(ikiz, GEN, N)
_g = [round(coz_uzunluk(x, max(_c))) for x in _c]
print("\nikiz kumesi, sayimlar uzunluk kanalindan gecince (kaynak / geri okunan):")
for i in range(N):
    print(f"{i * GEN:5.0f}-{(i + 1) * GEN:<3.0f}{_c[i]:5d}{_g[i]:6d} "
          f"|{'#' * round(40 * _g[i] / max(_g))}")
_kayip = [(i, _c[i]) for i in range(N) if _c[i] > 0 and _g[i] == 0]
print(f"geri okunanda sifira dusen aralik: {len(_kayip)}, "
      f"icindeki okuma: {sum(x for _, x in _kayip)}\n")
print(f"{'kume':7s}{'gosterim':16s}{'tepe':>12s}{'carpiklik':>12s}{'uc deger':>11s}"
      f"{'gecen ozellik':>15s}")
for ad, v, _ in KUME:
    kt, kc, _ku = KAYNAK[ad]
    for g_ad, f in GOSTERIM:
        t, c, u, _ek = f(v)
        gecen = sum([t == kt, c == kc, u])
        print(f"{ad:7s}{g_ad:16s}{str(t) if t is not None else 'okunamaz':>12s}"
              f"{c:>12s}{'evet' if u else 'hayir':>11s}{gecen:>12d}/3")
```

```
aralik genisligi 4 m3, cekirdek genisligi 2 m3, eksen 0-48 m3, 60 ornekleme noktasi

ikiz kumesi, sayimlar uzunluk kanalindan gecince (kaynak / geri okunan):
    0-4     32    33 |####
    4-8      0     0 |
    8-12     0     0 |
   12-16   318   321 |#######################################
   16-20   114   115 |##############
   20-24   110   107 |#############
   24-28   329   329 |########################################
   28-32    11     8 |#
   32-36     4     0 |
   36-40     0     0 |
   40-44     2     0 |
   44-48     1     0 |
geri okunanda sifira dusen aralik: 3, icindeki okuma: 7

kume   gosterim                tepe   carpiklik   uc deger  gecen ozellik
kuzey  aralik sayimi              1        saga       evet           3/3
kuzey  kutu ozeti          okunamaz        saga       evet           2/3
kuzey  yogunluk                   1        saga       evet           3/3
ikiz   aralik sayimi              2        saga      hayir           2/3
ikiz   kutu ozeti          okunamaz        saga       evet           2/3
ikiz   yogunluk                   2        sola      hayir           1/3
```

Çubuk tablosu kanalın ne yaptığını doğrudan gösteriyor. Kırk basamaklık bir çubuk en yüksek sayıma
göre ölçeklenir; ikizde en yüksek sayım 329'dur, yani bir basamak 8,2 okumaya karşılık gelir. Beş
okumadan az olan her aralık yarım basamağın altında kalır ve sıfır olarak geri okunur. Üç aralık
böyle silinir ve içlerindeki **7 okuma** gösterimden tamamen çıkar; aralarında 45,47 m³'lük uç
değer de vardır.

Aynı uç değer kuzeyde geri okunuyor ve tek sebebi GT12'dir: kuzeyde en yüksek sayım 234, bir
basamak 5,9 okumadır ve 44–48 m³ aralığındaki üç okuma bir basamağı doldurmaya yeter. Uç değer iki
kümede de aynı yerde ve aynı sayıdadır; görünmesini belirleyen şey **en yüksek çubuğun
yüksekliğidir**.

Özellik tablosunda hiçbir gösterim üçünü birden geçirmiyor ve her biri ötekilerin kaçırdığı bir
şeyi tutuyor. **Kutu özeti** tepe sayısını hiç taşımıyor — sütunda "okunamaz" yazıyor, çünkü beş sayıdan tepe
türetilecek bir yol yoktur. Buna karşılık uç değeri iki kümede de geri okutan tek gösterim odur:
çit dışındaki gözlemler ayrı nokta olarak basılır, ölçeklenmez ve komşularına bakmaz. **Aralık
sayımı ile yoğunluk** ise ikizin iki tepesini geri okuyan tek gösterimlerdir ve ikisi de uç değeri
kaybeder, çünkü ikisi de en yüksek değere göre ölçeklenir.

Yoğunluk gösteriminin ikizde çarpıklığı **ters** okuması bir gösterim kusuru değil, eşiğe yakın
duran bir kaynağın sonucudur: 0,094 m³'lük bir asimetri altmış örnekleme noktasına ve yüz basamaklı
bir konum ızgarasına indirgendiğinde yönünü koruyamaz. Karar eşiğine bu kadar yakın bir biçim
özelliği için sonucu gösterim seçimi belirler ve o özellik raporlanmaz.

#### Aralık Genişliği Bir Karardır

```python
# karar.py — aralik genisligi ve cekirdek genisligi birer karardir; ikisi de
# geri okunan tepe sayisini oynatir. Kaynaktaki tepe sayisi degismez.
print(f"{'genislik':>9s}{'aralik':>8s}" + "".join(f"{ad:>9s}" for ad, _, _ in KUME))
for gen in (2.0, 3.0, 4.0, 6.0, 8.0, 12.0):
    print(f"{gen:8.0f} m3{round(EKSEN / gen):8d}"
          + "".join(f"{gost_aralik(v, gen)[0]:9d}" for _, v, _ in KUME))
print(f"{'cekirdek':>9s}{'ornek':>8s}" + "".join(f"{ad:>9s}" for ad, _, _ in KUME))
for h in (0.5, 1.0, 2.0, 3.0, 5.0, 8.0):
    print(f"{h:8.1f} m3{M:8d}"
          + "".join(f"{gost_yogunluk(v, h)[0]:9d}" for _, v, _ in KUME))
print("kaynaktaki tepe sayisi: " + ", ".join(f"{ad} {t}" for ad, _, t in KUME))
```

```
 genislik  aralik    kuzey     ikiz
       2 m3      24        1        2
       3 m3      16        1        2
       4 m3      12        1        2
       6 m3       8        1        2
       8 m3       6        1        2
      12 m3       4        1        1
 cekirdek   ornek    kuzey     ikiz
     0.5 m3      60        4        2
     1.0 m3      60        1        2
     2.0 m3      60        1        2
     3.0 m3      60        1        2
     5.0 m3      60        1        1
     8.0 m3      60        1        1
kaynaktaki tepe sayisi: kuzey 1, ikiz 2
```

Kaynaktaki tepe sayısı iki satırda da değişmiyor: kuzey bir, ikiz iki. Değişen tek şey geri okunan
sayı ve onu değiştiren tek şey bir karar.

Aralık genişliği 2 ile 8 m³ arasındayken ikizin iki tepesi geri okunuyor. 12 m³'te sayı bire
düşüyor, çünkü iki toplanma merkezi arasındaki mesafe yaklaşık 12 m³'tür ve tek bir aralık ikisini
birden yutar. Bir önceki kurs bu tuzağı bir cümleyle işaret etmişti; burada eşiğin yeri sayılıyor.

Çekirdek genişliği ters yönde de yanılıyor. 0,5 m³'te kuzeyde **dört** tepe geri okunuyor, oysa
kaynakta bir tane var. Bu genişlikte çekirdek örnekleme noktaları arasındaki 0,81 m³'lük aralıktan
dardır; eğri veriyi düzleştirmek yerine tek tek okumaları izler ve okuma yoğunluğundaki rastgele
oynamalar tepe olarak geri okunur. 5 m³ ve üstünde ise ikizin iki tepesi birleşir. İki karar
arasında bir tercih değil, iki ayrı hata vardır: dar seçim olmayan tepeyi üretir, geniş seçim var
olanı siler. Kural GT11'in somut halidir — iki genişlik de çıktının altında yazılır, yoksa geri
okunan tepe sayısı bir bulgu gibi görünür.

#### Özet

- Aralık sayımı sayımları uzunluk, kutu özeti ve yoğunluk ise değerleri konum kanalına koyar; geri
  okunan şey bir m³ değeri değil tepe sayısı, çarpıklık yönü ve uç değerdir.
- Hiçbir gösterim üç özelliği birden geçirmiyor: kutu özeti tepeyi hiç taşımıyor ama uç değeri iki
  kümede de geri okutan tek gösterim; aralık sayımı ile yoğunluk iki tepeyi geri okuyor ama uç
  değeri kaybediyor.
- Uzunluk kanalından geçen sayımlarda üç aralık sıfıra düşüyor ve 7 okuma siliniyor; aynı 45,47
  m³'lük uç değer kuzeyde görünüyor çünkü orada en yüksek çubuk 329 değil 234.
- Aralık genişliği 12 m³'te ikizin geri okunan tepe sayısını ikiden bire düşürüyor, çekirdek
  genişliği 0,5 m³'te kuzeyde olmayan dört tepe gösteriyor; kaynaktaki tepe sayısı değişmiyor.
- İkizin 0,094 m³'lük çarpıklığı eşiğin hemen üstündedir ve yönü gösterime göre değişir; bu kadar
  eşiğe yakın bir biçim özelliği raporlanmaz.

#### Sonraki Adım

Bu derste eksenin bir yanında sürekli bir değer vardı ve gösterim onu aralıklara ya da bir eğriye
çevirdi. Rapordaki soruların çoğu ise **kategoriktir**: beş bölgenin dört tarife basamağındaki
abone sayısı ve bu sayıların bölge içindeki payı. Kategorik gösterimin kendine özgü bir kararı
vardır — dilimler üst üste mi yığılacak yoksa yan yana mı dizilecek — ve bu bir kanal sorunudur:
yığılmış gösterimde yalnız alttaki dilimin taban çizgisi olur. Sonraki ders üstteki dilimlerin
geri okuma hatasını dilim sırasına göre sayar ve gruplu gösterimle karşılaştırır.

### Kategorik Görselleştirme

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/kategorik-gorsellestirme

Beş bölge ve dört tarife basamağından oluşan çapraz tablo iki gösterimle kodlanıyor ve fark taban çizgisinde çıkıyor: yığılmış gösterimde alttaki dilim konum kanalından okunuyor ve oransal hatası yüzde 2,7, üstteki dilimler uzunluk kanalından okunuyor ve hata üçüncü dilimde yüzde 22,0, dördüncüde yüzde 47,2 oluyor. Kuzeyin 8 okumalık dördüncü dilimi ile batının 11 okumalık üçüncü dilimi sıfır okunuyor, yani gösterimden tamamen çıkıyor. Ayrılması gereken 39 bölge çiftinden yığılmış gösterim 35'ini ayırıyor, gruplu gösterim 39'unu; hiçbir çift ters okunmuyor, kaybolan şey sıra değil ayrım. Aynı yığılmış gösterim toplamı tek okumayla veriyor: 921 gözlem 920 olarak geri geliyor.

Bir önceki ders sürekli bir değerin dağılımını üç gösterimle kodladı. Rapordaki soruların bir
bölümü ise sürekli değil **kategoriktir**: beş bölgenin dört tarife basamağındaki okuma sayısı,
bu sayıların bölge içindeki payı, bir basamağın bölgeler arasındaki sırası. Kategorik veri bir
çapraz tabloda durur ve o tablo iki farklı biçimde gösterilebilir — dilimler ya üst üste yığılır ya
da yan yana dizilir.

Bu iki biçim arasındaki fark bir üslup tercihi değil, bir kanal farkıdır. Yığılmış gösterimde
yalnız en alttaki dilim eksenin sıfırından başlar; öbürleri eksenin üzerinde yüzer ve okuyucunun
elinde onları ölçecek bir başlangıç noktası yoktur. Dersin sayısı bu farkın gözlem cinsinden
karşılığıdır.

- **GT14. Yığılmış gösterimde tek bir taban çizgisi vardır.** Alttaki dilim eksenin sıfırından
  başlar ve **konum** kanalından okunur; üstteki dilimlerin alt kenarı eksende değil, bir önceki
  dilimin bittiği yerdedir.
- **GT15. Yüzen dilim uzunluk kanalından okunur.** Okuyucu iki konumu okuyup çıkarmaz, dilimin
  uzunluğuna bakar. Birinci dersin çözünürlükleri sürer: konum 100, uzunluk 40 basamak.
- **GT16. Eksen açıklığı gösterimden gelir.** Yığılmış eksen **toplamı** taşımak zorundadır,
  gruplu eksen yalnız **en büyük dilimi**. Aynı basamak sayısı iki gösterimde farklı büyüklükte
  bir basamak demektir.
- **GT17. Eksen basamağının yarısından küçük dilim sıfır okunur.** Kaynakta duran bir dilim
  gösterimden tamamen çıkar ve okuyucunun eline "yok" olarak geçer.
- **GT18. Kategorik sorunun karşılaştırması kategoriler arasıdır.** Ölçü, aynı basamakta
  **ayrılması gereken bölge çiftlerinden kaçının ayrıldığıdır**.
- **GT19. Yığılmış gösterim toplamı tek okumayla verir.** Yığının üst kenarı eksenin sıfırından
  ölçülür; o kenarın taşıdığı sayı toplamdır, parçalar değil.

#### Çapraz Tablo ve İki Eksen

```python
# kategorik.py — MODELDIR: K03-K05'in kurgu olcum agi ayni tohumla uretilir.
# Bes bolge ve dort tarife basamagindan olusan capraz tablo girdidir; bu
# tablo iki ayri gosterimle kodlanip geri okunacak.
import math

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


def uretec(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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


_k, _e = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (_e if r() < 0.046 else _k).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {a["abone_no"] for a in _k}

OKUMA = []
for a in _k + _e:
    r = uretec(TOHUM + 7000 + a["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            a["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        if a["abone_no"] not in GECERLI:
            r()
            continue
        OKUMA.append({"abone_no": a["abone_no"], "bolge": a["bolge"],
                      "donem": DONEM[d], "tuketim_m3": t})
        r()

TARIFE = [10, 25, 40]
AD = [b[0] for b in BOLGE]


def basamak(x):
    return (1 if x < TARIFE[0] else 2 if x < TARIFE[1]
            else 3 if x < TARIFE[2] else 4)


K = len(TARIFE) + 1
CAPRAZ = {a: [0] * K for a in AD}
for o in OKUMA:
    CAPRAZ[o["bolge"]][basamak(o["tuketim_m3"]) - 1] += 1
TOPLAM = {a: sum(CAPRAZ[a]) for a in AD}

# Eksen acikligi gosterime gore degisir: yigilmis eksen toplami tasimak
# zorundadir, gruplu eksen yalnizca en buyuk dilimi. Ust sinir bir sonraki
# yuzluge yuvarlanir, cunku eksen etiketi yuvarlak sayilarda durur.
COZ_KONUM = 100


def yuvarla_yuz(x):
    return (int(x) // 100 + 1) * 100


EKSEN_Y = yuvarla_yuz(max(TOPLAM.values()))
EKSEN_G = yuvarla_yuz(max(max(CAPRAZ[a]) for a in AD))


def izgara(v, eksen, n):
    return eksen * round(n * v / eksen) / n


print(f"kurgu capraz tablo: {len(OKUMA)} okuma, tarife basamaklari "
      f"{TARIFE[0]}/{TARIFE[1]}/{TARIFE[2]} m3")
print(f"{'bolge':8s}" + "".join(f"{'basamak ' + str(i + 1):>12s}" for i in range(K))
      + f"{'toplam':>9s}")
for a in AD:
    print(f"{a:8s}" + "".join(f"{c:12d}" for c in CAPRAZ[a]) + f"{TOPLAM[a]:9d}")
print(f"yigilmis eksen 0-{EKSEN_Y}, bir basamak {EKSEN_Y / COZ_KONUM:.1f} gozlem; "
      f"gruplu eksen 0-{EKSEN_G}, bir basamak {EKSEN_G / COZ_KONUM:.1f} gozlem")
```

```
kurgu capraz tablo: 3199 okuma, tarife basamaklari 10/25/40 m3
bolge      basamak 1   basamak 2   basamak 3   basamak 4   toplam
kuzey             42         661         210           8      921
guney             59         538          48           0      645
dogu              21         281         225          30      557
bati              88         377          11           0      476
merkez            30         352         198          20      600
yigilmis eksen 0-1000, bir basamak 10.0 gozlem; gruplu eksen 0-700, bir basamak 7.0 gozlem
```

Çapraz tablo kaynağın kendisidir ve yirmi sayı taşır. Sayıların büyüklükleri çok farklıdır: ikinci
basamak 281 ile 661 arasında, dördüncü basamak 0 ile 30 arasında değişir. Kategorik gösterimin
zorluğu buradan başlar, çünkü tek bir eksen bu iki büyüklüğü aynı anda taşımak zorunda kalır.

İki gösterimin eksen açıklığı farklıdır ve fark bir tercih değil bir zorunluluktur. Yığılmış
gösterimde bir bölgenin çubuğu bütün dilimlerin toplamı kadar uzar, dolayısıyla eksen en büyük
toplamı, 921'i taşımalıdır ve bir sonraki yuvarlak sayı olan 1000'de biter. Gruplu gösterimde
çubuklar yan yana durur ve eksen yalnız en büyük dilimi, 661'i taşır; üst sınır 700'dür. Yüz
basamaklı aynı konum ızgarası birinde 10 gözlemlik, öbüründe 7 gözlemlik bir adım demektir.

#### Yığılmış: Taban Çizgisi Olmayan Dilim

```python
# yigilmis.py — dilimler ust uste konur. Alttaki dilim eksenin sifirindan
# baslar ve konum kanalindan okunur; ustteki dilimler eksenin uzerinde yuzer,
# okuyucu iki konumu cikarmak yerine dilimin uzunluguna bakar ve uzunluk
# kanalinin 40 basamagi ile okur (birinci ders).
GEN, COZ_UZUNLUK = 40, 40


def yigilmis_oku(c):
    return [izgara(c[0], EKSEN_Y, COZ_KONUM)] + [
        izgara(x, EKSEN_Y, COZ_UZUNLUK) for x in c[1:]]


def gruplu_oku(c):
    return [izgara(x, EKSEN_G, COZ_KONUM) for x in c]


def cizgi(c):
    s, t = "", 0.0
    for i, x in enumerate(c):
        t += x
        s += str(i + 1) * (round(GEN * t / EKSEN_Y) - len(s))
    return s


print(f"yigilmis gosterim, {GEN} basamakli eksen (okuyucunun gordugu):")
for a in AD:
    print(f"{a:8s}|{cizgi(CAPRAZ[a]):<40s}| toplam {TOPLAM[a]}")

print(f"\n{'bolge':8s}" + "".join(f"{'dilim ' + str(i + 1):>14s}" for i in range(K)))
mut = [[] for _ in range(K)]
ora = [[] for _ in range(K)]
kayip = 0
for a in AD:
    g = yigilmis_oku(CAPRAZ[a])
    for i in range(K):
        mut[i].append(abs(g[i] - CAPRAZ[a][i]))
        if CAPRAZ[a][i] > 0:
            ora[i].append(100 * abs(g[i] - CAPRAZ[a][i]) / CAPRAZ[a][i])
            kayip += g[i] == 0
    print(f"{a:8s}" + "".join(f"{c:>7d}/{x:<6.0f}" for c, x in zip(CAPRAZ[a], g)))
print(f"\n{'dilim':6s}{'kanal':>10s}{'|hata|':>9s}{'oransal':>10s}")
for i in range(K):
    print(f"{i + 1:<6d}{'konum' if i == 0 else 'uzunluk':>10s}"
          f"{sum(mut[i]) / len(AD):9.1f}{sum(ora[i]) / len(ora[i]):9.1f}%")
print(f"kaynakta sifirdan buyukken sifir okunan dilim: {kayip}")
_t = izgara(max(TOPLAM.values()), EKSEN_Y, COZ_KONUM)
print(f"ayni gosterimde toplam tek okumayla geliyor: kuzey {TOPLAM['kuzey']} "
      f"-> {_t:.0f}")
```

```
yigilmis gosterim, 40 basamakli eksen (okuyucunun gordugu):
kuzey   |1122222222222222222222222222333333333   | toplam 921
guney   |11222222222222222222222233              | toplam 645
dogu    |1222222222223333333334                  | toplam 557
bati    |1111222222222222222                     | toplam 476
merkez  |122222222222222333333334                | toplam 600

bolge          dilim 1       dilim 2       dilim 3       dilim 4
kuzey        42/40        661/650       210/200         8/0     
guney        59/60        538/550        48/50          0/0     
dogu         21/20        281/275       225/225        30/25    
bati         88/90        377/375        11/0           0/0     
merkez       30/30        352/350       198/200        20/25    

dilim      kanal   |hata|   oransal
1          konum      1.2      2.7%
2        uzunluk      6.6      1.4%
3        uzunluk      5.0     22.0%
4        uzunluk      3.6     47.2%
kaynakta sifirdan buyukken sifir okunan dilim: 2
ayni gosterimde toplam tek okumayla geliyor: kuzey 921 -> 920
```

Çubukların kendisi dersin en açık kanıtıdır. Kuzeyin dördüncü dilimi, yani 40 m³ üstünde okuma
yapan sekiz abone, kırk basamaklık çubukta hiç görünmez; batının üçüncü dilimi de öyle. Bunlar
gösterimin sildiği değil, gösterimin taşıyamadığı sayılardır: 1000 birimlik bir eksende 40
basamağa bölünmüş bir çubukta bir basamak 25 gözlem eder ve 11 gözlem yarım basamağın altındadır.

Hata tablosu dilim sırasına göre okunur. Alttaki dilim konum kanalından geçer ve oransal hatası
yüzde 2,7'de kalır. Üstteki dilimler uzunluk kanalından geçer ve oransal hata üçüncü dilimde yüzde
22,0'ye, dördüncüde yüzde 47,2'ye çıkar. İkinci dilimin yüzde 1,4'lük hatası bu düzeni bozuyor gibi
görünür ama bozmaz: ikinci dilim kümenin en büyük dilimidir ve mutlak hatası 6,6 gözlemle en
yüksek olanıdır — büyük olduğu için oransal hatası küçük kalır. **Hatayı belirleyen iki şey vardır:
dilimin taban çizgisi olup olmadığı ve dilimin eksene göre büyüklüğü.**

Aynı gösterimin kaybetmediği tek şey **toplamdır**. Yığının üst kenarı eksenin sıfırından ölçülür,
tek bir konum okumasıyla gelir ve kuzeyin 921 gözlemi 920 olarak geri okunur. Yığılmış gösterim bu
yüzden vardır: toplamı ve toplamın büyüklük sırasını tek bakışta verir. Parçaları vermeyi ise
yalnız en alttaki dilim için başarır.

Bir itiraz burada yerinde durur: dikkatli bir okuyucu dilimin iki kenarını da eksende okuyup
çıkarabilir ve daha iyi bir sayı elde eder. Doğrudur, ama o okuyucu artık grafiğe bakmıyor,
grafiğin üzerinde aritmetik yapıyordur. Bir sayıyı çıkarma işlemiyle elde etmek gerekiyorsa
gösterimin yerini bir tablo alır.

#### Gruplu: Her Dilime Bir Taban Çizgisi

```python
# gruplu.py — ayni sayilar yan yana dizilir: her dilim kendi taban cizgisinden
# baslar, hepsi konum kanalindan okunur, eksen yalniz en buyuk dilimi tasir.
# Soru bolgeler arasidir: ayni basamakta hangi bolge daha yuksek.
from itertools import combinations

print(f"{'basamak':8s}{'kaynak':>26s}{'yigilmis':>26s}{'gruplu':>26s}")
SERI = []
for i in range(K):
    s = ([CAPRAZ[a][i] for a in AD], [yigilmis_oku(CAPRAZ[a])[i] for a in AD],
         [gruplu_oku(CAPRAZ[a])[i] for a in AD])
    SERI.append(s)
    print(f"{i + 1:<8d}" + "".join(f"{x:>26s}" for x in
          ["/".join(f"{v:.0f}" for v in t) for t in s]))

print(f"\n{'basamak':8s}{'ayrilmasi gereken cift':>24s}{'yigilmis':>10s}"
      f"{'gruplu':>8s}{'ters okunan':>13s}")
top = [0, 0, 0, 0]
for i, (k, y, g) in enumerate(SERI):
    c = [(p, q) for p, q in combinations(range(len(AD)), 2) if k[p] != k[q]]
    ay = sum(1 for p, q in c if y[p] != y[q])
    ag = sum(1 for p, q in c if g[p] != g[q])
    ters = sum(1 for p, q in c if (k[p] - k[q]) * (y[p] - y[q]) < 0)
    for j, v in enumerate((len(c), ay, ag, ters)):
        top[j] += v
    print(f"{i + 1:<8d}{len(c):>24d}{ay:>10d}{ag:>8d}{ters:>13d}")
print(f"{'toplam':8s}{top[0]:>24d}{top[1]:>10d}{top[2]:>8d}{top[3]:>13d}")
```

```
basamak                     kaynak                  yigilmis                    gruplu
1                   42/59/21/88/30            40/60/20/90/30            42/56/21/91/28
2              661/538/281/377/352       650/550/275/375/350       658/539/280/378/350
3                210/48/225/11/198          200/50/225/0/200         210/49/224/14/196
4                      8/0/30/0/20               0/0/25/0/25               7/0/28/0/21

basamak   ayrilmasi gereken cift  yigilmis  gruplu  ters okunan
1                             10        10      10            0
2                             10        10      10            0
3                             10         9      10            0
4                              9         6       9            0
toplam                        39        35      39            0
```

Karşılaştırma sorusu kategoriler arasındadır ve ölçü, kaynakta birbirinden farklı olan bölge
çiftlerinden kaçının gösterimden sonra da farklı okunduğudur. Dördüncü basamakta on çiftin dokuzu
kaynakta farklıdır — güney ile batı gerçekten de aynı, sıfır. Yığılmış gösterim bu dokuzun altısını
ayırıyor, gruplu gösterim dokuzunu birden.

Yığılmış gösterimin kaybettiği üç çift bellidir. Doğunun 30 gözlemi ile merkezin 20 gözlemi ikisi
de 25 okunuyor, kuzeyin 8 gözlemi ise sıfıra düşüp gerçekten sıfır olan iki bölgeyle aynı hale
geliyor. Üçüncü basamakta batının 11 gözlemi de sıfıra düşüyor ama kimseyle çakışmıyor; oradaki
tek kayıp kuzeyin 210'u ile merkezin 198'inin ikisinin de 200 okunmasıdır ve dokuz yerine on
çiftin dokuzu ayrılıyor. Toplamda ayrılması gereken 39 çiftten yığılmış gösterim 35'ini, gruplu
gösterim 39'unu ayırıyor.

Son sütun bu dersin ince bulgusudur: **ters okunan çift yok**. Yığılmış gösterim hiçbir yerde
küçük bölgeyi büyük göstermiyor; yaptığı tek şey farkları silmek. Bu, hatanın yönsüz olmasından
gelir — ızgaraya yuvarlama iki bölgeyi birbirine yaklaştırır, ama yer değiştirtmez. Rapora
yazılacak cümle buradan çıkar: yığılmış gösterimden okunan sıralama yanlış değildir, **eksiktir**.
Sıralamanın kaç basamağının okunabildiği ayrı bir sayıdır ve o sayı 39'un 35'idir.

Gruplu gösterimin bedeli geri okumada değil, yerdedir: dört dilim beş bölgede yirmi çubuk demektir
ve bir dilim daha eklendiğinde yirmi beş olur. Yığılmış gösterim aynı sayıları beş çubuğa sığdırır.
İki gösterim arasındaki seçim, toplam mı yoksa parçalar mı sorulacağına bağlıdır ve bu soru
verinin değil raporun sorusudur.

#### Özet

- Yığılmış gösterimde yalnız alttaki dilimin taban çizgisi vardır; o dilim konum kanalından, yüzen
  dilimler uzunluk kanalından okunur ve eksen toplamı taşıdığı için bir basamak 25 gözleme çıkar.
- Oransal geri okuma hatası dilim sırasına göre yüzde 2,7 · 1,4 · 22,0 · 47,2 çıkar; hatayı
  belirleyen iki şey dilimin taban çizgisi olup olmadığı ve dilimin eksene göre büyüklüğüdür.
- Kuzeyin 8 okumalık dördüncü dilimi ile batının 11 okumalık üçüncü dilimi sıfır okunur, yani
  kaynakta duran iki dilim gösterimden tamamen çıkar.
- Ayrılması gereken 39 bölge çiftinden yığılmış gösterim 35'ini, gruplu gösterim 39'unu ayırır;
  ters okunan çift yoktur, kaybolan şey sıra değil ayrımdır.
- Yığılmış gösterim toplamı tek okumayla verir — kuzeyin 921 gözlemi 920 olarak geri okunur — ve
  kaybettiği şey yalnız parçalardır.

#### Sonraki Adım

Üç derste kategoriler sayıldı, dağılımlar aralıklara bölündü ve her seferinde eksenin bir yanında
bir etiket, öbür yanında bir sayı vardı. Rapordaki soruların bir kısmı ise iki sayıyı birden sorar:
bir bölgede tüketim arttıkça basamak da yükseliyor mu, iki dönemin okumaları birlikte mi
oynuyor. Birinci ders bu sorunun yalnız konum kanalıyla cevaplanabileceğini göstermişti, çünkü
kanalın ikinci bağımsız kopyası yalnız orada var. Sonraki ders o iki kopyayı kullanır ve nokta
sayısı arttıkça ne olduğunu sayar: kaç nokta üst üste biniyor, okunamayan bölge nereden başlıyor ve
aynı ilişki bir ısı haritasının renk basamağına konduğunda kaç ayrı düzey geri okunuyor.

### İlişki Görselleştirme

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/iliski-gorsellestirme

1.101 abone çifti saçılım olarak kodlandığında işaret genişliği 1.600 ayırt edilebilir hücre bırakıyor ve noktaların 792'si, yani yüzde 71,9'u üst üste binip geri okunamaz oluyor; en yoğun hücrede 16 nokta var. Aynı çiftler ısı haritasına konduğunda tek bir nokta kaybolmuyor ama sayım kayboluyor: kaynaktaki 28 ayrı sayım değeri yedi renk basamağının altısına iniyor, 51 dolu hücrenin 37'si tek bir basamağa düşüyor ve ortalama oransal geri okuma hatası yüzde 280,9 çıkıyor. Basamaklar eşit aralık yerine eşit sayıda hücreye bölündüğünde aynı hata yüzde 15,7'ye iniyor. Her x için tek bir y veren çizgi ise bulutun ortalama 44,1 basamaklık açıklığını tek basamağa indiriyor.

Birinci ders dört soru türünü kanallara bağlarken ilişki sorusunu ayırmıştı: bu soru iki değerin
**aynı anda** geri okunmasını ister ve bunun için kanalın ikinci bir bağımsız kopyası gerekir.
Yalnız konumun iki kopyası vardır, yatay ve düşey eksen. Bu ders o iki kopyayı kullanır.

Kullanılan çift, kurgu ağın doğal sorusudur: bir abonenin **ilk dönem okuması** onun **genel
düzeyini** ne kadar iyi bildiriyor. İlişkinin gücü bir önceki kursta katsayıyla ölçüldü ve burada
ne hesaplanır ne yorumlanır. Buradaki soru gösterime aittir: aynı ilişki üç biçimde kodlandığında
okuyucunun eline ne geçiyor ve nokta sayısı arttıkça ne kayboluyor.

- **GT20. İlişki sorusu iki konum kanalını aynı anda ister.** Birinci dersin sonucudur ve burada
  girdi olarak kullanılır.
- **GT21. Çizilen nokta bir işarettir ve genişliği vardır.** Tek bir noktanın değeri konum
  kanalından 100 basamakla okunur, ama **iki nokta** ancak işaret genişliğinden fazla ayrıldığında
  ayırt edilir. İşaret genişliği eksenin kırkta biri alındı.
- **GT22. Üst üste binen nokta geri okunmaz.** Okuyucu bir hücreyi bir kez görür; ölçü, kaç
  noktanın ayrı bir hücrede kaldığıdır.
- **GT23. Isı haritası nokta kaybetmez, sayım kaybeder.** Her nokta bir hücrede sayılır, ama sayım
  renk basamağına iner ve geri okunabilen düzey sayısı basamak sayısıyla sınırlıdır.
- **GT24. Renk basamağı sayımların dağılımına değil, konan sınıra göre bölünür.** Sayımlar çarpıksa
  eşit aralıklı basamaklarda alt basamaklar dolar, üst basamaklar boş kalır.
- **GT25. Bir x için tek bir y veren gösterim, o x'teki açıklığı taşımaz.** Çizginin geri okuttuğu
  sayı bir basamaktır; bulutun kapladığı basamak sayısı ayrı bir ölçüdür.

#### İki Konum Kanalı ve İşaret Genişliği

```python
# iliski.py — MODELDIR: K03-K05'in kurgu olcum agi ayni tohumla uretilir.
# Iliski cifti: abonenin ilk donem okumasi ile ayni abonenin ortalamasi.
import math

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


def uretec(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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


_k, _e = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (_e if r() < 0.046 else _k).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {a["abone_no"] for a in _k}

OKUMA = []
for a in _k + _e:
    r = uretec(TOHUM + 7000 + a["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            a["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        if a["abone_no"] not in GECERLI:
            r()
            continue
        OKUMA.append({"abone_no": a["abone_no"], "bolge": a["bolge"],
                      "donem": DONEM[d], "tuketim_m3": t})
        r()

from collections import defaultdict

# Ilki abonenin ilk donem okumasi, ikincisi ayni abonenin butun donemlerinin
# ortalamasi. En az iki okumasi olan aboneler alinir.
_d = defaultdict(list)
for o in OKUMA:
    _d[o["abone_no"]].append((o["donem"], o["tuketim_m3"]))
CIFT = []
for _no in sorted(_d):
    v = sorted(_d[_no])
    if len(v) >= 2:
        CIFT.append((v[0][1], sum(x for _, x in v) / len(v)))

# Iki nokta ancak isaret genisliginden fazla ayrildiginda ayirt edilir.
# Cizilen nokta bir isarettir ve genisligi vardir; isaret genisligi eksenin
# kirkta biri alindi. Tek bir noktanin degeri ise konum kanalindan, 100
# basamakla okunur.
COZ_KONUM, ISARET = 100, 40


def yuvarla_on(x):
    return (int(x) // 10 + 1) * 10


EKSEN_X = yuvarla_on(max(a for a, _ in CIFT))
EKSEN_Y = yuvarla_on(max(b for _, b in CIFT))


def hucre(p):
    return (min(ISARET - 1, int(ISARET * p[0] / EKSEN_X)),
            min(ISARET - 1, int(ISARET * p[1] / EKSEN_Y)))


# Alt kume secimi tohumdan gelir: cift dizisi bir kez karistirilir, sonra
# bastan alinir. Boylece her boyut ayni siranin onekidir.
_r = uretec(TOHUM + 404)
SIRA = list(range(len(CIFT)))
for i in range(len(SIRA) - 1, 0, -1):
    j = int(_r() * (i + 1))
    SIRA[i], SIRA[j] = SIRA[j], SIRA[i]
KARISIK = [CIFT[i] for i in SIRA]

print(f"kurgu iliski kumesi: {len(CIFT)} abone cifti "
      f"(ilk donem okumasi, abonenin ortalamasi)")
print(f"eksenler 0-{EKSEN_X} ve 0-{EKSEN_Y} m3; konum cozunurlugu {COZ_KONUM} "
      f"basamak, isaret genisligi eksenin 1/{ISARET}'i")
print(f"duzlem {ISARET}x{ISARET} = {ISARET * ISARET} ayirt edilebilir hucre")
```

```
kurgu iliski kumesi: 1101 abone cifti (ilk donem okumasi, abonenin ortalamasi)
eksenler 0-60 ve 0-50 m3; konum cozunurlugu 100 basamak, isaret genisligi eksenin 1/40'i
duzlem 40x40 = 1600 ayirt edilebilir hucre
```

İki çözünürlük sayısı yan yana duruyor ve ikisi aynı şey değil. Tek bir noktanın değeri konum
kanalından okunur ve eksende 100 basamak vardır; birinci dersin sayısıdır. Ama iki noktanın **ayrı
iki nokta olarak** görülmesi için aralarındaki uzaklığın işaretin kendi genişliğini aşması gerekir.
Çizilen nokta bir matematiksel nokta değildir; sayfada bir yer kaplar ve o yer eksenin kırkta biri
alındı. Düzlem bu yüzden 100 çarpı 100 değil, **40 çarpı 40** ayırt edilebilir hücreye bölünür ve
1.600 hücreye 1.101 nokta konacaktır.

#### Nokta Sayısı Arttıkça

```python
# sacilim.py — nokta sayisi arttikca kac nokta ayri bir hucrede kaliyor.
# Ust uste binen nokta geri okunmaz: okuyucu hucreyi bir kez gorur.
print(f"{'nokta':>7s}{'dolu hucre':>12s}{'gizlenen':>10s}{'gizlenen %':>12s}"
      f"{'en yogun hucre':>16s}")
for n in (100, 250, 500, 800, len(KARISIK)):
    s = defaultdict(int)
    for p in KARISIK[:n]:
        s[hucre(p)] += 1
    print(f"{n:7d}{len(s):12d}{n - len(s):10d}{100 * (n - len(s)) / n:11.1f}%"
          f"{max(s.values()):16d}")
```

```
  nokta  dolu hucre  gizlenen  gizlenen %  en yogun hucre
    100          85        15       15.0%               3
    250         161        89       35.6%               5
    500         232       268       53.6%               9
    800         266       534       66.8%              12
   1101         309       792       71.9%              16
```

Düzlemde 1.600 hücre var ve noktalar 1.101 tane. Boş yer bol görünüyor, ama 1.101 nokta yalnız
**309** hücreye düşüyor ve **792 nokta**, yani yüzde 71,9'u, üzerine başka bir nokta binmiş
durumda. Sebep, noktaların düzleme eşit dağılmamasıdır: ilişkinin olduğu yerde nokta yığılır ve
tam da en çok bilgi taşıyan bölge en okunamaz bölge olur. En yoğun hücrede 16 nokta üst üste
duruyor ve okuyucu için o hücre tek bir noktadan farksız.

Sütunun nasıl büyüdüğü ayrıca kaydedilmeye değer. Yüz noktada gizlenen oran yüzde 15,0; iki yüz
elli noktada yüzde 35,6; beş yüzde yüzde 53,6. Nokta sayısı on bir katına çıkarken dolu hücre
sayısı yalnız 85'ten 309'a, yani üç buçuk katına çıkıyor. Saçılımın taşıdığı bilgi nokta sayısıyla
değil, **dolu hücre sayısıyla** büyür ve bu sayının bir tavanı vardır: 1.600.

#### Isı Haritası ve Basamak Sınırı Kararı

```python
# isi_haritasi.py — ayni cift kumesi kaba araliklara bolunur, her hucrenin
# sayimi renk basamagina cevrilir ve geri okunur. Renk bir ad degil, bir
# basamak numarasidir; basamak sayisi birinci dersteki gibi 7.
NX, NY, BASAMAK = 12, 10, 7
GX, GY = EKSEN_X / NX, EKSEN_Y / NY
SAYIM = defaultdict(int)
for a, b in CIFT:
    SAYIM[(min(NX - 1, int(a / GX)), min(NY - 1, int(b / GY)))] += 1
ENC = max(SAYIM.values())


def kodla_renk(c):
    return min(BASAMAK - 1, int(BASAMAK * c / ENC))


def coz_renk(b):
    return (b + 0.5) * ENC / BASAMAK


print(f"{NX}x{NY} aralik ({GX:.0f}x{GY:.0f} m3), en yogun hucre {ENC} nokta, "
      f"{BASAMAK} renk basamagi")
for j in range(NY - 1, -1, -1):
    print(f"{(j + 1) * GY:5.0f} |" + "".join(
        str(kodla_renk(SAYIM[(i, j)])) if SAYIM[(i, j)] else "." for i in range(NX)))
print("      +" + "-" * NX + f"  x ekseni 0-{EKSEN_X} m3")

dolu = [c for c in SAYIM.values() if c > 0]
duzey_k = len(set(dolu))
duzey_g = len({kodla_renk(c) for c in dolu})
ora = sum(abs(coz_renk(kodla_renk(c)) - c) / c for c in dolu) / len(dolu)
print(f"dolu hucre {len(dolu)}, hicbir nokta kaybolmadi: "
      f"{sum(dolu)} = {len(CIFT)}")
print(f"kaynakta {duzey_k} ayri sayim degeri var, geri okunan duzey {duzey_g}"
      f"/{BASAMAK}; ortalama oransal geri okuma hatasi {100 * ora:.1f}%")
print("basamak dolulugu: " + ", ".join(
    f"{b}:{sum(1 for c in dolu if kodla_renk(c) == b)}" for b in range(BASAMAK)))

# Ikinci karar: basamaklar esit araliklara degil, esit sayida hucreye bolunur.
# Sinirlar sirali sayimlardan gelir, her basamagin temsil degeri kendi
# hucrelerinin ortancasidir.
_s = sorted(dolu)
SINIR = [_s[round(len(_s) * k / BASAMAK)] for k in range(1, BASAMAK)]


def kodla_sira(c):
    b = 0
    while b < BASAMAK - 1 and c >= SINIR[b]:
        b += 1
    return b


TEMSIL = []
for b in range(BASAMAK):
    g = sorted(c for c in dolu if kodla_sira(c) == b)
    TEMSIL.append(g[len(g) // 2] if g else 0)
ora2 = sum(abs(TEMSIL[kodla_sira(c)] - c) / c for c in dolu) / len(dolu)
print(f"esit sayida hucre karari: geri okunan duzey "
      f"{len({kodla_sira(c) for c in dolu})}/{BASAMAK}, "
      f"ortalama oransal hata {100 * ora2:.1f}%")
print("basamak dolulugu: " + ", ".join(
    f"{b}:{sum(1 for c in dolu if kodla_sira(c) == b)}" for b in range(BASAMAK)))
```

```
12x10 aralik (5x5 m3), en yogun hucre 140 nokta, 7 renk basamagi
   50 |............
   45 |........0...
   40 |...000.000..
   35 |...00000000.
   30 |..00121000..
   25 |..044310....
   20 |004641000...
   15 |0142000.....
   10 |00000.......
    5 |.0..........
      +------------  x ekseni 0-60 m3
dolu hucre 51, hicbir nokta kaybolmadi: 1101 = 1101
kaynakta 28 ayri sayim degeri var, geri okunan duzey 6/7; ortalama oransal geri okuma hatasi 280.9%
basamak dolulugu: 0:37, 1:5, 2:2, 3:1, 4:5, 5:0, 6:1
esit sayida hucre karari: geri okunan duzey 6/7, ortalama oransal hata 15.7%
basamak dolulugu: 0:0, 1:13, 2:7, 3:8, 4:8, 5:8, 6:7
```

Isı haritası saçılımın kaybettiği şeyi kaybetmiyor: 1.101 noktanın hepsi bir hücrede sayıldı ve
toplam birebir tutuyor. Kaybettiği başka bir şey — **sayımın kendisi**. Kaynakta 28 ayrı sayım
değeri var; renk basamağı bunları yediye, gerçekte de altıya indiriyor.

Basamak dolulukları GT24'ün doğrudan kanıtı. Eşit aralıklı bölme, en yoğun hücrenin 140 noktasını
ölçü alır ve 51 dolu hücrenin **37'sini** tek bir basamağa, en alttakine yığar. O basamağın orta
değeri 10 noktadır; içinde tek noktalı hücreler de vardır. Ortalama oransal geri okuma hatası bu
yüzden yüzde 280,9 çıkar ve okuyucu düzlemin çoğunu tek bir düzey olarak görür — düzlemdeki
sıfırların yanında sıralanan sıfırlar bunu gösteriyor.

İkinci karar aynı veriyi aynı yedi basamakla kodluyor, yalnız sınırları başka yere koyuyor:
basamaklar eşit aralığa değil, **eşit sayıda hücreye** bölünüyor. Doluluk dengeleniyor ve ortalama
oransal hata yüzde 15,7'ye, on sekizde birine iniyor. Bedeli vardır ve yazılmalıdır: eşit sayıda
hücreye bölünmüş bir basamak dizisi **büyüklüğü değil sırayı** taşır. Birinci kararla iki hücrenin
basamak farkı kaç nokta olduğunu bildirir, ikinci kararla yalnız hangisinin daha kalabalık olduğunu.
Aynı veri, aynı gösterim, iki savunulabilir karar ve on sekiz kat fark.

#### Çizginin Taşımadığı

```python
# cizgi.py — ucuncu gosterim: her x icin tek bir y veren bir cizgi. Cizginin
# katsayisi M26/K05'te hesaplandi ve burada ne hesaplanir ne yorumlanir;
# olculen tek sey, tek bir y'nin o x'te ne kadarini tasidigi. Aciklik konum
# kanalinin basamagi cinsinden yazilir (eksen 100 basamaga bolunmus).
print(f"{'x araligi':>12s}{'nokta':>7s}{'y en kucuk':>12s}{'y en buyuk':>12s}"
      f"{'aciklik basamak':>17s}")
bas = []
for i in range(NX):
    ys = [b for a, b in CIFT if min(NX - 1, int(a / GX)) == i]
    if len(ys) < 20:
        continue
    n = round(COZ_KONUM * (max(ys) - min(ys)) / EKSEN_Y)
    bas.append(n)
    print(f"{i * GX:6.0f}-{(i + 1) * GX:<5.0f}{len(ys):7d}{min(ys):12.2f}"
          f"{max(ys):12.2f}{n:17d}")
print(f"cizgi her x icin 1 basamak veriyor; bulut ortalama "
      f"{sum(bas) / len(bas):.1f} basamak kapliyor")
```

```
   x araligi  nokta  y en kucuk  y en buyuk  aciklik basamak
     0-5         36        5.82       19.04               26
     5-10        30        3.49       19.43               32
    10-15       205        5.46       26.61               42
    15-20       295        6.57       35.66               58
    20-25       235        7.84       38.13               61
    25-30       154       12.01       35.71               47
    30-35        84       13.91       33.52               39
    35-40        34       17.53       38.84               43
    40-45        20       18.87       43.48               49
cizgi her x icin 1 basamak veriyor; bulut ortalama 44.1 basamak kapliyor
```

Üçüncü gösterim en az yer kaplayanıdır ve en çok şeyi atar. Bir çizgi her x için tek bir y verir;
o değer konum kanalının tek bir basamağıdır. Aynı x aralığında gözlemler ortalama **44,1 basamak**
kaplıyor, yani çizginin geri okuttuğu sayı bulutun düşey açıklığının kırk dörtte biri kadar bir
yer tutuyor. 15–20 m³ aralığında 295 abone var ve ortalamaları 6,57 ile 35,66 m³ arasında
değişiyor; çizgi bu aralığa tek bir sayı koyar.

Bu, çizgiye karşı bir itiraz değildir. Çizgi zaten açıklığı atmak için vardır ve rapor bir eğilim
soruyorsa doğru gösterimdir. Kaydedilecek şey, atılan şeyin **sayılabilir** olmasıdır: çizginin
yanına açıklık yazılmadığında okuyucu, kırk dört basamaklık bir bulutu bir basamak sanır.

#### Özet

- İlişki iki konum kanalıyla kodlanır; tek noktanın değeri 100 basamakla okunur, ama iki noktanın
  ayrılması işaret genişliğine bağlıdır ve düzlem 40 çarpı 40 hücreye iner.
- 1.101 nokta yalnız 309 hücreye düşer: 792 nokta, yüzde 71,9'u, üst üste biner ve en yoğun hücrede
  16 nokta durur. Saçılımın taşıdığı bilgi nokta sayısıyla değil dolu hücre sayısıyla büyür.
- Isı haritası hiçbir noktayı kaybetmez ama sayımı kaybeder: kaynaktaki 28 ayrı sayım değeri yedi
  basamağın altısına iner, 51 dolu hücrenin 37'si tek basamağa düşer, oransal hata yüzde 280,9.
- Basamak sınırı eşit aralık yerine eşit sayıda hücreye konduğunda aynı hata yüzde 15,7'ye iner;
  bedeli, basamağın artık büyüklüğü değil sırayı taşımasıdır.
- Her x için tek bir y veren çizgi, ortalama 44,1 basamaklık bir açıklığı tek basamağa indirir;
  açıklık yazılmadığında okuyucu bulutu çizgi sanır.

#### Sonraki Adım

Dört derste dört kanal ölçüldü: konum, uzunluk, alan ve renk basamağı — hepsi tek bir **anlık
görüntü** üzerinde. Beş bölgenin ortalaması bir andaydı, kuzeyin dağılımı bir dönemin toplamıydı,
çapraz tablo bir kesitti, saçılımın iki ekseni de aynı zamana aitti. Eksenlerden biri **zaman**
olduğunda kanalın taşıdığı şey değişir: okuyucu artık bir değer değil bir **değişim** okur ve
değişimin kendisi ölçülmemiş bir sayıdır. Sonraki ders o eksene geçer ve eğimin geri okunmasını
ölçer — aynı dizi iki eksen ölçeğiyle kodlandığında geri okunan eğim ne oluyor, mevsimsel bileşen
ayrılmadan eğilim okunabiliyor mu.

### Zaman Serisi Görselleştirme

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/zaman-serisi-gorsellestirme

Aynı 36 dönemlik dizi aynı yükseklikte iki eksen ölçeğiyle kodlanıyor: sıfırdan başlayan eksende okuma çözünürlüğü 2,273 m³ ve nokta başına ortalama geri okuma hatası 0,540 m³, kırpılmış eksende 0,909 m³ ve 0,246 m³. Eğim 0,05 m³/dönem duyarlıkla sıfırdan başlayan eksende ancak 24 dönemlik, kırpılmış eksende 16 dönemlik pencereden itibaren geri okunuyor. Mevsimsel bileşenin kendi eğimi vardır ve pencere başlangıcına göre işaret değiştirir: aynı 24 dönemlik uzunluk üç ayrı yerde -0,2239, -0,0487 ve -0,2260 m³/dönem veriyor, kurguya konan eğilim ise -0,140.

Önceki dört ders bir sayıyı dört kanalla kodladı ve her kanalın geri okuttuğu değeri ölçtü.
Dördünde de ortak bir varsayım vardı: gösterilen şey tek bir anlık görüntüydü. Eksenin kendisi bir
**zaman** olduğunda kanalın taşıdığı şey değişir; okuyucu artık bir değeri değil değerin
**değişimini** geri okur ve okuduğu şey uzunluk değil **eğim**dir.

Bu dersin ölçüsü eğimin geri okunmasıdır ve iki soru sorulur. Aynı dizi aynı yükseklikte iki farklı
eksen aralığıyla kodlandığında geri okunan eğim ne oluyor ve mevsimsel bileşen ayrılmadan eğilim
geri okunabiliyor mu. Yeniden örnekleme kararı Python ile Veri Analizi kursunun veri çerçeveleri
konusunda ölçüldü ve tekrarlanmaz; burada dizi verili kabul edilir, yalnız gösterimi ölçülür.

- **GT26. Zaman ekseninde okunan şey eğimdir.** İki nokta arasındaki konum farkı bölü dönem farkı
  eğimi verir; uzunluk kanalının okuttuğu oran burada okunmaz.
- **GT27. Okuma çözünürlüğü bir satırdır.** Gösterimin dikey uzamı sonlu sayıda ayrılabilir konuma
  bölünür ve bir satırdan ince konum ayırt edilemez. Çözünürlük eksen aralığının satır sayısına
  bölümüdür ve her koşumda sayı olarak basılır.
- **GT28. Eğimin geri okuma hatası pencere uzunluğuyla küçülür.** İki uçtan okunan eğimin hatası en
  çok çözünürlük bölü pencere uzunluğudur; kısa pencerede çözünürlük eğimi yutar.
- **GT29. Mevsimsel bileşenin kendi eğimi vardır.** Bileşen bütün dizide sıfır ortalamalıdır, ama
  sonlu bir pencerede en küçük kareler eğimi sıfır değildir ve pencerenin nereden başladığına göre
  işaret değiştirir. Okuyucu tek bir eğim okur ve o eğim iki bileşenin toplamıdır.
- **GT30. Dizi kurgudur.** Ağın ölçülen üç dönemi 36 döneme uzatılamaz; dizi kuzey bölgesinin
  ortalaması taban alınarak aynı tohumla türetildi ve içine konan eğilim ile mevsim genliği koda
  yazılıdır.

#### Eksen Bir Zaman Olduğunda

```python
# zaman.py — MODELDIR: K03-K05'in kurgu olcum agi ayni tohumla yeniden uretilir
# (3.199 satir, 1.329 abone, bes bolge). 36 donemlik dizi de KURGUDUR: agin
# olculen uc donemi 36'ya uzatilamaz, dizi kuzey ortalamasi taban alinarak
# ayni tohumla turetildi. Icine konan egilim ve mevsim genligi sabittir.
import math

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


def uretec(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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


_k, _e = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (_e if r() < 0.046 else _k).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {a["abone_no"] for a in _k}

OKUMA = []
for a in _k + _e:
    r = uretec(TOHUM + 7000 + a["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            a["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        if a["abone_no"] not in GECERLI:
            r()
            continue
        OKUMA.append({"abone_no": a["abone_no"], "bolge": a["bolge"],
                      "donem": DONEM[d], "tuketim_m3": t})
        r()

kuzey = [o["tuketim_m3"] for o in OKUMA if o["bolge"] == "kuzey"]
BASLANGIC = sum(kuzey) / len(kuzey)
N, EGILIM, GENLIK, MEVSIM = 36, -0.140, 2.20, 12
_g = uretec(TOHUM + 505)
MEVSIMSEL = [GENLIK * math.sin(2 * math.pi * t / MEVSIM) for t in range(N)]
SERI = [round(BASLANGIC + EGILIM * t + MEVSIMSEL[t]
              + (_g() + _g() + _g() - 1.5) * 0.60, 3) for t in range(N)]


def egim(v):
    """En kucuk kareler egimi, birim: m3 / donem."""
    n = len(v)
    ot, ov = (n - 1) / 2, sum(v) / n
    return (sum((i - ot) * (x - ov) for i, x in enumerate(v))
            / sum((i - ot) ** 2 for i in range(n)))


def kodla(v, alt, ust, satir):
    """Kodlayici: deger -> konum. Izgarada tam satir numarasi verir."""
    return max(0, min(satir - 1, round((v - alt) / (ust - alt) * (satir - 1))))


def coz(k, alt, ust, satir):
    """Cozucu: konum -> deger. Okuyucu satirin kendi degerini okur."""
    return alt + k / (satir - 1) * (ust - alt)


def ciz(v, alt, ust, satir):
    yer = [kodla(x, alt, ust, satir) for x in v]
    for s in range(satir - 1, -1, -1):
        print(f"{coz(s, alt, ust, satir):5.1f} |"
              + "".join("*" if k == s else " " for k in yer))
    print("      +" + "-" * len(v))
    return yer


print(f"kuzey bolgesi {len(kuzey)} okuma, ortalamasi {BASLANGIC:.3f} m3")
print(f"kurgu dizi: {N} donem, icine konan egilim {EGILIM:+.3f} m3/donem, "
      f"mevsim uzunlugu {MEVSIM} donem, genlik {GENLIK:.2f} m3")
print(f"dizinin en kucuk degeri {min(SERI):.2f}, en buyugu {max(SERI):.2f} m3")
print(f"dizinin kendi egimi {egim(SERI):+.4f} m3/donem")
```

```
kuzey bolgesi 921 okuma, ortalamasi 20.210 m3
kurgu dizi: 36 donem, icine konan egilim -0.140 m3/donem, mevsim uzunlugu 12 donem, genlik 2.20 m3
dizinin en kucuk degeri 13.00, en buyugu 22.02 m3
dizinin kendi egimi -0.1797 m3/donem
```

Son iki satır dersin gerilimini taşıyor. Kurgunun içine konan eğilim dönem başına -0,140 m³'tür ve
koda yazılıdır; dizinin kendi eğimi -0,1797 çıkıyor. Aradaki 0,0397'lik fark gürültüden değil,
mevsimsel bileşenin kendi eğiminden gelir ve üçüncü bölümde ayrıca basılır. Bu fark henüz hiçbir
gösterim kurulmadan, yalnız sayı düzeyinde ortaya çıkıyor.

#### Aynı Dizi, İki Eksen Ölçeği

```python
# eksen.py — ayni dizi ayni yukseklikte iki eksen olcegiyle kodlanir. Okuma
# cozunurlugu bir satirdir: okuyucu izgarada satirdan ince bir konum ayiramaz.
SATIR, DUYARLIK = 12, 0.05
KAYNAK = egim(SERI)
sonuc = {}
for ad, alt, ust in (("sifirdan", 0.0, 25.0), ("kirpilmis", 13.0, 23.0)):
    c = (ust - alt) / (SATIR - 1)
    print(f"eksen {ad}: {alt:.0f}-{ust:.0f} m3, {SATIR} satir, "
          f"okuma cozunurlugu {c:.3f} m3/satir")
    yer = ciz(SERI, alt, ust, SATIR)
    geri = [coz(k, alt, ust, SATIR) for k in yer]
    hata = sum(abs(geri[i] - SERI[i]) for i in range(N)) / N
    print(f"{N} noktanin ortalama geri okuma hatasi {hata:.3f} m3; "
          f"dizi {SATIR} satirin {len(set(yer))} tanesini kullaniyor")
    sonuc[ad] = geri
    print("")

print(f"egim penceresi  sifirdan  kirpilmis  (pencere sayisi)  "
      f"duyarlik {DUYARLIK:.2f} m3/donem")
for w in (4, 6, 9, 12, 15, 18, 24):
    say = []
    for ad in ("sifirdan", "kirpilmis"):
        g = sonuc[ad]
        say.append(sum(1 for i in range(N - w + 1)
                       if abs(egim(g[i:i + w]) - egim(SERI[i:i + w])) <= DUYARLIK))
    print(f"{w:14d} {say[0]:9d} {say[1]:10d} {N - w + 1:18d}")
for ad in ("sifirdan", "kirpilmis"):
    g = sonuc[ad]
    tam = [w for w in range(2, N + 1)
           if all(abs(egim(g[i:i + w]) - egim(SERI[i:i + w])) <= DUYARLIK
                  for i in range(N - w + 1))]
    en = next(w for w in tam if all(v in tam for v in range(w, N + 1)))
    print(f"{ad} eksende egim {DUYARLIK:.2f} duyarlikla ancak {en} donemlik "
          f"pencereden itibaren her yerde geri okunuyor")
```

```
eksen sifirdan: 0-25 m3, 12 satir, okuma cozunurlugu 2.273 m3/satir
 25.0 |                                    
 22.7 | ** *                               
 20.5 |*  * *        ***                   
 18.2 |      **   ***   **     ******      
 15.9 |        ***        *****      **    
 13.6 |                                ****
 11.4 |                                    
  9.1 |                                    
  6.8 |                                    
  4.5 |                                    
  2.3 |                                    
  0.0 |                                    
      +------------------------------------
36 noktanin ortalama geri okuma hatasi 0.540 m3; dizi 12 satirin 5 tanesini kullaniyor

eksen kirpilmis: 13-23 m3, 12 satir, okuma cozunurlugu 0.909 m3/satir
 23.0 |                                    
 22.1 |  *                                 
 21.2 | * **          *                    
 20.3 |*    *        * *                   
 19.4 |      *      *   *         *        
 18.5 |       *    *             *         
 17.5 |           *      *      *  **      
 16.6 |        ***        *    *           
 15.7 |                    * **      **    
 14.8 |                     *              
 13.9 |                                * **
 13.0 |                                 *  
      +------------------------------------
36 noktanin ortalama geri okuma hatasi 0.246 m3; dizi 12 satirin 11 tanesini kullaniyor

egim penceresi  sifirdan  kirpilmis  (pencere sayisi)  duyarlik 0.05 m3/donem
             4         4         13                 33
             6         3         19                 31
             9        13         21                 28
            12        13         22                 25
            15        15         20                 22
            18        14         19                 19
            24        13         13                 13
sifirdan eksende egim 0.05 duyarlikla ancak 24 donemlik pencereden itibaren her yerde geri okunuyor
kirpilmis eksende egim 0.05 duyarlikla ancak 16 donemlik pencereden itibaren her yerde geri okunuyor
```

İki gösterim aynı 36 sayıyı aynı 12 satırlık yüksekliğe kodluyor; tek fark eksen aralığıdır.
Sıfırdan başlayan eksende dizi 12 satırın yalnız **5 tanesini** kullanıyor, kırpılmış eksende
**11 satıra** yayılıyor. Nokta başına ortalama geri okuma hatası bunun sayısal karşılığıdır: 0,540
m³'e karşı 0,246 m³. Hiçbir sayı değişmedi, yalnız aynı sayılar daha ince bir ızgaraya oturdu.

İkinci tablo asıl ölçüdür. Rapora yazılacak eğimin 0,05 m³/dönem duyarlıkla doğru olması isteniyor;
bu eşik üç yıllık toplam değişimin onda birine denk geldiği için seçildi. Dört dönemlik pencerede
sıfırdan başlayan eksen 33 pencerenin yalnız 4'ünde duyarlığı tutturuyor, kırpılmış eksen 13'ünde;
altı dönemlikte fark en keskin yerine geliyor, 3'e karşı 19. Eşiği her pencerede tutturmak için
gereken uzunluk sıfırdan başlayan eksende **24 dönem**, kırpılmış eksende **16 dönem**dir.

Elektronik Tablolarla Analiz kursunun temel grafikler dersi eksen kırpmayı uzunluk kanalı üzerinde
ölçmüş ve kırpmanın çubuk oranını veriye ait olmaktan çıkardığını sayıyla göstermişti. Burada aynı
işlem ters yönde çalışıyor: uzunluk kanalında okunan şey tabana göre bir orandır ve taban kayınca
oran bozulur, konum kanalında okunan şey iki konum arasındaki **farktır** ve fark tabandan
bağımsızdır. Aynı kararın iki kanalda zıt işaret alması, belirleyici olanın karar değil kanal
olduğunu gösterir.

#### Mevsimin Kendi Eğimi

```python
# mevsim.py — mevsimsel bilesenin kendi egimi vardir ve pencerenin nereden
# basladigina gore isaret degistirir.
adim = [SERI[i + 1] - SERI[i] for i in range(N - 1)]
print(f"{len(adim)} komsu adimin {sum(1 for a in adim if a > 0)} tanesinin "
      f"isareti egilimle ters")
print("pencere    dizinin egimi  mevsimin egimi  egilim payi")
for bas in (0, 6, 12):
    p = slice(bas, bas + 24)
    print(f"{bas:2d}-{bas + 24:<2d} donem {egim(SERI[p]):+14.4f} "
          f"{egim(MEVSIMSEL[p]):+15.4f} {EGILIM:+12.3f}")


def duzlestir(v, p):
    """Ortalanmis hareketli ortalama; cift uzunlukta uc degerler yarim sayilir."""
    y = p // 2
    return [(sum(v[i - y + 1:i + y]) + (v[i - y] + v[i + y]) / 2) / p
            for i in range(y, len(v) - y)]


duz = duzlestir(SERI, MEVSIM)
dadim = [duz[i + 1] - duz[i] for i in range(len(duz) - 1)]
print(f"mevsim ayrildiktan sonra {len(duz)} donem kaliyor; {len(dadim)} adimin "
      f"{sum(1 for a in dadim if a > 0)} tanesi ters")
print(f"duzlestirilmis dizinin egimi {egim(duz):+.4f}, ayni pencerenin ham "
      f"egimi {egim(SERI[6:30]):+.4f}, kurguya konan egilim {EGILIM:+.3f}")
```

```
35 komsu adimin 17 tanesinin isareti egilimle ters
pencere    dizinin egimi  mevsimin egimi  egilim payi
 0-24 donem        -0.2239         -0.0857       -0.140
 6-30 donem        -0.0487         +0.0857       -0.140
12-36 donem        -0.2260         -0.0857       -0.140
mevsim ayrildiktan sonra 24 donem kaliyor; 23 adimin 0 tanesi ters
duzlestirilmis dizinin egimi -0.1347, ayni pencerenin ham egimi -0.0487, kurguya konan egilim -0.140
```

Birinci satır gösterimin en yanıltıcı yanını veriyor: 35 komşu adımın 17'si eğilimin tersi yönde.
İki komşu döneme bakıp yön okuyan bir okuyucu yazı tura atmış olur.

Orta tablo nedeni ayırıyor. Üç pencere de 24 dönem uzunluğunda ve üçünde de aynı eğilim var, ama
dizinin eğimi -0,2239, -0,0487 ve -0,2260 çıkıyor. Üçüncü sütun farkın kaynağını söylüyor:
mevsimsel bileşenin kendi en küçük kareler eğimi 0. dönemden başlayan pencerede -0,0857, 6.
dönemden başlayanda **+0,0857**'dir. Bileşen bütün dizide sıfır ortalamalıdır, sonlu bir pencerede
eğime katkı yapar ve katkının işareti pencerenin mevsim içindeki başlangıcına bağlıdır.

Bu, gösterimin değil dizinin özelliğidir ve hiçbir eksen ayarı düzeltemez; ayırma işlemi
gösterimden önce yapılmalıdır. Son iki satır ayırmanın sonucudur: mevsim uzunluğunda ortalanmış
hareketli ortalamayla 23 adımın hiçbiri ters işaretli kalmıyor ve geri okunan eğim -0,1347 oluyor.
Aynı pencerenin ham eğimi -0,0487'ydi; düzleştirme 0,0913'lük bir sapmayı ortadan kaldırdı. Bedeli
12 dönemdir, dizinin başından ve sonundan altışar dönem düşer.

#### Çözünürlük ve Mevsim Birlikte Bağlar

```python
# ikisi_birden.py — cozunurluk ve mevsim ayri ayri degil birlikte baglar.
print("pencere  yalniz eksen  yalniz mevsim  ikisi birlikte")
kirp = sonuc["kirpilmis"]
kd = duzlestir(kirp, MEVSIM)
sd = duzlestir(SERI, MEVSIM)
for w in (4, 6, 9, 12, 18):
    a = sum(1 for i in range(N - w + 1)
            if abs(egim(kirp[i:i + w]) - EGILIM) <= DUYARLIK) / (N - w + 1)
    b = sum(1 for i in range(len(sd) - w + 1)
            if abs(egim(sd[i:i + w]) - EGILIM) <= DUYARLIK) / (len(sd) - w + 1)
    c = sum(1 for i in range(len(kd) - w + 1)
            if abs(egim(kd[i:i + w]) - EGILIM) <= DUYARLIK) / (len(kd) - w + 1)
    print(f"{w:7d} {a * 100:14.1f}% {b * 100:15.1f}% {c * 100:14.1f}%")
print(f"kaynaktaki egilim {EGILIM:+.3f} m3/donem; yukaridaki oranlar "
      f"pencerenin bu sayiyi {DUYARLIK:.2f} duyarlikla verme sikligidir")
```

```
pencere  yalniz eksen  yalniz mevsim  ikisi birlikte
      4            3.0%           100.0%           85.7%
      6            0.0%           100.0%           89.5%
      9            7.1%           100.0%          100.0%
     12            4.0%           100.0%          100.0%
     18          100.0%           100.0%          100.0%
kaynaktaki egilim -0.140 m3/donem; yukaridaki oranlar pencerenin bu sayiyi 0.05 duyarlikla verme sikligidir
```

Önceki tablo geri okunan eğimi **pencerenin kendi eğimiyle** karşılaştırıyordu; bu tablo doğrudan
kurguya konan eğilimle karşılaştırıyor ve sütunlar üç ayrı düzeltmeye karşılık geliyor.

Birinci sütun tek başına eksen ayarının yetersizliğini gösteriyor: kırpılmış eksen altı dönemlik
pencerede eğilimi **hiçbir** pencerede vermiyor, dokuz dönemlikte 28 pencerenin ikisinde veriyor.
Eksen çözünürlüğü noktaların yerini düzeltir, mevsimin eğime kattığı payı düzeltmez. İkinci sütun
tersini gösteriyor: mevsim ayrıldığında dört dönemlik pencere bile duyarlığı tutturuyor. Üçüncü
sütun ikisinin birlikte bedelini yazıyor — kırpılmış eksende kodlanmış ve sonra düzleştirilmiş dizi
yüzde 85,7 ile başlıyor ve dokuz dönemden itibaren yüzde 100 veriyor. Kalan eksik sayısallaştırmadan
gelir: her nokta satır ızgarasına yuvarlanmıştır ve kısa pencerede bu yuvarlama düzleştirmeden sonra
da eğime yansır.

Karar kuralı buradan çıkar: bir zaman serisi gösterimi **eksenin okuma çözünürlüğü** ile **eğimin
okunduğu pencere uzunluğu** yanına yazılmadan raporlanamaz. İkisinden biri eksikse okuyucunun
gördüğü eğim veride bulunan eğilim değildir.

#### Özet

- Zaman ekseninde okunan şey uzunluk değil eğimdir; okuma çözünürlüğü bir satırdır ve eksen
  aralığının satır sayısına bölümüyle yazılır.
- Aynı dizi aynı yükseklikte iki eksen aralığıyla kodlandığında nokta başına ortalama geri okuma
  hatası 0,540 m³ ile 0,246 m³ oluyor; sıfırdan başlayan eksen 12 satırın 5'ini kullanıyor.
- Eğimin 0,05 m³/dönem duyarlıkla her pencerede geri okunduğu en kısa uzunluk sıfırdan başlayan
  eksende 24, kırpılmış eksende 16 dönemdir.
- Mevsimsel bileşenin kendi eğimi pencere başlangıcına göre -0,0857 ile +0,0857 arasında işaret
  değiştirir; aynı 24 dönemlik uzunluk üç yerde -0,2239, -0,0487 ve -0,2260 m³/dönem veriyor.
- Mevsim ortalanmış hareketli ortalamayla ayrıldığında 23 adımın hiçbiri ters kalmıyor ve geri
  okunan eğim -0,1347 oluyor; bedel 12 dönemdir, eksen kırpma bunu tek başına yapamıyor.

#### Sonraki Adım

Bu ders ekseni zamana çevirdi ve kanalın taşıdığı şeyin değişim olduğunu ölçtü. Ölçüm ağının her
okuması bir yerde de alınıyor ve o yer iki boyutlu bir alandır. Sonraki ders bölgeleri bir ızgara
olarak modeller ve alan boyalı bir gösterimin geri okuttuğu şeyin değer değil **alan** olduğunu
ölçer: büyük bir bölgenin küçük değeri, küçük bir bölgenin büyük değerini bastırır ve hata bölgenin
kapladığı hücre sayısıyla orantılı olarak sayılır. Aynı ızgara düzlem üzerine basıldığında esner ve
esneme geri okunan sayıyı üçüncü kez oynatır.

### Coğrafi Görselleştirme

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/cografi-gorsellestirme

Beş bölge 12x12'lik bir kapsama ızgarasında beş renk basamağıyla kodlanıyor ve okuyucunun geri okuduğu şey basamak değil basamağın kapladığı alan oluyor: okuma sayısıyla ağırlıklı doğru ortalama 19,713 m³ iken alanla ağırlıklı geri okuma 18,484 m³ veriyor ve fark -1,228 m³. Batı, okumaların yüzde 14,9'unu ve alanın yüzde 25'ini taşıdığı için 1,680 ağırlık çarpanı alıyor. İzdüşüm esnemesi eşit alanlı iki bölgeyi 1,237 kat farklı bastırıyor. Alan payı okuma payına eşitlendiğinde hata 0,396 m³'e iniyor, bedeli 93 hücrenin bölge değiştirmesi ve komşu bölge çiftinin 8'den 4'e düşmesidir.

Önceki ders ekseni zamana çevirdi ve kanalın taşıdığı şeyin değişim olduğunu ölçtü. Ölçüm ağının
her okuması bir yerde de alınıyor ve o yer tek boyutlu bir eksen değil, iki boyutlu bir alandır.
Alan üzerine kurulan bir gösterimde değer bir renk basamağına çevrilir ve okuyucu basamağı geri
okur. Ama okuyucunun gözüne giren şey basamak değil, **basamağın kapladığı alandır**.

Bu dersin ölçüsü budur: alan boyalı bir gösterimden geri okunan sayı ile veride bulunan sayı
arasındaki fark ve bu farkın bölgelerin alanıyla nasıl orantılı olduğu. Renk basamağı kanalının
kaç ayrı düzeyi geri okuttuğu ilişki görselleştirme dersinde ölçüldü; burada basamak sayısı beşte
sabit tutulur ve değişken alandır. Hiçbir gerçek yer, harita ürünü ya da izdüşüm adı yazılmaz;
kapsama alanı bir ızgara olarak modellenir.

- **GT31. Kapsama ızgarası kurgudur.** Ağın bölgeleri 12x12'lik bir hücre ızgarasına bölünür; her
  hücre bir kapsama birimidir ve bölünme koda yazılıdır.
- **GT32. Alan boyalı gösterimde kanal renk basamağıdır.** Kodlayıcı değeri eşit genişlikli beş
  dilimden birine, çözücü dilimin ortasına eşler; okuma çözünürlüğü dilim genişliğidir ve okuyucu
  dilim içindeki yeri ayırt edemediği için bu sayı geri okunamayan payı verir.
- **GT33. Geri okuma alanla ağırlıklanır.** Bir bakışta okunan ortalama, bölge ortalamalarının
  hücre sayısıyla ağırlıklı ortalamasıdır. Doğru sayı ise okuma sayısıyla ağırlıklı olandır.
- **GT34. Ağırlık çarpanı bölgenin alan payının okuma payına bölümüdür.** Çarpan birden büyükse
  bölge hak ettiğinden fazla, küçükse hak ettiğinden az okunur.
- **GT35. İzdüşüm esnemesi eşit alanları eşit basmaz.** Eğri bir yüzey dikdörtgen bir ızgaraya
  basıldığında satırlar eşit genişlikte basılmaz; esneme çarpanı modelde satır numarasının
  işlevidir ve koda yazılıdır.
- **GT36. Alan payı düzeltilebilir, komşuluk düzeltilemez.** Hücreler okuma payıyla orantılı
  dağıtıldığında alan ağırlığı ortadan kalkar, ama ızgaranın hangi bölgenin hangisine komşu olduğu
  bilgisi bozulur ve bozulma çift sayısıyla ölçülür.

#### Izgara ve Renk Basamağı

```python
# izgara.py — MODELDIR: K03-K05'in kurgu olcum agi ayni tohumla uretilir.
# Agin kapsama alani da KURGUDUR: 12x12'lik bir hucre izgarasidir ve her hucre
# bir kapsama birimidir. Hicbir gercek yer, harita ya da izdusum anilmaz.
import math

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


def uretec(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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


_k, _e = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (_e if r() < 0.046 else _k).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {a["abone_no"] for a in _k}

OKUMA = []
for a in _k + _e:
    r = uretec(TOHUM + 7000 + a["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            a["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        if a["abone_no"] not in GECERLI:
            r()
            continue
        OKUMA.append({"abone_no": a["abone_no"], "bolge": a["bolge"],
                      "donem": DONEM[d], "tuketim_m3": t})
        r()

AD = [b[0] for b in BOLGE]
SAYIM = {a: sum(1 for o in OKUMA if o["bolge"] == a) for a in AD}
DEGER = {a: sum(o["tuketim_m3"] for o in OKUMA if o["bolge"] == a) / SAYIM[a]
         for a in AD}

# Kapsama izgarasi: 12x12, her hucre bir bolgeye ait. Kurgu bir bolunmedir.
BOY, BASAMAK = 12, 5


def hucre_bolge(i, j):
    if i < 3:
        return "kuzey"
    if j < 4:
        return "bati"
    if i >= 8:
        return "guney"
    if j >= 9:
        return "dogu"
    return "merkez"


IZGARA = [[hucre_bolge(i, j) for j in range(BOY)] for i in range(BOY)]
ALAN = {a: sum(r.count(a) for r in IZGARA) for a in AD}
ENAZ, ENCOK = min(DEGER.values()), max(DEGER.values())
ADIM = (ENCOK - ENAZ) / BASAMAK


def kodla(v):
    """Kodlayici: deger -> renk basamagi. Esit genislikli BASAMAK dilim."""
    return min(BASAMAK - 1, int((v - ENAZ) / ADIM))


def coz(b):
    """Cozucu: renk basamagi -> deger. Okuyucu dilimin ortasini okur."""
    return ENAZ + (b + 0.5) * ADIM


print(f"izgara {BOY}x{BOY} = {BOY * BOY} hucre, {BASAMAK} renk basamagi, "
      f"okuma cozunurlugu {ADIM:.3f} m3/basamak")
print("bolge   alan(hucre)  okuma  ortalama  basamak  geri okunan  hata")
for a in AD:
    b = kodla(DEGER[a])
    print(f"{a:7s} {ALAN[a]:11d} {SAYIM[a]:6d} {DEGER[a]:9.3f} {b:8d} "
          f"{coz(b):12.3f} {coz(b) - DEGER[a]:+6.3f}")
for i in range(BOY):
    print("       " + "".join(str(kodla(DEGER[IZGARA[i][j]])) for j in range(BOY)))
```

```
izgara 12x12 = 144 hucre, 5 renk basamagi, okuma cozunurlugu 2.179 m3/basamak
bolge   alan(hucre)  okuma  ortalama  basamak  geri okunan  hata
kuzey            36    921    20.210        2       19.226 -0.985
guney            32    645    16.602        1       17.046 +0.445
dogu             15    557    24.674        4       23.584 -1.090
bati             36    476    13.778        0       14.867 +1.090
merkez           25    600    22.396        3       21.405 -0.991
       222222222222
       222222222222
       222222222222
       000033333444
       000033333444
       000033333444
       000033333444
       000033333444
       000011111111
       000011111111
       000011111111
       000011111111
```

Basamak sütunu kanalın tamamıdır: beş bölge beş ayrı basamağa düştü ve her biri ayırt edilebilir.
Bölge başına geri okuma hatası -1,090 ile +1,090 m³ arasında, yani en çok dilim genişliğinin
yarısı kadar. Bu, renk basamağı kanalının bilinen ve sınırlı bedelidir.

Alttaki ızgara asıl sorunu gösteriyor. Doğunun basamağı 4'tür, ızgaranın en yüksek değeri, ama
sayfada yalnız 15 hücre kaplıyor. Batının basamağı 0'dır ve 36 hücre kaplıyor. Aynı ızgaraya bakan
biri "bu ağ düşük tüketiyor" izlenimi alır; sayfada en çok yer kaplayan basamak en düşük olandır.
Bölge başına hata bunu hiçbir biçimde bildirmez, çünkü hata bölge bölge ölçülür ve okuyucu bölge
bölge bakmaz.

#### Alan Ağırlığı

```python
# agirlik.py — okuyucunun bir bakista geri okudugu sey basamak degil, basamagin
# kapladigi alandir. Iki ortalama yan yana konur.
TOPLAM = sum(SAYIM.values())
dogru = sum(DEGER[a] * SAYIM[a] for a in AD) / TOPLAM
bakis = sum(coz(kodla(DEGER[a])) * ALAN[a] for a in AD) / (BOY * BOY)
duz = sum(coz(kodla(DEGER[a])) for a in AD) / len(AD)
print(f"okuma sayisiyla agirlikli ortalama  {dogru:.3f} m3  ({TOPLAM} okuma)")
print(f"bolge basina duz ortalama           {duz:.3f} m3  ({len(AD)} bolge)")
print(f"alanla agirlikli geri okuma         {bakis:.3f} m3  ({BOY * BOY} hucre)")
print(f"alan agirliginin actigi fark        {bakis - dogru:+.3f} m3")
print("bolge   okuma payi  alan payi  agirlik carpani")
for a in AD:
    op, ap = SAYIM[a] / TOPLAM, ALAN[a] / (BOY * BOY)
    print(f"{a:7s} {op * 100:9.1f}% {ap * 100:9.1f}% {ap / op:16.3f}")
```

```
okuma sayisiyla agirlikli ortalama  19.713 m3  (3199 okuma)
bolge basina duz ortalama           19.226 m3  (5 bolge)
alanla agirlikli geri okuma         18.484 m3  (144 hucre)
alan agirliginin actigi fark        -1.228 m3
bolge   okuma payi  alan payi  agirlik carpani
kuzey        28.8%      25.0%            0.868
guney        20.2%      22.2%            1.102
dogu         17.4%      10.4%            0.598
bati         14.9%      25.0%            1.680
merkez       18.8%      17.4%            0.926
```

Üç ortalama, aynı veriden ve hiçbiri hatalı hesaplanmadan. 3.199 okumanın ortalaması 19,713 m³;
bölge başına düz ortalama 19,226; alan boyalı gösterimden okunan 18,484. Alan ağırlığının açtığı
fark **-1,228 m³**'tür ve tek bir okuma değişmedi.

Alt tablo farkın nereden geldiğini bölge bölge yazıyor. Batı, okumaların yüzde 14,9'unu taşıyor ama
alanın yüzde 25'ini kaplıyor; ağırlık çarpanı **1,680**'dir, yani sayfada gerçek payının yaklaşık
iki katı kadar konuşuyor. Doğu ise tersine 0,598 çarpanla, payının yarısından biraz fazlası kadar.
Batının en düşük, doğunun en yüksek ortalamaya sahip olması bir rastlantıdır, ama iki rastlantı
aynı yöne çalışınca geri okunan sayı bir tarafa kayar.

Bu, kanalın seçiminden bağımsız bir kusur değildir; **alan kanalının tanımıdır**. Renk basamağı
alana boyanır ve alan zaten sabittir, veriyle ilgisi yoktur. Gösterim, tasarımcının seçmediği bir
ağırlıklandırma uygular ve o ağırlığı hiçbir yere yazmaz.

#### İzdüşüm Esnemesi

```python
# esneme.py — egri bir yuzey dikdortgen bir izgaraya basildiginda satirlar esit
# genislikte basilmaz. Esneme carpani MODELDIR: ust satirlar daha genis basilir.
BUKUM, REF = 0.07, BOY - 1


def esneme(i):
    return 1.0 / math.cos(BUKUM * (REF - i))


BASILAN = {a: sum(esneme(i) for i in range(BOY) for j in range(BOY)
                  if IZGARA[i][j] == a) for a in AD}
TOPLAM_ALAN = sum(BASILAN.values())
esnek = sum(coz(kodla(DEGER[a])) * BASILAN[a] for a in AD) / TOPLAM_ALAN
print(f"esneme carpani en ust satirda {esneme(0):.3f}, en alt satirda "
      f"{esneme(BOY - 1):.3f}")
print("bolge   gercek alan  basilan alan  buyume")
for a in AD:
    print(f"{a:7s} {ALAN[a]:11d} {BASILAN[a]:13.2f} "
          f"{BASILAN[a] / ALAN[a] - 1:+7.1%}")
print(f"kuzey ile bati esit alanli ({ALAN['kuzey']} hucre) ama basilan alanlari "
      f"{BASILAN['kuzey'] / BASILAN['bati']:.3f} kat farkli")
print(f"esnemeden once alanla agirlikli geri okuma {bakis:.3f} m3, "
      f"sonra {esnek:.3f} m3")
print(f"dogru sayiya uzaklik: {abs(bakis - dogru):.3f} -> "
      f"{abs(esnek - dogru):.3f} m3")
```

```
esneme carpani en ust satirda 1.393, en alt satirda 1.000
bolge   gercek alan  basilan alan  buyume
kuzey            36         47.26  +31.3%
guney            32         32.28   +0.9%
dogu             15         16.54  +10.3%
bati             36         38.19   +6.1%
merkez           25         27.57  +10.3%
kuzey ile bati esit alanli (36 hucre) ama basilan alanlari 1.237 kat farkli
esnemeden once alanla agirlikli geri okuma 18.484 m3, sonra 18.579 m3
dogru sayiya uzaklik: 1.228 -> 1.133 m3
```

Esneme, alan ağırlığının üzerine binen ikinci bir çarpıtmadır ve bu kez tasarımcının kararı bile
değildir: eğri bir yüzeyi düz bir ızgaraya basmanın kaçınılmaz bedelidir. Modelde en üst satır en
alt satırın 1,393 katı genişlikte basılıyor.

Beşinci satır ölçünün en açık ifadesidir. Kuzey ile batı **eşit alanlıdır**, ikisi de 36 hücre.
Basıldıklarında 47,26 ile 38,19 oluyor, yani aynı büyüklükteki iki bölge **1,237 kat** farklı
büyüklükte görünüyor. Bir okuyucunun iki bölgenin ağırlığını karşılaştırması için hiçbir dayanağı
yok.

Son iki satır dikkatle okunmalı. Esneme bu kurguda toplam hatayı 1,228'den 1,133 m³'e **düşürüyor**.
Bu bir düzeltme değil, bir rastlantıdır: en çok esneyen bölge kuzeydir ve kuzeyin geri okunan
değeri doğru sayıya yakın olduğu için ortalamayı yukarı çekmiştir. Esnemenin işareti, bölgelerin
ızgarada nerede durduğuna bağlıdır ve tasarımcının denetiminde değildir. Hatayı azaltması da
artırması kadar tesadüfidir; ölçülmesi gereken şey toplam değil, bölge başına basılan alan
sapmasıdır.

#### Alan Payını Düzeltmek

```python
# kartogram.py — hucreler okuma payiyla orantili yeniden dagitilir. Alan
# agirligi duzelir, izgaranin komsuluk yapisi bozulur.
kota = {a: SAYIM[a] * BOY * BOY / TOPLAM for a in AD}
pay = {a: int(kota[a]) for a in AD}
for a in sorted(AD, key=lambda x: kota[x] - int(kota[x]), reverse=True):
    if sum(pay.values()) < BOY * BOY:
        pay[a] += 1
akan = [a for a in AD for _ in range(pay[a])]
KART = [akan[i * BOY:(i + 1) * BOY] for i in range(BOY)]
YENI = {a: sum(r.count(a) for r in KART) for a in AD}
kartbakis = sum(coz(kodla(DEGER[a])) * YENI[a] for a in AD) / (BOY * BOY)
tasinan = sum(1 for i in range(BOY) for j in range(BOY)
              if IZGARA[i][j] != KART[i][j])


def komsuluk(g):
    c = set()
    for i in range(BOY):
        for j in range(BOY):
            for di, dj in ((0, 1), (1, 0)):
                x, y = i + di, j + dj
                if x < BOY and y < BOY and g[i][j] != g[x][y]:
                    c.add(tuple(sorted((g[i][j], g[x][y]))))
    return c


k1, k2 = komsuluk(IZGARA), komsuluk(KART)
print("bolge   eski alan  kartogram alan  okuma payi")
for a in AD:
    print(f"{a:7s} {ALAN[a]:9d} {YENI[a]:15d} {SAYIM[a] / TOPLAM * 100:10.1f}%")
for i in range(BOY):
    print("       " + "".join(str(kodla(DEGER[KART[i][j]])) for j in range(BOY)))
print(f"alanla agirlikli geri okuma {bakis:.3f} -> {kartbakis:.3f} m3, "
      f"dogru sayi {dogru:.3f}")
print(f"hata {abs(bakis - dogru):.3f} -> {abs(kartbakis - dogru):.3f} m3; "
      f"{tasinan} hucre bolge degistirdi")
print(f"komsu bolge cifti {len(k1)} -> {len(k2)}; kaybolan "
      f"{len(k1 - k2)}, uydurulan {len(k2 - k1)}")
```

```
bolge   eski alan  kartogram alan  okuma payi
kuzey          36              42       28.8%
guney          32              29       20.2%
dogu           15              25       17.4%
bati           36              21       14.9%
merkez         25              27       18.8%
       222222222222
       222222222222
       222222222222
       222222111111
       111111111111
       111111111114
       444444444444
       444444444444
       000000000000
       000000000333
       333333333333
       333333333333
alanla agirlikli geri okuma 18.484 -> 19.317 m3, dogru sayi 19.713
hata 1.228 -> 0.396 m3; 93 hucre bolge degistirdi
komsu bolge cifti 8 -> 4; kaybolan 6, uydurulan 2
```

Düzeltme basittir: her bölgeye okuma payıyla orantılı sayıda hücre verilir. Doğunun alanı 15'ten
25'e çıkar, batının 36'dan 21'e iner. Geri okuma hatası **1,228'den 0,396 m³'e** düşer, yani üçte
birine. Kalan 0,396 alan ağırlığından değil, renk basamağının 2,179 m³'lük çözünürlüğünden gelir ve
alan düzeltmesiyle kapatılamaz.

Bedel iki sayıyla yazılıdır. Birincisi 144 hücrenin **93'ünün** bölge değiştirmesidir; ızgaranın
üçte ikisi artık başlangıçtaki yerinde değil. İkincisi daha ağırdır: özgün ızgarada **8** komşu
bölge çifti vardı, kartogramda **4** kaldı. Altı komşuluk kayboldu ve iki tane de var olmayan
komşuluk uyduruldu. Kartogram doğru sayıyı geri okutur, ama "hangi bölge hangisine bitişik"
sorusunun yanıtını bozar; okuyucu ikinci soruyu da bu gösterimden sorabileceğini varsayarsa yeni
bir yanlışa düşer.

Karar kuralı buradan çıkar. Alan üzerine kurulan bir gösterim iki sayı olmadan raporlanamaz:
**bölgenin alan payı ile okuma payının oranı** ve **renk basamağının çözünürlüğü**. Alan payı
düzeltilirse komşuluk kaybının kaç çift olduğu da yazılır, çünkü düzeltme başka bir soruyu
okunamaz hale getirmiştir.

#### Özet

- Alan boyalı gösterimde kanal renk basamağıdır; beş basamak 2,179 m³'lük bir okuma çözünürlüğü
  verir ve bölge başına geri okuma hatası en çok 1,090 m³'tür.
- Okuyucunun bir bakışta geri okuduğu sayı alanla ağırlıklıdır: 3.199 okumanın doğru ortalaması
  19,713 m³ iken gösterimden okunan 18,484 m³ çıkıyor, fark -1,228 m³.
- Ağırlık çarpanı alan payının okuma payına bölümüdür; batı 1,680, doğu 0,598 çarpan alıyor ve
  gösterim bu ağırlığı hiçbir yere yazmıyor.
- İzdüşüm esnemesi eşit alanlı kuzey ile batıyı 1,237 kat farklı bastırıyor; toplam hatayı bu
  kurguda 0,095 m³ azaltması bir rastlantıdır ve işareti tasarımcının denetiminde değildir.
- Alan payı okuma payına eşitlendiğinde hata 0,396 m³'e iniyor; bedeli 93 hücrenin yer değiştirmesi
  ve komşu bölge çiftinin 8'den 4'e düşmesidir.

#### Sonraki Adım

Bu derste ölçülen çarpıtmanın hiçbiri bir tercihten doğmadı: alan ızgarada verilidir, esneme düz
bir yüzeye basmanın bedelidir. Yine de geri okunan sayı 1,228 m³ kaydı. Sonraki ders aynı soruyu
tersinden sorar ve dört çarpıtmayı bir **aile** olarak ele alır — eksen kırpma, alan kanalının
oranı karesiyle büyütmesi, ikili eksen ve düzensiz aralık aynı veriye sırayla uygulanır ve her
birinin geri okuma hatası ayrı ayrı basılır. Dördü de savunulabilir bir varsayılan davranıştan
doğar; gösterilecek olan, yanıltmanın kasıt gerektirmediğidir.

### Görsel Yanıltma

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/gorsel-yaniltma

Doğu ile kuzey arasındaki 1,2208'lik gerçek oran, eksen 18 m³'ten başlatıldığında 3,0769 çubuk oranı veriyor ve uzunluğu değerle orantılı sanan okuyucu kuzeyi 20,210 yerine 8,019 m³ okuyor. Aynı oran bir kareye kodlandığında iki kenar da çarpıldığı için alan oranı 1,4905 çıkıyor ve doğu 30,123 m³ okunuyor. Farklı birimli iki dizi için dokuz savunulabilir eksen çiftinden yedi ayrı kesişme dönemi çıkıyor. Düzensiz aralıklı 12 dönem eşit aralıklarla dizildiğinde -0,2079 m³/dönemlik eğim -0,6635 okunuyor. Dördü de savunulabilir bir varsayılan davranıştan doğuyor.

Önceki ders bir çarpıtmayı hiçbir tercihe bağlamadan ölçtü: bölgelerin alanı ızgarada verilidir ve
esneme düz bir yüzeye basmanın bedelidir. Yine de geri okunan sayı 1,228 m³ kaydı. Bu ders aynı
soruyu genişletir ve dört ayrı çarpıtmayı tek bir **aile** olarak ele alır. Ailenin ortak özelliği
şudur: her üye kanalın kendi tanımından doğar, hiçbiri hesap hatası değildir ve hiçbiri kasıt
gerektirmez.

Eksen kırpmanın çubuk uzunluklarının oranını bozması Elektronik Tablolarla Analiz kursunun temel
grafikler dersinde ölçüldü ve o sayı burada tekrarlanmaz. Orada ölçü **gösterimin meşruluğuydu** —
bu grafik bu ölçekte olur mu. Burada kırpma, ailenin dört üyesinden yalnız biridir ve aynı veriye
sırayla uygulanan dört işlemin ilkidir. Ölçülen şey her üyenin geri okuttuğu sayıdır.

- **GT37. Çarpıtma kanalın tanımından doğar.** Uzunluk kanalı tabana göre okunur, alan kanalı iki
  boyutta birden büyür, konum kanalı eksen aralığına bağlıdır. Hiçbiri hesabı bozmaz, üçü de geri
  okumayı bozar.
- **GT38. Okuyucunun çözücüsü değiştirilemez.** Okuyucu uzunluğu değerle, alanı değerle, komşu
  noktaları eşit dönem aralığıyla orantılı sayar. Gösterim bu varsayımı yanlışlarsa hata
  gösterime değil okuyucuya yazılır.
- **GT39. Alan kanalı oranı karesiyle büyütür.** Bir oranı "büyük göstermek" için şeklin iki kenarı
  birden çarpılırsa alan oranı değer oranının karesi olur.
- **GT40. İkili eksende kesişmenin veride karşılığı yoktur.** İki dizi farklı birimdeyse kesişme
  dönemi iki eksen aralığının seçimiyle belirlenir ve seçimler savunulabilir olduğu halde tek bir
  sayı vermez.
- **GT41. Düzensiz aralık eşit dizildiğinde eğim adım sayısıyla ölçülür.** Okuyucu adımı dönem
  sanar; büyütme çarpanı dönem açıklığının adım sayısına bölümüdür.
- **GT42. Diziler kurgudur.** 36 dönemlik tüketim ve okuma sayısı dizileri aynı tohumla türetildi
  ve kurgu olduğu koda yazılıdır.

#### Uzunluk: Eksen Kırpma

```python
# yaniltma.py — MODELDIR: K03-K05'in kurgu olcum agi ayni tohumla uretilir.
# 36 donemlik iki dizi de KURGUDUR ve ayni tohumla turetildi: tuketim
# ortalamasi (m3) ve donem basina okuma sayisi (adet).
import math

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


def uretec(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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


_k, _e = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (_e if r() < 0.046 else _k).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {a["abone_no"] for a in _k}

OKUMA = []
for a in _k + _e:
    r = uretec(TOHUM + 7000 + a["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            a["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        if a["abone_no"] not in GECERLI:
            r()
            continue
        OKUMA.append({"abone_no": a["abone_no"], "bolge": a["bolge"],
                      "donem": DONEM[d], "tuketim_m3": t})
        r()

AD = [b[0] for b in BOLGE]
DEGER = {a: sum(o["tuketim_m3"] for o in OKUMA if o["bolge"] == a)
         / sum(1 for o in OKUMA if o["bolge"] == a) for a in AD}
N = 36
_g = uretec(TOHUM + 505)
TUKETIM = [round(DEGER["kuzey"] - 0.140 * t
                 + 2.20 * math.sin(2 * math.pi * t / 12)
                 + (_g() + _g() + _g() - 1.5) * 0.60, 3) for t in range(N)]
_h = uretec(TOHUM + 917)
SAYAC = [round(1066 + 6.4 * t + 40 * math.sin(2 * math.pi * t / 12)
               + (_h() + _h() + _h() - 1.5) * 26) for t in range(N)]

GENIS = 40


def uzunluk(v, taban, tepe):
    """Kodlayici: deger -> uzunluk. Cubuk tam karaktere yuvarlanir."""
    return max(0, round(GENIS * (v - taban) / (tepe - taban)))


def coz_uzunluk(u, taban, tepe):
    """Cozucu: uzunluk -> deger. Okuma cozunurlugu bir karakterdir."""
    return taban + u / GENIS * (tepe - taban)


a, b = DEGER["dogu"], DEGER["kuzey"]
GERCEK = a / b
print(f"dogu {a:.3f} m3, kuzey {b:.3f} m3, gercek oran {GERCEK:.4f}")
for taban in (0.0, 18.0):
    ua, ub = uzunluk(a, taban, a), uzunluk(b, taban, a)
    print(f"eksen {taban:4.0f} m3'ten: dogu  |{'#' * ua}")
    print(f"              kuzey |{'#' * ub}")
    print(f"  cubuk orani {ua / ub:.4f}; uzunlugu degerle orantili sanan "
          f"okuyucu kuzeyi {coz_uzunluk(ub, 0.0, a):.3f} m3 okur")
KIRPIK = uzunluk(a, 18.0, a) / uzunluk(b, 18.0, a)
print(f"kirpma orani {GERCEK:.4f} -> {KIRPIK:.4f}, buyutme carpani "
      f"{KIRPIK / GERCEK:.3f}; kuzeyde geri okuma hatasi "
      f"{coz_uzunluk(uzunluk(b, 18.0, a), 0.0, a) - b:+.3f} m3")
```

```
dogu 24.674 m3, kuzey 20.210 m3, gercek oran 1.2208
eksen    0 m3'ten: dogu  |########################################
              kuzey |#################################
  cubuk orani 1.2121; uzunlugu degerle orantili sanan okuyucu kuzeyi 20.356 m3 okur
eksen   18 m3'ten: dogu  |########################################
              kuzey |#############
  cubuk orani 3.0769; uzunlugu degerle orantili sanan okuyucu kuzeyi 8.019 m3 okur
kirpma orani 1.2208 -> 3.0769, buyutme carpani 2.520; kuzeyde geri okuma hatasi -12.191 m3
```

İki çubuk kümesi aynı iki sayıyı taşıyor. Sıfırdan başlayan eksende oran 1,2121 çıkıyor; gerçek
oran 1,2208'dir ve aradaki fark çubukların tam karaktere yuvarlanmasından, yani okuma
çözünürlüğünden gelir. 18 m³'ten başlayan eksende aynı iki sayının oranı **3,0769** oluyor.

Asıl sayı son satırdadır. Uzunluğu değerle orantılı sayan bir okuyucu kırpılmış gösterimden kuzeyi
**8,019 m³** okur; gerçek değer 20,210'dur ve geri okuma hatası **-12,191 m³**'tür. Ne veri
değişti ne de bir hesap yanlış yapıldı.

Bu kararın nereden geldiği önemlidir. İki bölgenin ortalaması 20,210 ile 24,674 arasında sıkışık ve
sıfırdan başlayan bir eksende iki çubuk neredeyse aynı görünüyor. "Farkı görünür kıl" bir tasarım
gereğidir ve bir önceki kurs bunu meşru bir karar olarak ele almıştı — çizgi grafiğinde okunan şey
eğim olduğu için orada eksen kırpılabilir. Aynı karar uzunluk kanalında uygulandığında okuyucunun
çözücüsünü yanlışlar. Karar aynıdır, kanal farklıdır, sonuç 12 m³'lük bir hatadır.

#### Alan: Oranın Karesi

```python
# alan.py — "iki kat buyuk goster" karari sekli iki boyutta birden buyutur;
# okuyucu alani okur ve oran karesiyle geri doner.
KENAR = 12


def alan_kodla(oran):
    """Kodlayici: oran -> alan. Iki kenar da oranla carpilir."""
    k = KENAR * oran
    return k * k


def alan_coz(al):
    """Cozucu: alan -> deger. Okuyucu alani degerle orantili sanar."""
    return b * al / (KENAR * KENAR)


al_a, al_b = alan_kodla(GERCEK), alan_kodla(1.0)
print(f"kuzey karesi {KENAR}x{KENAR} = {al_b:.0f} birim alan")
print(f"dogu karesi {KENAR * GERCEK:.2f}x{KENAR * GERCEK:.2f} = {al_a:.1f} birim alan")
print(f"alan orani {al_a / al_b:.4f} = gercek oranin karesi {GERCEK ** 2:.4f}")
print(f"geri okunan dogu degeri {alan_coz(al_a):.3f} m3, gercek {a:.3f} m3, "
      f"hata {alan_coz(al_a) - a:+.3f} m3")
```

```
kuzey karesi 12x12 = 144 birim alan
dogu karesi 14.65x14.65 = 214.6 birim alan
alan orani 1.4905 = gercek oranin karesi 1.4905
geri okunan dogu degeri 30.123 m3, gercek 24.674 m3, hata +5.449 m3
```

Ailenin ikinci üyesi eksene hiç dokunmaz. Doğunun kuzeyden 1,2208 kat büyük olduğunu göstermek
için doğunun karesi 1,2208 ile ölçeklenir; ölçekleme iki kenara birden uygulanır çünkü kare kare
kalmalıdır. Sonuç, alan oranının **1,4905** olmasıdır ve bu sayı değer oranının karesidir.

Okuyucu alanı görür, kenarı değil. Alanı değerle orantılı sayan bir okuyucu doğuyu **30,123 m³**
okur; gerçek değer 24,674'tür ve geri okuma hatası **+5,449 m³**'tür. Büyütme çarpanı 1,2208, yani
oranın kendisi kadardır — oran ne kadar büyükse çarpıtma da o kadar büyür.

Bu üye eksen kırpmadan iki bakımdan farklıdır. Birincisi, düzeltilemez: kenar oranını değerin
karekökü yapmak alanı düzeltir ama bu kez kenar oranı veriye ait olmaktan çıkar ve kenarı okuyan
biri yanılır. İkincisi, kararın kendisi görünmez; eksen tabanı bir sayıdır ve yazılabilir, "hangi
boyut ölçeklendi" ise gösterimde hiçbir yerde durmaz.

#### Konum: İkili Eksen ve Düzensiz Aralık

```python
# ikili.py — iki dizi iki ayri birimde. Her eksen kendi araligina cekildiginde
# kesisme donemi eksen secimiyle belirlenir.
SATIR = 12


def konum(v, alt, ust):
    return max(0, min(SATIR - 1, round((v - alt) / (ust - alt) * (SATIR - 1))))


def secenekler(v):
    en, ec = min(v), max(v)
    return {"sifirdan": (0.0, ec),
            "veri araligi": (math.floor(en), math.ceil(ec)),
            "genis": (math.floor(en) - (ec - en) * 0.5, math.ceil(ec))}


sa, sb = secenekler(TUKETIM), secenekler(SAYAC)
print("tuketim ekseni  sayac ekseni    kesisme donemi")
kesisme = set()
for na, (aa, au) in sa.items():
    for nb, (ba, bu) in sb.items():
        k = next((t for t in range(N)
                  if konum(TUKETIM[t], aa, au) <= konum(SAYAC[t], ba, bu)), None)
        kesisme.add(k)
        print(f"{na:14s}  {nb:14s} {str(k):>15s}")
print(f"{len(sa) * len(sb)} savunulabilir eksen ciftinden "
      f"{len(kesisme)} farkli kesisme donemi cikiyor")
print(f"iki dizinin birimleri farkli (m3 ve adet); kesisme donemi veride "
      f"karsiligi olmayan bir sayidir")
```

```
tuketim ekseni  sayac ekseni    kesisme donemi
sifirdan        sifirdan                     5
sifirdan        veri araligi                25
sifirdan        genis                       19
veri araligi    sifirdan                     0
veri araligi    veri araligi                18
veri araligi    genis                        8
genis           sifirdan                     0
genis           veri araligi                19
genis           genis                       17
9 savunulabilir eksen ciftinden 7 farkli kesisme donemi cikiyor
iki dizinin birimleri farkli (m3 ve adet); kesisme donemi veride karsiligi olmayan bir sayidir
```

İkili eksen, ailenin en zor savunulan üyesidir çünkü var olma nedeni meşrudur: tüketim ortalaması
m³, dönem başına okuma sayısı adet cinsindendir ve ikisini tek eksene koymak bir seriyi düz bir
çizgiye indirger. Her seriyi kendi aralığına çekmek doğal çözümdür.

Bedeli tablodadır. Dokuz savunulabilir eksen çiftinden **yedi ayrı kesişme dönemi** çıkıyor ve
aralarında 0. dönemle 25. dönem gibi birbirinden tamamen farklı yanıtlar var. Okuyucunun bu
gösterimden çıkardığı "şu dönemde biri ötekini geçti" cümlesi veride hiçbir şeye karşılık gelmez;
iki farklı birimin karşılaştırılabilir olduğu izlenimi tümüyle eksen seçiminin ürünüdür. Diğer üç
üyede geri okunan sayının bir hatası vardı; burada geri okunan sayının **kaynağı yoktur**.

```python
# aralik.py — elde bulunan donemler esit araliklarla dizilirse egim bozulur.
ELDE = [0, 1, 2, 3, 4, 5, 6, 12, 18, 24, 30, 35]


def egim(xs, ys):
    n = len(xs)
    ox, oy = sum(xs) / n, sum(ys) / n
    return (sum((xs[i] - ox) * (ys[i] - oy) for i in range(n))
            / sum((x - ox) ** 2 for x in xs))


y = [TUKETIM[t] for t in ELDE]
ara = [ELDE[i + 1] - ELDE[i] for i in range(len(ELDE) - 1)]
dogru = egim(ELDE, y)
cizilen = egim(list(range(len(ELDE))), y)
print(f"{len(ELDE)} donem elde var, {ELDE[0]}. donemden {ELDE[-1]}. doneme "
      f"yayiliyor; aralar {min(ara)}-{max(ara)} donem")
print(f"gercek egim {dogru:+.4f} m3/donem ({ELDE[-1] - ELDE[0]} donemlik yayilim)")
print(f"esit araliklarla cizilen gosterimden okunan egim {cizilen:+.4f} "
      f"m3/donem ({len(ELDE) - 1} adim), buyutme {cizilen / dogru:.2f} kat")
print("adim  gercek aralik  gercek egim  cizilen egim")
for i in (0, 6, 7, len(ara) - 1):
    print(f"{i:4d} {ara[i]:14d} {(y[i + 1] - y[i]) / ara[i]:+12.4f} "
          f"{y[i + 1] - y[i]:+13.4f}")

TABLO = [("eksen kirpma", "uzunluk", f"{GERCEK:.4f}", f"{KIRPIK:.4f}",
          f"{KIRPIK / GERCEK:.2f} kat", "eksen tabani"),
         ("alan buyutme", "alan", f"{GERCEK:.4f}", f"{al_a / al_b:.4f}",
          f"{(al_a / al_b) / GERCEK:.2f} kat", "kenar mi alan mi olceklendi"),
         ("ikili eksen", "konum", "yok", f"{len(kesisme)} ayri donem",
          "-", "iki eksenin araligi"),
         ("duzensiz aralik", "konum", f"{dogru:.4f}", f"{cizilen:.4f}",
          f"{cizilen / dogru:.2f} kat", "donem araliklari")]
print("carpitma         kanal    kaynak    geri okunan   buyutme   yazilmayan karar")
for ad, kanal, kay, geri, buy, satir in TABLO:
    print(f"{ad:16s} {kanal:8s} {kay:>8s} {geri:>13s} {buy:>9s}   {satir}")
```

```
12 donem elde var, 0. donemden 35. doneme yayiliyor; aralar 1-6 donem
gercek egim -0.2079 m3/donem (35 donemlik yayilim)
esit araliklarla cizilen gosterimden okunan egim -0.6635 m3/donem (11 adim), buyutme 3.19 kat
adim  gercek aralik  gercek egim  cizilen egim
   0              1      +1.3370       +1.3370
   6              6      -0.1895       -1.1370
   7              6      -0.0655       -0.3930
  10              5      -0.3822       -1.9110
carpitma         kanal    kaynak    geri okunan   buyutme   yazilmayan karar
eksen kirpma     uzunluk    1.2208        3.0769  2.52 kat   eksen tabani
alan buyutme     alan       1.2208        1.4905  1.22 kat   kenar mi alan mi olceklendi
ikili eksen      konum         yok  7 ayri donem         -   iki eksenin araligi
duzensiz aralik  konum     -0.2079       -0.6635  3.19 kat   donem araliklari
```

Dördüncü üye en sık ve en fark edilmeden ortaya çıkanıdır. Elde 12 dönem var ve bunlar 0. dönemden
35. döneme yayılıyor; aralar 1 ile 6 dönem arasında değişiyor. Gösterim onları sırayla ve eşit
aralıklarla diziyor, çünkü elde bulunanı sırayla dizmek varsayılan davranıştır.

Eğim -0,2079 m³/dönem yerine **-0,6635** okunuyor, **3,19 kat** büyük. Çarpan dönem açıklığının adım
sayısına bölümüne yakındır: 35 bölü 11, yani 3,18. Adım tablosu bunun adım adım nasıl oluştuğunu
gösteriyor — 0. adımda gerçek aralık bir dönemdir ve iki sayı çakışıyor, 6. adımda aralık altı
döneme çıkıyor ve dönem başına -0,1895 olan düşüş gösterimde -1,1370 olarak okunuyor.

Son tablo dört üyeyi yan yana koyuyor. Son sütun, dördünün ortak yanıdır: **her birinde geri okumayı
düzeltecek bilgi gösterimin dışında kalmıştır**. Eksen tabanı bir sayıdır ve yazılabilir; dönem
aralıkları da öyle. "Hangi boyut ölçeklendi" ve "iki eksenin aralığı ne" ise ancak gösterimin
yapıldığı yerde bilinir ve okuyucuya hiç ulaşmaz. Dördü de bir savunulabilir varsayılandan doğdu:
farkı görünür kıl, şekli oranla büyüt, iki seriyi kendi aralığına çek, elde bulunanı sırayla diz.
Yanıltma bunların hiçbirinde kasıt gerektirmedi.

#### Özet

- Dört çarpıtma tek bir ailedir: kanalın kendi tanımından doğar, hesabı bozmaz, geri okumayı bozar.
- Eksen 18 m³'ten başlatıldığında 1,2208'lik oran 3,0769 okunuyor ve uzunluğu değerle orantılı sayan
  okuyucu kuzeyi 20,210 yerine 8,019 m³ okuyor, hata -12,191 m³.
- Aynı oran bir kareye kodlandığında iki kenar birden çarpıldığı için alan oranı 1,4905, yani
  oranın karesi oluyor; doğu 30,123 m³ okunuyor ve hata +5,449 m³.
- Farklı birimli iki dizide dokuz savunulabilir eksen çiftinden yedi ayrı kesişme dönemi çıkıyor;
  geri okunan sayının kaynakta karşılığı yok.
- Düzensiz aralıklı 12 dönem eşit dizildiğinde -0,2079 m³/dönemlik eğim -0,6635 okunuyor; çarpan
  dönem açıklığının adım sayısına bölümüne yakındır, 35 bölü 11 ile 3,18.
- Dördünde de geri okumayı düzeltecek bilgi gösterimin dışında kalıyor ve dördü de savunulabilir
  bir varsayılan davranıştan doğuyor.

#### Sonraki Adım

Bu ders çarpıtmanın kasıt gerektirmediğini gösterdi; her üyede gösterim doğru kurulmuş, tek bir
sayı yanlış hesaplanmamıştı. Bir varsayım hâlâ el değmeden duruyor: okuyucunun kanalı okuyabildiği.
Sonraki ders bunu ölçer. Renk tek kodlama kanalıyken kaç seri birbirinden ayırt edilemez kalıyor
sayılır ve şekil, doğrudan etiketleme ile sıralamadan oluşan **yedekli kodlama** bu sayıyı kaça
indiriyor ölçülür. Karşıtlık oranı hesabı ve renk sistemi arayüz kurslarının işidir ve
tekrarlanmaz; burada sayılan yalnız veriye özgü olandır.

### Erişilebilir Grafik

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/erisilebilir-grafik

Sekiz seri iki ton ailesi ve dört aydınlık basamağından oluşan varsayılan bir paletle kodlandığında tam çözücüyle sekizi de ayırt ediliyor, ton ayrımını okumayan çözücüyle yalnız dördü kalıyor. Şekil kanalı sırayla dağıtıldığında sayı dörtte kalıyor çünkü şekil dört seride başa dönüyor ve renk çakışması da dört seri arayla oluyor; şekil çakışmaya göre dağıtıldığında sekize çıkıyor. Gösterge kutusu 16 geçiş istiyor, sıralı gösterge 6, doğrudan etiketleme 0. Dokuz aydınlık basamağından iki aralıkla beşi ayırt ediliyor ve dört şekille tek gösterimin tavanı 20 seri çıkıyor.

Yedi ders boyunca ölçülen şey kanalın taşıdığı sayıydı ve her seferinde okuyucunun kanalı
**okuyabildiği** varsayıldı. Bu ders o varsayımı ölçer. Bir gösterimde birden çok seri varsa ilk
soru serilerin değerlerinin doğru okunup okunmadığı değildir; ilk soru hangi serinin hangisi
olduğudur. Bir seri komşusundan ayırt edilemiyorsa değeri de raporlanmamış sayılır.

Karşıtlık oranı hesabı, renk sistemleri ve erişilebilirlik ölçütleri Arayüz Temelleri kursunda ve
erişilebilirlik kurslarında kuruldu; bunların hiçbiri burada tekrarlanmaz ve hiçbir renk sistemi
adı yazılmaz. Buradaki soru veriye özgüdür ve tek bir sayıyla ölçülür: **ayırt edilebilir seri
sayısı**. Renk tek kodlama kanalıyken bu sayı kaç ve yedekli kodlama onu kaça çıkarıyor.

- **GT43. Renk bir ad değil iki sayıdır.** Modelde renk, ton ailesi ile aydınlık basamağından
  oluşur. Kodlayıcı seriye bir çift atar; hiçbir renk adı ya da sistemi anılmaz.
- **GT44. Okuma çözünürlüğü iki basamaktır.** İki aydınlık basamağı en az iki basamak farklı
  olmadan ayırt edilemez, çünkü gösterimde seriler ince çizgilerdir ve birbirinden uzakta durur;
  yan yana konmuş iki geniş yüzeyin ayrımı bu değildir.
- **GT45. İki çözücü koşar.** Tam çözücü ton ailesini de aydınlık basamağını da okur. Daralmış
  çözücü ton ayrımını okumaz ve yalnız aydınlık basamağını görür; ikisi de aynı kodlamaya uygulanır.
- **GT46. Ayırt edilebilir seri sayısı küme sayısıdır.** Birbirinden ayırt edilemeyen seriler tek
  bir kümede toplanır; geri okunan seri sayısı küme sayısıdır ve serilerin toplamından küçük olur.
- **GT47. Yedekli kodlamanın yeri sonucu belirler.** İkinci bir kanal eklemek tek başına yetmez;
  kanalın, rengin çakıştığı eksende değişmesi gerekir.
- **GT48. Tek gösterimin bir tavanı vardır.** Tavan, ayırt edilebilir basamak sayısı ile şekil
  sayısının çarpımıdır; üzerinde gösterim parçalanmadan seri sayısı geri okunamaz.

#### Renk Tek Kanalken

```python
# seri.py — MODELDIR: K03-K05'in kurgu olcum agi ayni tohumla uretilir.
# Sekiz seri ayni 3.199 okumanin alt kumeleridir: bes bolge ve uc tarife
# basamagi. Renk bir ad degil, iki sayidir: ton ailesi ve aydinlik basamagi.
import math

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


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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


_k, _e = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (_e if r() < 0.046 else _k).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {a["abone_no"] for a in _k}

OKUMA = []
for a in _k + _e:
    r = uretec(TOHUM + 7000 + a["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            a["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        if a["abone_no"] not in GECERLI:
            r()
            continue
        OKUMA.append({"abone_no": a["abone_no"], "bolge": a["bolge"],
                      "donem": DONEM[d], "tuketim_m3": t})
        r()


def basamak(v):
    return 1 + sum(1 for t in TARIFE if v >= t)


def ortala(f):
    v = [o["tuketim_m3"] for o in OKUMA if f(o)]
    return sum(v) / len(v), len(v)


SERI = []
for ad, _, _ in BOLGE:
    d, n = ortala(lambda o, x=ad: o["bolge"] == x)
    SERI.append((ad, d, n))
for bs in (2, 3, 4):
    d, n = ortala(lambda o, x=bs: basamak(o["tuketim_m3"]) == x)
    SERI.append((f"basamak{bs}", d, n))

# Renk kanali: ton ailesi (0/1) ve aydinlik basamagi (0-8). Palet iki aileden
# dorder basamak seciyor; bu, sekiz seri icin dogal bir varsayilan dagilimdir.
BASAMAK_SAYISI, AYIRMA, SEKIL_SAYISI = 9, 2, 4
PALET = [(i // 4, [1, 3, 5, 7][i % 4]) for i in range(len(SERI))]


def tam_cozucu(x, y):
    """Iki seri ayirt edilir mi: ton farkli ya da basamak farki AYIRMA kadar."""
    return x[0] != y[0] or abs(x[1] - y[1]) >= AYIRMA


def daralmis_cozucu(x, y):
    """Ton ayrimini okumayan cozucu yalniz aydinlik basamagini gorur."""
    return abs(x[1] - y[1]) >= AYIRMA


def ayirt_edilebilir(kod, cozucu):
    """Ayirt edilemeyen seriler tek kume olur; kume sayisi geri okunan seridir."""
    kok = list(range(len(kod)))

    def bul(i):
        while kok[i] != i:
            kok[i] = kok[kok[i]]
            i = kok[i]
        return i
    for i in range(len(kod)):
        for j in range(i + 1, len(kod)):
            if not cozucu(kod[i], kod[j]):
                kok[bul(i)] = bul(j)
    return len({bul(i) for i in range(len(kod))})


print(f"{len(SERI)} seri, hepsi ayni {len(OKUMA)} okumanin alt kumesi")
print("seri        gozlem  ortalama  ton  basamak")
for (ad, d, n), (t, bsm) in zip(SERI, PALET):
    print(f"{ad:11s} {n:6d} {d:9.3f} {t:4d} {bsm:8d}")
print(f"okuma cozunurlugu: iki aydinlik basamagi en az {AYIRMA} basamak "
      f"farkli olmadan ayirt edilemez")
print(f"tam cozucuyle ayirt edilebilir seri: "
      f"{ayirt_edilebilir(PALET, tam_cozucu)}/{len(SERI)}")
print(f"ton ayrimini okumayan cozucuyle: "
      f"{ayirt_edilebilir(PALET, daralmis_cozucu)}/{len(SERI)}")
```

```
8 seri, hepsi ayni 3199 okumanin alt kumesi
seri        gozlem  ortalama  ton  basamak
kuzey          921    20.210    0        1
guney          645    16.602    0        3
dogu           557    24.674    0        5
bati           476    13.778    0        7
merkez         600    22.396    1        1
basamak2      2209    17.506    1        3
basamak3       692    30.009    1        5
basamak4        58    44.171    1        7
okuma cozunurlugu: iki aydinlik basamagi en az 2 basamak farkli olmadan ayirt edilemez
tam cozucuyle ayirt edilebilir seri: 8/8
ton ayrimini okumayan cozucuyle: 4/8
```

Palet kusursuz görünüyor. Sekiz serinin sekizi de ayrı bir renk çifti almış, hiçbir çift
tekrarlanmamış ve tam çözücü sekizini de ayırt ediyor. Bu, paleti seçen kişinin gördüğü sonuçtur.

Alt satır aynı paletin ikinci çözücü altında ne olduğunu söylüyor: **8 seriden 4'ü** kalıyor.
Kuzey ile merkez aynı aydınlık basamağını taşıyor, güney ile ikinci tarife basamağı aynı, doğu ile
üçüncüsü aynı, batı ile dördüncüsü aynı. Dört seri çifti tek bir seriye iniyor ve her çiftin hangi
üyesinin okunduğu belirsiz kalıyor. Rapor, 3.199 okumanın sekiz alt kümesini gösterdiğini
sanıyorken dördünü gösteriyor.

Bu bir palet kusuru değil, bir **yapı** kusurudur. Palet iki ton ailesini dört aydınlık basamağıyla
çaprazlamış ve sekiz gözü doldurmuş; ton ailesi okunamadığında çapraz tabloda sütun kalıyor, satır
kalmıyor. Renk tek kodlama kanalıysa bu sonuç kaçınılmazdır ve paletteki renk sayısını artırmak
işe yaramaz.

#### Yedekli Kodlama Nereye Eklenir

```python
# yedek.py — sekil kanali eklenir. Nereye eklendigi sonucu belirler.
SIRAYLA = [(p[0], p[1], i % SEKIL_SAYISI) for i, p in enumerate(PALET)]
CAKISMAYA = [(p[0], p[1], i // SEKIL_SAYISI) for i, p in enumerate(PALET)]


def sekilli(x, y):
    return daralmis_cozucu(x, y) or x[2] != y[2]


print("katman                        tam cozucu  daralmis cozucu")
for ad, kod, coz in (("yalniz renk", PALET, daralmis_cozucu),
                     ("renk + sekil (sirayla)", SIRAYLA, sekilli),
                     ("renk + sekil (cakismaya)", CAKISMAYA, sekilli)):
    tam = ayirt_edilebilir([k[:2] for k in kod], tam_cozucu)
    print(f"{ad:28s} {tam:11d} {ayirt_edilebilir(kod, coz):16d}")
print(f"sirayla dagitilan sekil {SEKIL_SAYISI} tanedir ve {SEKIL_SAYISI}. seride "
      f"bastan basliyor; renk cakismasi da {SEKIL_SAYISI} seri arayla oldugu "
      f"icin iki cakisma ust uste geliyor")
```

```
katman                        tam cozucu  daralmis cozucu
yalniz renk                            8                4
renk + sekil (sirayla)                 8                4
renk + sekil (cakismaya)               8                8
sirayla dagitilan sekil 4 tanedir ve 4. seride bastan basliyor; renk cakismasi da 4 seri arayla oldugu icin iki cakisma ust uste geliyor
```

Orta satır bu dersin en pahalı bulgusudur. Şekil kanalı eklendi, dört ayrı şekil serilere sırayla
dağıtıldı ve daralmış çözücünün okuduğu sayı **dörtte kaldı**. Yedekli kodlama eklendi ama hiçbir
şey düzelmedi.

Nedeni son satırdadır. Şekiller dört tanedir ve beşinci seride başa döner; paletin renk çakışması
da tam dört seri arayladır. İki dönüş üst üste gelince aynı çakışan çiftler bu kez hem aynı
basamağı hem aynı şekli paylaşır. İkinci kanal, birincinin çakıştığı yerde birlikte çakışıyor.

Üçüncü satır düzeltmeyi veriyor: şekil, sıraya göre değil **çakışmaya göre** dağıtıldığında —
yani ilk dört seri bir şekil, sonraki dört seri başka bir şekil aldığında — daralmış çözücü de
sekizini ayırt ediyor. Kullanılan şekil sayısı dörtten ikiye **indi** ve sonuç iyileşti. Yedekli
kodlamanın ölçüsü kanal sayısı değil, kanalın nerede değiştiğidir.

#### Gösterge Kutusu ve Geçiş Sayısı

```python
# gecis.py — gosterge kutusu okuyucuyu gosterimle kutu arasinda gidip
# getirir. Sag kenar konumu ucuncu bir kanaldir ve sirayi tasir.
SATIR = 12
enaz, encok = min(d for _, d, _ in SERI), max(d for _, d, _ in SERI)
konum = [round((d - enaz) / (encok - enaz) * (SATIR - 1)) for _, d, _ in SERI]
cakisan = sum(1 for i in range(len(SERI)) for j in range(i + 1, len(SERI))
              if konum[i] == konum[j])
tekil = [i for i in range(len(SERI))
         if all(konum[i] != konum[j] for j in range(len(SERI)) if j != i)]
print(f"sag kenar konumlari ({SATIR} satir): {konum}")
print(f"{len(SERI)} serinin {len(tekil)} tanesinin konumu tekil, "
      f"{cakisan} cift ayni satira dusuyor")
kutu = 2 * len(SERI)
sirali = 2 + 2 * (len(SERI) - len(tekil))
print("duzen                      gecis sayisi  ayirt edilebilir seri")
for ad, g, s in (("gosterge kutusu", kutu, ayirt_edilebilir(CAKISMAYA, sekilli)),
                 ("sirali gosterge kutusu", sirali,
                  ayirt_edilebilir(CAKISMAYA, sekilli)),
                 ("dogrudan etiketleme", 0, len(SERI))):
    print(f"{ad:26s} {g:13d} {s:22d}")
```

```
sag kenar konumlari (12 satir): [2, 1, 4, 0, 3, 1, 6, 11]
8 serinin 6 tanesinin konumu tekil, 1 cift ayni satira dusuyor
duzen                      gecis sayisi  ayirt edilebilir seri
gosterge kutusu                       16                      8
sirali gosterge kutusu                 6                      8
dogrudan etiketleme                    0                      8
```

Ayırt edilebilirlik gerekli koşuldur, yeterli değildir. Sekiz seri birbirinden ayrılıyor olabilir
ve okuyucu yine de hangisinin hangi bölge olduğunu bilmez; bunu ancak gösterge kutusuna bakarak
öğrenir. Ölçü burada **geçiş sayısıdır**: bakışın gösterimle gösterge kutusu arasında kaç kez gidip
geldiği.

Sıradan bir gösterge kutusu her seri için bir gidiş bir geliş ister; sekiz seri **16 geçiş** eder.
Gösterge kutusundaki sıra serilerin sağ kenardaki sırasıyla aynı yapıldığında okuyucu tek bir
eşlemeyle bütün listeyi çözer ve geçiş **6**'ya iner. Kalan dördü, sağ kenarda aynı satıra düşen
tek çiftten gelir: 12 satırın çözünürlüğünde güney ile ikinci tarife basamağı ayrılamıyor ve o iki
seri tek tek eşleştirilmek zorunda.

Doğrudan etiketleme geçiş sayısını **sıfıra** indirir, çünkü ad serinin bittiği yere yazılır ve
eşleme diye bir iş kalmaz. Sıralama ve doğrudan etiketleme de birer yedekli kodlama katmanıdır;
farkları, ayırt edilebilirliği değil **tanımayı** taşımalarıdır. Üç düzen de aynı sekiz seriyi
ayırt ediyor, ama okuyucudan istedikleri iş 16, 6 ve 0.

#### Tek Gösterimin Tavanı

```python
# tavan.py — kac seri tek gosterimde tasinabilir.
AYRIK_BASAMAK = len(range(0, BASAMAK_SAYISI, AYIRMA))
TAVAN = AYRIK_BASAMAK * SEKIL_SAYISI
print(f"{BASAMAK_SAYISI} aydinlik basamagindan {AYIRMA} aralikla "
      f"{AYRIK_BASAMAK} tanesi ayirt edilebilir; {SEKIL_SAYISI} sekille "
      f"tavan {TAVAN} seri")
print("seri  ayirt edilebilir  parca")
for n in (8, 12, 16, 20, 24, 30, 40):
    kod = [(0, (i % AYRIK_BASAMAK) * AYIRMA,
            (i // AYRIK_BASAMAK) % SEKIL_SAYISI) for i in range(n)]
    e = ayirt_edilebilir(kod, sekilli)
    print(f"{n:4d} {e:17d} {max(1, -(-n // TAVAN)):6d}")
print(f"tek gosterimde en cok {TAVAN} seri ayirt edilebiliyor; uzerinde "
      f"gosterim parcalanmadan seri sayisi geri okunamiyor")
```

```
9 aydinlik basamagindan 2 aralikla 5 tanesi ayirt edilebilir; 4 sekille tavan 20 seri
seri  ayirt edilebilir  parca
   8                 8      1
  12                12      1
  16                16      1
  20                20      1
  24                20      2
  30                20      2
  40                20      2
tek gosterimde en cok 20 seri ayirt edilebiliyor; uzerinde gosterim parcalanmadan seri sayisi geri okunamiyor
```

Tavan iki sayıdan çıkar. Dokuz aydınlık basamağından iki basamaklık çözünürlükle beşi ayırt
edilebilir; dört şekille çarpımı **20**'dir. Yirmiye kadar her seri ayırt ediliyor, 24 seride sayı
20'de kalıyor ve dördü okunamaz oluyor; 40 seride hâlâ 20.

Bu, gösterimin en sert sınırıdır ve tasarımla aşılamaz. Yirmiden çok seri taşınacaksa gösterim
parçalanır: seriler tavanı geçmeyen gruplara bölünür ve her grup kendi gösteriminde çizilir.
Bedeli, gruplar arası karşılaştırmanın artık aynı gösterimden okunamamasıdır. Konunun kuralı
buradan çıkar — **bir gösterim, ayırt edilebilir seri sayısı kadar seri taşır; fazlası çizilmiş ama
raporlanmamış sayılır**.

#### Özet

- Renk bir ad değil iki sayıdır ve modelde ton ailesi ile aydınlık basamağından oluşur; okuma
  çözünürlüğü iki basamaktır.
- İki ton ailesini dört aydınlık basamağıyla çaprazlayan varsayılan palet tam çözücüyle sekiz
  seriyi ayırt ediyor, ton ayrımını okumayan çözücüyle dördünü.
- Şekil kanalı sırayla dağıtıldığında sayı dörtte kalıyor, çünkü şekil dört seride başa dönüyor ve
  renk çakışmasıyla üst üste geliyor; çakışmaya göre dağıtıldığında iki şekille sekize çıkıyor.
- Ayırt edilebilirlik tanımayı vermez: aynı sekiz seri gösterge kutusuyla 16, sıralı göstergeyle 6,
  doğrudan etiketlemeyle 0 geçiş istiyor.
- Tek gösterimin tavanı ayırt edilebilir basamak sayısı ile şekil sayısının çarpımıdır; beş basamak
  ve dört şekille 20 seri, üzerinde gösterim parçalanmak zorundadır.

#### Sonraki Adım

Sekiz derste tek bir gösterim doğru kuruldu. Soru türüyle kanal eşleştirildi, dağılımın biçimi
sayıya indi, taban çizgisi olmayan dilim ölçüldü, ilişkinin okunamaz olduğu nokta bulundu, zaman
ekseninde eğim ve mevsim ayrıldı, alan kanalının ağırlığı sayıldı, dört çarpıtma tek tek basıldı ve
son olarak kaç serinin ayırt edildiği ölçüldü. Hepsinde ölçü aynıydı: gösterim, okuyucunun geri
okuyabildiği sayı kadar doğrudur.

Bir rapor ise tek gösterimden oluşmaz. Okuyucu bir bakışta birden çok sayıyla karşılaşır ve bunlar
farklı kanallarla kodlanmış, farklı çözünürlüklerde durur. Sonraki konu ölçüyü buraya taşır: bir
bakışta kaç ölçüt geri okunabiliyor, aynı ölçüt kaç yoldan ölçtüğünü oynatmadan oynatılabiliyor ve
bir rapor ikinci kez üretildiğinde aynı sayıyı veriyor mu. Okuyucu artık bir sayıyı geri okuyan
kişi değil, o sayıyla bir karar veren kişidir.

## Raporlama

Bulgunun karar verecek kişiye taşınırken ne kadarının sağ kaldığının sayılması: gösterge tablosundan geri okunabilen ölçüt sayısı ve düzenin desteklediği karar, bir ölçütün ölçtüğü şeyi oynatmadan kaç yoldan oynatılabildiği, veri modeli ile veri kaynağı arasındaki yenileme gecikmesinin rapora yazdığı sayı, bulgudan öneriye giden anlatının kaç adımda kurulduğu ve atlanan adımın okuyucuda bıraktığı boşluk, aynı raporu ikinci kez üretmek için sabitlenmesi gereken kalem sayısı.

### Gösterge Tablosu Tasarımı

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/gosterge-tablosu-tasarimi

Aynı on iki ölçüt üç düzende kodlanır ve her düzende kaçının bir bakışta geri okunduğu sayılır: eklenme sırasına dizilmiş düzende 1, her ölçüte bir gösterim verildiğinde 2, karara göre bölünmüş düzende 9 ölçüt geri okunur. Desteklenen karar sayısı sırasıyla 0, 0 ve 3'tür; bakış bütçesi on ikiye çıkarıldığında ilk iki düzen 2 ve 6 ölçüt geri okutur ama yine hiçbir kararı desteklemez. Uzunluk kanalında ayrı ayrı yüzde 1,50 ve yüzde 3,08 hatayla geri okunan iki oranın arasındaki 1,861'lik fark yüzde 34,3 hatayla 2,500 okunur.

Sekiz derste tek bir gösterim doğru kuruldu: değer bir kanala kodlandı, okuyucu onu geri okudu,
aradaki hata sayıldı. Bir rapor tek bir gösterimden oluşmuyor. Karar verecek kişi bir bakışta
birden çok sayıyla karşılaşıyor; her sayı doğru kodlanmış olabilir ve sayfa yine de hiçbir kararı
desteklemeyebilir.

Bu dersin ölçüsü iki sayıdır: **bir bakışta geri okunabilen ölçüt sayısı** ve **desteklenen karar
sayısı**. Buradaki gösterge tablosu bir işletim aracı değildir. Gözlemlenebilirlik ve İşletim
kursunun gösterge tablosu dersi nöbetçinin ekranını ele almış, "panel bir soruya yanıt veriyor mu"
diye denetlemişti; uyarı kuralı ve eşik tasarımı orada kuruldu ve **burada tekrarlanmaz**. Soru
burada başkadır: sayı kanalından geri okunabiliyor mu ve onu karara çevirecek bir dayanağı var mı.

- **RP1.** Küme K03'ten devralınır ve **kurgudur**: 3.199 satırlık okuma tablosu, 1.329 abone, üç
  dönem, beş bölge, tarife basamakları 10/25/40 m³. Tohum koda yazılıdır.
- **RP2.** Rapor da **kurgudur**: on iki ölçüt ve üç karar, ölçüm ağını yürüten birimin dönem sonu
  raporundan gelir.
- **RP3.** Bir bakış **beş göz duraktır**. Bu bir karardır; aynı denetim üç, beş, sekiz ve on iki
  bütçesiyle yeniden koşturulur.
- **RP4.** Okuma çözünürlüğü çözücüde yazılıdır: metin çubuğu **tam hücre** sayılır (20 hücre),
  renk basamağı ancak basamağın **orta değerine** çözülür (5 basamak), çıplak sayı iki ondalıkla
  basılır. Kanal büyüklüğü, etiket işareti taşır.
- **RP5.** Hata eşiği **yüzde 5**'tir; gerekçesi raporun en küçük karşılaştırmasının bu büyüklükte
  olmasıdır. Ölçek tavanı da bir karardır ve ölçüt tanımıyla birlikte yazılır.
- **RP6.** Bir ölçüt üç koşul birden sağlanırsa **geri okunmuş** sayılır: bakış bütçesinin içinde
  olmak, hatası eşiğin altında kalmak, bir dayanağı olmak.

#### Ölçüt, Kanal ve Dayanak

On iki ölçütün tamamı kurgu kümeden hesaplanır. Üçü K05'ten gelir: doğu ortalaması 24,674 m³,
kuzeyle farkı 4,463 m³, hiç okuması olmayan 69 abone.

```python
# rapor.py — MODELDIR. K03'un kurgu kumesi ayni tohumla uretilir; uzerine kurgu
# bir donem sonu raporu kurulur. Her olcut bir kanala kodlanir, okuyucu geri okur.
import math

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26),
         ("bati", 0.14, 14), ("merkez", 0.18, 23)]


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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, ELENEN = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (ELENEN if r() < 0.046 else ABONE).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {k["abone_no"] for k in ABONE}
OKUMA = {}
for k in ABONE + ELENEN:
    r = uretec(TOHUM + 7000 + k["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            k["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100
            + 0.5) / 100
        if k["abone_no"] in GECERLI:
            OKUMA.setdefault(k["abone_no"], {})[d + 1] = t
        r()


def okumalar(donem, bolge=None):
    return [OKUMA[k["abone_no"]][donem] for k in ABONE
            if (bolge is None or k["bolge"] == bolge)
            and donem in OKUMA.get(k["abone_no"], {})]


def eksik_oran(donem, bolge=None):
    kume = [k for k in ABONE if bolge is None or k["bolge"] == bolge]
    return sum(1 for k in kume if donem not in OKUMA.get(k["abone_no"], {})) / len(kume)


d2, d3 = okumalar(2), okumalar(3)
sirali = sorted(d3)
tum = {b[0]: sum((okumalar(d, b[0]) for d in (1, 2, 3)), []) for b in BOLGE}
ort = {b: sum(v) / len(v) for b, v in tum.items()}
eksik = {b[0]: eksik_oran(3, b[0]) for b in BOLGE}
en_eksik = max(eksik, key=lambda b: eksik[b])

# (kod, ad, deger, birim, olcek tavani). Tavan bir karardir; kanal ona gore kodlar.
OLCUT = [
    ("O1", "donem 3 toplam tuketim", sum(d3), "m3", 20000.0),
    ("O2", "abone basina ortalama", sum(d3) / len(d3), "m3", 40.0),
    ("O3", "ortanca tuketim",
     (sirali[418] + sirali[419]) / 2, "m3", 40.0),
    ("O4", "eksik okuma orani", eksik_oran(3) * 100, "%", 50.0),
    ("O5", "sifir okuma sayisi", sum(1 for v in d3 if v == 0.0), "adet", 50.0),
    ("O6", "40 m3 ustu abone orani", sum(1 for v in d3 if v > 40) / len(d3) * 100, "%", 5.0),
    ("O7", "en yuksek bolge ortalamasi", max(ort.values()), "m3", 40.0),
    ("O8", "dogu-kuzey ortalama farki", ort["dogu"] - ort["kuzey"], "m3", 10.0),
    ("O9", "donem 2-3 degisim orani",
     (sum(d3) / len(d3) - sum(d2) / len(d2)) / (sum(d2) / len(d2)) * 100, "%", 20.0),
    ("O10", "10-25 basamagindaki abone orani",
     sum(1 for v in d3 if 10 <= v < 25) / len(d3) * 100, "%", 100.0),
    ("O11", "hic okumasi olmayan abone",
     sum(1 for k in ABONE if not OKUMA.get(k["abone_no"])), "adet", 100.0),
    ("O12", "en yuksek eksik okuma orani (" + en_eksik + ")", eksik[en_eksik] * 100, "%", 50.0),
]
AD = {o[0]: o[1] for o in OLCUT}
DEG = {o[0]: o[2] for o in OLCUT}
BIRIM = {o[0]: o[3] for o in OLCUT}
TAVAN = {o[0]: o[4] for o in OLCUT}

print(f"kurgu kume (tohum {TOHUM}): {len(ABONE)} abone, "
      f"{sum(len(v) for v in OKUMA.values())} okuma, {len(BOLGE)} bolge")
print(f"rapor donem 3 uzerine kurulur: {len(d3)} okuma\n")
for k, ad, d, br, tv in OLCUT:
    print(f"  {k:<4} {ad:<38} {d:>10.3f} {br}")
```

```
kurgu kume (tohum 20260218): 1329 abone, 3199 okuma, 5 bolge
rapor donem 3 uzerine kurulur: 838 okuma

  O1   donem 3 toplam tuketim                  15353.190 m3
  O2   abone basina ortalama                      18.321 m3
  O3   ortanca tuketim                            17.845 m3
  O4   eksik okuma orani                          36.945 %
  O5   sifir okuma sayisi                         28.000 adet
  O6   40 m3 ustu abone orani                      0.955 %
  O7   en yuksek bolge ortalamasi                 24.674 m3
  O8   dogu-kuzey ortalama farki                   4.463 m3
  O9   donem 2-3 degisim orani                    -6.910 %
  O10  10-25 basamagindaki abone orani            74.105 %
  O11  hic okumasi olmayan abone                  69.000 adet
  O12  en yuksek eksik okuma orani (bati)         38.806 %
```

#### Aynı Sayının Üç Kanalı

Eşiğin gerekçesi de bu listeden çıkar: raporun taşıdığı en küçük karşılaştırma, küme geneli eksik
okuma oranı ile en yüksek bölgenin oranı arasındadır.

```python

# Her kanal bir kodlayici ve bir cozucu ciftidir; okuma cozunurlugu cozucude yazilidir.
HUCRE, BASAMAK, ESIK = 20, 5, 0.05


def kodla(kanal, x, tavan):
    if kanal == "sayi":
        return round(abs(x), 2)
    if kanal == "uzunluk":
        return max(0, min(HUCRE, int(abs(x) / tavan * HUCRE + 0.5)))
    return max(0, min(BASAMAK - 1, int(abs(x) / tavan * BASAMAK)))


def coz(kanal, kod, tavan):
    if kanal == "sayi":
        return kod
    if kanal == "uzunluk":
        return kod * tavan / HUCRE
    return (kod + 0.5) * tavan / BASAMAK


def hata(kanal, k):
    x, tavan = DEG[k], TAVAN[k]
    return abs(coz(kanal, kodla(kanal, x, tavan), tavan) - abs(x)) / abs(x)


en_kucuk = (DEG["O12"] - DEG["O4"]) / DEG["O4"]
print(f"raporun tasidigi en kucuk karsilastirma: kume geneli eksik okuma orani "
      f"{DEG['O4']:.3f} ile\n{AD['O12']} {DEG['O12']:.3f} arasinda yuzde "
      f"{en_kucuk * 100:.1f}. Geri okuma hatasi esigi bu yuzden yuzde {ESIK * 100:.0f}.\n")
print(f"{'olcut':<6}{'kaynaktaki sayi':>16}  {'kanal':<9}{'kodlanan':>9}"
      f"{'geri okunan':>13}{'hata %':>9}")
for k in ("O9", "O12"):
    for kanal in ("sayi", "uzunluk", "renk"):
        print(f"{k:<6}{DEG[k]:>16.3f}  {kanal:<9}{kodla(kanal, DEG[k], TAVAN[k]):>9}"
              f"{coz(kanal, kodla(kanal, DEG[k], TAVAN[k]), TAVAN[k]):>13.3f}"
              f"{hata(kanal, k) * 100:>9.2f}")
```

```
raporun tasidigi en kucuk karsilastirma: kume geneli eksik okuma orani 36.945 ile
en yuksek eksik okuma orani (bati) 38.806 arasinda yuzde 5.0. Geri okuma hatasi esigi bu yuzden yuzde 5.

olcut  kaynaktaki sayi  kanal     kodlanan  geri okunan   hata %
O9              -6.910  sayi          6.91        6.910     0.00
O9              -6.910  uzunluk          7        7.000     1.31
O9              -6.910  renk             1        6.000    13.17
O12             38.806  sayi         38.81       38.810     0.01
O12             38.806  uzunluk         16       40.000     3.08
O12             38.806  renk             3       35.000     9.81
```

Çıplak sayının hatası sıfıra yakındır ve bu onu en iyi kanal yapmaz: hatasız okunan 38,81'in iyi
mi kötü mü olduğu kaynağın kendisinden okunamaz. Renk basamağı ise beş kovaya böldüğü için iki
ölçütte de eşiği aşıyor.

#### Üç Düzen, İki Sayı

Birinci düzen ölçütlerin eklenme sırasını korur ve hepsini çıplak sayı basar. İkincisi her ölçüte
bir gösterim ve bir dayanak verir, sırayı değiştirmez. Üçüncüsü kararlara göre bölümlere ayırır.

```python

# RP3: bir bakis bes gozduraktir; asagida dort butce icin ayni denetim kosar.
BUTCE = 5
KARAR = [("ek okuma turu hangi bolgeye", ("O4", "O12", "O8")),
         ("tarife basamagi siniri gozden gecirilsin mi", ("O3", "O6", "O10")),
         ("donem 3 toplamasi yenilensin mi", ("O5", "O9", "O11"))]
SIRA = ["O1", "O2", "O9", "O4", "O5", "O3", "O6", "O7", "O8", "O10", "O11", "O12"]
CESIT = {"O1": "uzunluk", "O2": "renk", "O9": "renk", "O4": "renk", "O5": "uzunluk",
         "O3": "uzunluk", "O6": "renk", "O7": "uzunluk", "O8": "sayi", "O10": "renk",
         "O11": "uzunluk", "O12": "renk"}
DUZEN = {
    "eklenme sirasi": [[(k, "sayi", k in ("O8", "O9")) for k in SIRA]],
    "her olcute bir gosterim": [[(k, CESIT[k], True) for k in SIRA]],
    "karara gore bolunmus": [[(k, "sayi" if k == "O8" else "uzunluk", True) for k in kk]
                             for _, kk in KARAR],
}


def denetle(bolumler, butce=BUTCE):
    """Bir olcut ancak uc kosul birden saglanirsa geri okunmus sayilir:
    bakis butcesinin icinde olmak, hatasi esigin altinda kalmak, dayanagi olmak."""
    okunan = [k for b in bolumler for k, kanal, dayanak in b[:butce]
              if dayanak and hata(kanal, k) <= ESIK]
    return okunan, [ad for ad, kk in KARAR if all(k in okunan for k in kk)]


print(f"{len(OLCUT)} olcut, {len(KARAR)} karar, hata esigi yuzde {ESIK * 100:.0f}. "
      f"Son dort sutun: geri okunan olcut / desteklenen karar.\n")
print(f"{'duzen':<24}{'gosterilen':>11}{'bakis':>7}"
      + "".join(f"{'butce ' + str(b):>10}" for b in (3, 5, 8, 12)))
for ad, bol in DUZEN.items():
    print(f"{ad:<24}{sum(len(b) for b in bol):>11}{len(bol):>7}" + "".join(
        f"{'/'.join(str(len(x)) for x in denetle(bol, b)):>10}" for b in (3, 5, 8, 12)))
```

```
12 olcut, 3 karar, hata esigi yuzde 5. Son dort sutun: geri okunan olcut / desteklenen karar.

duzen                    gosterilen  bakis   butce 3   butce 5   butce 8  butce 12
eklenme sirasi                   12      1       1/0       1/0       1/0       2/0
her olcute bir gosterim          12      1       1/0       2/0       4/0       6/0
karara gore bolunmus              9      3       9/3       9/3       9/3       9/3
```

Beş göz duraklık bütçede üç düzen sırasıyla 1, 2 ve 9 ölçüt geri okutuyor; desteklenen karar
sayısı 0, 0 ve 3.

Asıl bilgi son sütunda. Bütçe on ikiye çıkarıldığında, yani okuyucunun sayfayı baştan sona
taradığı varsayıldığında bile ilk iki düzen hiçbir kararı desteklemiyor. Eksik olan yer değil
**birlikteliktir**: bir karar üç ölçütün aynı anda dayanaklı ve eşik altı olmasını ister, ilk iki
düzende bu üçlü hiçbir bütçede tamamlanmaz. On iki ölçütün altısını geri okutan bir sayfa sıfır
karar destekleyebilir. Üçüncü düzenin bedeli de sayılıdır: dokuz ölçüt gösterir, üçünü dışarıda
bırakır ve bir yerine **üç bakış** ister.

#### Okuyucunun Gördüğü Sayfa

```python

# Okuyucunun gordugu sey basilir; "<" geri okunmus olcutu isaretler.
def cizgi(k, kanal):
    if kanal == "uzunluk":
        n = kodla("uzunluk", DEG[k], TAVAN[k])
        return "#" * n + "." * (HUCRE - n)
    if kanal == "renk":
        return f"basamak {kodla('renk', DEG[k], TAVAN[k]) + 1}/{BASAMAK}".ljust(HUCRE)
    return f"{DEG[k]:.2f} {BIRIM[k]}".ljust(HUCRE)


def bas(baslik, bolumler, adlar=None, kes=None):
    print(f"--- {baslik} " + "-" * (52 - len(baslik)))
    for i, b in enumerate(bolumler):
        if adlar:
            print(f"  [karar: {adlar[i]}]")
        for j, (k, kanal, dayanak) in enumerate(b[:kes] if kes else b):
            im = " <" if j < BUTCE and dayanak and hata(kanal, k) <= ESIK else ""
            print(f"  {j + 1:>2}. {AD[k]:<36}{cizgi(k, kanal)}"
                  f"{'  dayanak var' if dayanak else '  dayanak yok'}{im}")


bas("duzen 1 — eklenme sirasi (bakisa giren bes satir)",
    DUZEN["eklenme sirasi"], kes=BUTCE)
print()
bas("duzen 3 — karara gore bolunmus", DUZEN["karara gore bolunmus"],
    [k[0] for k in KARAR])

gercek = DEG["O12"] - DEG["O4"]
okunan = coz("uzunluk", kodla("uzunluk", DEG["O12"], TAVAN["O12"]), TAVAN["O12"]) - \
    coz("uzunluk", kodla("uzunluk", DEG["O4"], TAVAN["O4"]), TAVAN["O4"])
print(f"\nO4 hatasi yuzde {hata('uzunluk', 'O4') * 100:.2f}, "
      f"O12 hatasi yuzde {hata('uzunluk', 'O12') * 100:.2f} — ikisi de esigin altinda.")
print(f"aradaki fark: kaynakta {gercek:.3f}, geri okunan {okunan:.3f}, "
      f"farkin hatasi yuzde {abs(okunan - gercek) / gercek * 100:.1f}")
```

```
--- duzen 1 — eklenme sirasi (bakisa giren bes satir) ---
   1. donem 3 toplam tuketim              15353.19 m3           dayanak yok
   2. abone basina ortalama               18.32 m3              dayanak yok
   3. donem 2-3 degisim orani             -6.91 %               dayanak var <
   4. eksik okuma orani                   36.95 %               dayanak yok
   5. sifir okuma sayisi                  28.00 adet            dayanak yok

--- duzen 3 — karara gore bolunmus ----------------------
  [karar: ek okuma turu hangi bolgeye]
   1. eksik okuma orani                   ###############.....  dayanak var <
   2. en yuksek eksik okuma orani (bati)  ################....  dayanak var <
   3. dogu-kuzey ortalama farki           4.46 m3               dayanak var <
  [karar: tarife basamagi siniri gozden gecirilsin mi]
   1. ortanca tuketim                     #########...........  dayanak var <
   2. 40 m3 ustu abone orani              ####................  dayanak var <
   3. 10-25 basamagindaki abone orani     ###############.....  dayanak var <
  [karar: donem 3 toplamasi yenilensin mi]
   1. sifir okuma sayisi                  ###########.........  dayanak var <
   2. donem 2-3 degisim orani             #######.............  dayanak var <
   3. hic okumasi olmayan abone           ##############......  dayanak var <

O4 hatasi yuzde 1.50, O12 hatasi yuzde 3.08 — ikisi de esigin altinda.
aradaki fark: kaynakta 1.861, geri okunan 2.500, farkin hatasi yuzde 34.3
```

Son iki satır düzenin kapatmadığı açığı gösteriyor. Birinci bölümdeki iki oran uzunluk kanalında
ayrı ayrı yüzde 1,50 ve yüzde 3,08 hatayla, ikisi de eşiğin çok altında geri okunuyor. Ama
okuyucunun o bölümde yaptığı iş tek tek değerleri okumak değil **aralarındaki farkı** okumaktır.
Kaynakta 1,861 puan olan fark 2,500 okunuyor: yüzde 34,3 hata. İki küçük hata aynı yöne düşmediği
için farkta büyüyor.

Bu, eşiğin yanlış seçildiğini değil ölçülen şeyin değiştiğini gösterir. Bir karar bir farka
dayanıyorsa geri okuma hatası **farkın üzerinde** ölçülmelidir: raporun en küçük karşılaştırması
yüzde 5 iken kanalın farkta ürettiği yüzde 34,3 o karşılaştırmayı çevirecek büyüklüktedir.

#### Özet

- Bir ölçüt üç koşul birden sağlanırsa geri okunmuş sayılır: bakış bütçesinin içinde olmak, geri
  okuma hatasının eşiğin altında kalması, bir dayanağının bulunması.
- Aynı on iki ölçüt beş göz duraklık bakışta üç düzende sırasıyla 1, 2 ve 9 kez geri okunuyor;
  desteklenen karar sayısı 0, 0 ve 3'tür. Bütçe on ikiye çıkarıldığında ilk iki düzen 2 ve 6 ölçüt
  geri okutur ama yine hiçbir kararı desteklemez.
- Karara göre bölünmüş düzenin bedeli sayılıdır: dokuz ölçüt gösterir, üçünü dışarıda bırakır ve
  bir yerine üç bakış ister. Çıplak sayı hatasız okunur ama dayanaksız olduğu için karar beslemez.
- Tek tek yüzde 1,50 ve yüzde 3,08 hatayla okunan iki oranın arasındaki 1,861'lik fark 2,500
  okunur; farkın hatası yüzde 34,3'tür.

#### Sonraki Adım

Bu ders ölçütleri verilmiş kabul etti ve yalnız sayfada nasıl durduklarını ölçtü. Ölçütün kendisi
sorgulanmadı: eksik okuma oranı neyin oranıdır, paydasında kim vardır, bir okuma hangi anda okuma
sayılır. Bir ölçüt karar üretmeye başladığı andan itibaren ölçtüğü şeyi oynatmadan da
oynatılabilir hâle gelir. Sonraki ders bunu sayar: aynı eksik okuma oranı, ölçüm ağında hiçbir şey
değişmeden kaç yoldan oynatılabiliyor, her yol ölçütü ne kadar oynatıyor ve ölçüt yeniden
tanımlandığında bu yolların kaçı kapanıyor.

### Anahtar Başarım Göstergeleri

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/anahtar-basarim-gostergeleri

Bir ölçütün oynatılabilirliği sayılır: yüzde 60,948 olan okuma kapsama oranı, ölçüm ağında tek bir okuma değişmeden altı yoldan denenir ve beşi oynama eşiğini aşar — payda daraltma 64,286, sıfır okumayı sayma 63,055, pencere genişletme 81,791, yuvarlama 61,000, hedefe oranlama 93,766. Altı maddelik yeniden tanım eşiği aşan yol sayısını 5'ten 0'a indirir; eşiği aşmayan yol kapanmış sayılmaz, çünkü 0,037 puanlık oynaması tanımdan değil bölge boylarından gelir. Ölçüm ağında 106 okuma gerçekten kaybolduğunda sıkı tanım 60,948'den 53,273'e iner, gevşek tanımın altı yolundan ikisi aynı düşüşte sayıyı düşüş öncesi değerin üstünde tutar.

Önceki ders on iki ölçütü verilmiş kabul etti ve yalnız sayfadaki yerlerini ölçtü. Ölçütlerden
biri, eksik okuma oranı, üç kararın ikisini besliyordu. Bir sayı karar üretmeye başladığı andan
itibaren yeni bir özellik kazanır: onu rapor eden kişi, **ölçtüğü şeye hiç dokunmadan** sayıyı
oynatabilir.

Bu dersin ölçüsü **oynatılabilirliktir (gameability)**: bir ölçüt, ölçtüğü şeyi hiç oynatmadan kaç
yoldan ve kaç puan oynatılabiliyor. Ölçüt tasarımının kendisi burada kurulmaz. Hata bütçesi, servis
düzeyi göstergesi ve işletim ölçütlerinin seçimi Gözlemlenebilirlik ve Güvenilirlik, Sistem Yönetimi ile DevOps Kültürü ve
Temelleri kurslarında kuruldu; burada tek bir ölçüt alınır ve yalnız tanımının ne kadar gevşek olduğu
sayılır.

- **RP7.** Küme K03'ten devralınır ve **kurgudur**: 3.199 satırlık okuma tablosu, 1.329 abone, üç
  dönem, beş bölge. Tohum koda yazılıdır.
- **RP8.** Ölçüt de **kurgudur**: **okuma kapsama oranı**, dönem 3'te sıfırdan farklı okuması olan
  abonenin dönem başında kayıtlı aboneye oranı.
- **RP9.** Oynatma ölçüm ağına **hiç dokunmaz**: hiçbir okuma eklenmez, silinmez, değiştirilmez.
  Yalnız ölçütün tanımı ya da sunumu değişir.
- **RP10.** Altı yolun her biri tek başına savunulabilir; hiçbiri veri uydurmaz.
- **RP11.** Oynama eşiği **0,05 puandır**. Gerekçesi paydadaki tek bir abonenin 0,075 puan
  etmesidir; eşik bir aboneden küçük her oynamayı eler.
- **RP12.** Ölçüm ağındaki gerçek düşüş de **kurgudur** ve ayrı bir tohumla üretilir.

#### Ölçüt

```python
# olcut.py — MODELDIR. K03'un kurgu kumesi ayni tohumla uretilir; uzerine tek bir
# anahtar basarim gostergesi (key performance indicator) kurulur.
import math

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26),
         ("bati", 0.14, 14), ("merkez", 0.18, 23)]


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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, ELENEN = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (ELENEN if r() < 0.046 else ABONE).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {k["abone_no"] for k in ABONE}
OKUMA = {}
for k in ABONE + ELENEN:
    r = uretec(TOHUM + 7000 + k["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            k["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100
            + 0.5) / 100
        if k["abone_no"] in GECERLI:
            OKUMA.setdefault(k["abone_no"], {})[d + 1] = t
        r()

HEDEF = 65.0


def kapsama(donem=3, sifir_sayilir=False, payda_daralt=False, genis=False, kume=None):
    kume = kume if kume is not None else OKUMA
    payda = [k for k in ABONE if not payda_daralt or kume.get(k["abone_no"])]
    pay = 0
    for k in payda:
        o = kume.get(k["abone_no"], {})
        dl = (2, 3) if genis else (donem,)
        if any(d in o and (sifir_sayilir or o[d] > 0.0) for d in dl):
            pay += 1
    return pay / len(payda) * 100, pay, len(payda)


taban, pay0, payda0 = kapsama()
print(f"kurgu kume (tohum {TOHUM}): {len(ABONE)} abone, "
      f"{sum(len(v) for v in OKUMA.values())} okuma")
print(f"olcut — okuma kapsama orani (donem 3): {pay0} / {payda0} = {taban:.3f}")
```

```
kurgu kume (tohum 20260218): 1329 abone, 3199 okuma
olcut — okuma kapsama orani (donem 3): 810 / 1329 = 60.948
```

Ölçütün ölçmek istediği şey tek cümleyle söylenebilir: ölçüm ağı dönem içinde abonelerin ne
kadarına ulaşabildi. Bu cümle bir sayıya çevrilirken en az dört karar veriliyor ve hiçbiri
cümlenin içinde yazılı değil. Kim paydadadır, sıfır bir okuma mıdır, dönem ne zaman kapanır,
sayı kaç ondalıkla yazılır.

#### Altı Yol

Her yol bu kararlardan birini gevşetir. Hiçbiri veri uydurmaz, hiçbiri ölçüm ağına dokunmaz ve
her biri bir toplantıda tek cümleyle savunulabilir.

```python

# Alti yol. Hicbiri olcum agina dokunmaz: okuma eklenmez, silinmez, degistirilmez.
# Her yol tanimin ya da sunumun bir maddesini gevsetir ve her biri savunulabilir.
ESIK_PUAN = 0.05
SIKI = {"payda", "sifir", "pencere", "sunum", "toplulastirma", "hedef"}


def bolge_orani(kume):
    o = []
    for b, _, _ in BOLGE:
        alt = [k for k in ABONE if k["bolge"] == b]
        o.append(sum(1 for k in alt if kume.get(k["abone_no"], {}).get(3, 0.0) > 0.0)
                 / len(alt) * 100)
    return sum(o) / len(o)


def yollar(kume, sabit=frozenset()):
    t = kapsama(kume=kume)[0]
    y = [("payda daraltma", "payda", kapsama(kume=kume, payda_daralt=True)[0], "%"),
         ("sifir okumayi okuma saymak", "sifir",
          kapsama(kume=kume, sifir_sayilir=True)[0], "%"),
         ("pencere genisletme (donem 2 ya da 3)", "pencere",
          kapsama(kume=kume, genis=True)[0], "%"),
         ("tam sayiya yuvarlama", "sunum", float(round(t)), "%"),
         ("agirliksiz bolge ortalamasi", "toplulastirma", bolge_orani(kume), "%"),
         ("hedefe oranlayarak sunmak", "hedef", t / HEDEF * 100, "hedefin %'i")]
    return [(ad, md, t if md in sabit else v, br) for ad, md, v, br in y]


print(f"taban olcut {taban:.3f}, hedef {HEDEF:.0f}. Paydadaki tek abone "
      f"{100 / payda0:.3f} puan ettigi icin oynama esigi {ESIK_PUAN} puandir.\n")
print(f"{'yol':<38}{'rapora yazilan':>15}{'oynama':>9}  birim")
for ad, md, v, br in yollar(OKUMA):
    print(f"{ad:<38}{v:>15.3f}{v - taban:>9.3f}  {br}")
bir = kapsama(sifir_sayilir=True, payda_daralt=True, genis=True)
print(f"{'ilk uc yol birlikte':<38}{bir[0]:>15.3f}{bir[0] - taban:>9.3f}  "
      f"%   ({bir[1]} / {bir[2]})")
```

```
taban olcut 60.948, hedef 65. Paydadaki tek abone 0.075 puan ettigi icin oynama esigi 0.05 puandir.

yol                                    rapora yazilan   oynama  birim
payda daraltma                                 64.286    3.338  %
sifir okumayi okuma saymak                     63.055    2.107  %
pencere genisletme (donem 2 ya da 3)           81.791   20.843  %
tam sayiya yuvarlama                           61.000    0.052  %
agirliksiz bolge ortalamasi                    60.985    0.037  %
hedefe oranlayarak sunmak                      93.766   32.818  hedefin %'i
ilk uc yol birlikte                            87.381   26.433  %   (1101 / 1260)
```

Ölçüm ağında tek bir okuma değişmeden aynı ölçüt yüzde 60,948 ile yüzde 93,766 arasında herhangi
bir yerde raporlanabiliyor. Yolların savunulabilirliği de gerçektir. Hiç okuması olmayan 69
aboneyi paydadan çıkarmak, bu abonelerin sayacına hiç ulaşılamadığı için makul görünür. Sıfır
okuma bir okumadır: sayaç okunmuş, tüketim çıkmamıştır. Dönem 2 ya da 3'te okuması olan aboneye
"ulaşıldı" demek de savunulabilir.

En büyük iki oynama en masum görünen iki yoldan geliyor. Pencere genişletme 20,843 puan, hedefe
oranlama 32,818 puan taşıyor. İkincisi sayının **birimini** değiştiriyor: yazılan 93,766 bir
kapsama oranı değil, hedefin yüzde kaçının gerçekleştiğidir. Aynı satırın altında birim yazmazsa
okuyucu bunu kapsamanın kendisi olarak geri okur.

Yollar birbirini dışlamıyor. İlk üçü birlikte uygulandığında rapora yazılan sayı, aynı birimde
kalarak, 87,381 oluyor: 1.101 abone 1.260 abonede. Üç ayrı toplantıda üç ayrı kişinin verdiği üç
savunulabilir karar, kimse bir şey uydurmadan ölçütü 26,433 puan yukarı taşıyor.

Yolların en zor fark edileni ilkidir ve nedeni önceki dersin kavramında yatıyor. Rapor satırında
yalnız "yüzde 64,286" yazıyorsa okuyucunun elinde payı 810, paydası 1.260 olan bir oranla payı
810, paydası 1.329 olan bir oranı ayırt edecek hiçbir şey yoktur. Oranın geri okunabilmesi
dayanağa değil, **payı ile paydasının yanında durmasına** bağlıdır; ikisi yazılmayan bir oran
kendi tanımını gizler.

#### Yeniden Tanım

Yeniden tanım altı maddedir ve her maddesi bir yolu bağlar. Sonra aynı altı yol yeniden denenir.

```python

# Yeniden tanim alti maddedir ve her maddesi bir yolu baglar: payda donem basinda
# kayitli abonedir, sifir okuma okuma sayilmaz, pencere donem 3'te kapanir, oran uc
# ondalikla yazilir, toplulastirma abone agirliklidir, hedefe oranlama yasaktir.
def dene(sabit, kume=OKUMA):
    t = kapsama(kume=kume)[0]
    return [(ad, abs(v - t) > ESIK_PUAN, v) for ad, md, v, br in yollar(kume, sabit)]


gevsek, siki = dene(frozenset()), dene(SIKI)
print(f"{'yol':<38}{'gevsek tanim':>16}{'siki tanim':>12}")
for (ad, ga, gv), (_, sa, sv) in zip(gevsek, siki):
    print(f"{ad:<38}{('oynadi ' + format(gv, '.3f')) if ga else 'oynamadi':>16}"
          f"{('oynadi ' + format(sv, '.3f')) if sa else 'oynamadi':>12}")
kapanmayan = [a for a in gevsek if not a[1]]
print(f"\nesigi asan yol: gevsek tanimda {sum(1 for a in gevsek if a[1])}, "
      f"siki tanimda {sum(1 for a in siki if a[1])} ({len(gevsek)} yol denendi)")
for ad, _, v in kapanmayan:
    print(f"kapanmadi, bu kumede islemedi: {ad} — {abs(v - taban):.3f} puan, "
          f"tek abonenin ettigi {100 / payda0:.3f} puandan kucuk")
```

```
yol                                       gevsek tanim  siki tanim
payda daraltma                           oynadi 64.286    oynamadi
sifir okumayi okuma saymak               oynadi 63.055    oynamadi
pencere genisletme (donem 2 ya da 3)     oynadi 81.791    oynamadi
tam sayiya yuvarlama                     oynadi 61.000    oynamadi
agirliksiz bolge ortalamasi                   oynamadi    oynamadi
hedefe oranlayarak sunmak                oynadi 93.766    oynamadi

esigi asan yol: gevsek tanimda 5, siki tanimda 0 (6 yol denendi)
kapanmadi, bu kumede islemedi: agirliksiz bolge ortalamasi — 0.037 puan, tek abonenin ettigi 0.075 puandan kucuk
```

Eşiği aşan yol sayısı 5'ten 0'a iniyor. Tablodaki asıl uyarı beşinci satırdadır. Ağırlıksız bölge
ortalaması gevşek tanımda da eşiği aşmadı, çünkü bu kümede beş bölgenin kapsama oranları
birbirine yeterince yakın. Bu yolu "kapandı" saymak, sayıyı koruyan şeyin tanım değil bölgelerin
boy dağılımı olduğunu kabul etmek olur. Bölgelerden biri küçülse ya da kapsaması ayrışsa aynı yol
eşiği aşar ve tanımda karşılığı yoksa hiçbir şey onu durdurmaz.

Bu, oynatılabilirlik sayısının nasıl okunacağını da belirler: **denenmiş ve oynamamış bir yol,
kapatılmış bir yol değildir.** Sayılan şey tanımın kaç yolu bağladığıdır, kaç yolun bu koşumda
işlediği değil.

Altı maddenin hepsinin bir tanıma yazılması ölçütü uzatıyor ve bu bir bedeldir. Bir satırlık
"okuma kapsama oranı" ifadesi altı maddelik bir tanıma dönüştüğünde raporun okuyucusu artık ölçütü
tek bakışta geri okuyamaz. Bedelin ödenme biçimi tanımı rapora değil, raporun dayandığı yere
yazmaktır: rapor satırı yalnız pay, payda ve tanım kimliğini taşır.

#### Gerçek Düşüş

Sıkı tanımın bir bedeli olabilir: ölçütü oynatılamaz yapmak, onu ölçtüğü şeye de duyarsız
yapıyorsa kazanç yoktur. Bunu ölçmek için ölçüm ağında gerçek bir kayıp üretilir.

```python

# Gercek dusus: donem 3 okumalarinin bir bolumu olcum aginda gercekten kaybolur.
# Bu bir oynatma degil, olculen seyin kendisinin degismesidir.
DUSUK, kayip = {}, 0
for k in ABONE:
    o = dict(OKUMA.get(k["abone_no"], {}))
    if 3 in o and uretec(TOHUM + 91000 + k["abone_no"])() < 0.14:
        del o[3]
        kayip += 1
    DUSUK[k["abone_no"]] = o
yeni = kapsama(kume=DUSUK)[0]
print(f"olcum aginda kaybolan donem 3 okumasi: {kayip}")
print(f"siki tanimin verdigi sayi: {taban:.3f} -> {yeni:.3f}  ({yeni - taban:+.3f} puan)\n")
print(f"{'ayni dususte gevsek tanimin yollari':<38}{'rapora yazilan':>15}"
      f"{'dususten once':>15}  birim")
orten = 0
for ad, md, v, br in yollar(DUSUK):
    orten += 1 if v >= taban else 0
    print(f"{ad:<38}{v:>15.3f}{('ustunde' if v >= taban else 'altinda'):>15}  {br}")
print(f"\nalti yoldan {orten} tanesi ayni gercek dususte sayiyi dusus oncesi degerin "
      f"ustunde tutuyor;\nbiri bunu birimi degistirerek yapiyor")
```

```
olcum aginda kaybolan donem 3 okumasi: 106
siki tanimin verdigi sayi: 60.948 -> 53.273  (-7.675 puan)

ayni dususte gevsek tanimin yollari    rapora yazilan  dususten once  birim
payda daraltma                                 56.190        altinda  %
sifir okumayi okuma saymak                     55.079        altinda  %
pencere genisletme (donem 2 ya da 3)           81.490        ustunde  %
tam sayiya yuvarlama                           53.000        altinda  %
agirliksiz bolge ortalamasi                    53.470        altinda  %
hedefe oranlayarak sunmak                      81.959        ustunde  hedefin %'i

alti yoldan 2 tanesi ayni gercek dususte sayiyi dusus oncesi degerin ustunde tutuyor;
biri bunu birimi degistirerek yapiyor
```

Sıkı tanım kaybı olduğu gibi geçiriyor: 106 okuma kaybolduğunda sayı 60,948'den 53,273'e, yani
7,675 puan iniyor. Oynatılamaz olmak duyarsız olmak değil.

Gevşek tanımın altı yolundan ikisi aynı gerçek düşüşte sayıyı düşüş öncesi değerin üstünde
tutuyor. Pencere genişletme 81,490 yazıyor, çünkü dönem 3'te kaybolan okumaların sahiplerinin
çoğunun dönem 2 okuması duruyor. Hedefe oranlama 81,959 yazıyor. İki rapor da aynı kümeden
çıkıyor, ikisi de doğru hesaplanmış ve biri kapsamanın düştüğünü, öteki yükseldiğini söylüyor.

Ölçütün oynatılabilirliği burada tam anlamını buluyor. Oynatma yalnız iyi bir sayıyı daha iyi
göstermez; **kötü haberi görünmez yapar.** Yedi puanlık gerçek bir kayıp, tanımı gevşek bir
ölçütte hiç raporlanmadan geçebilir.

Dikkat edilmesi gereken bir ayrım daha var. Pencere genişletmenin düşüşü örtmesi bir kötü niyet
belirtisi değildir: dönem 2 okuması duran bir aboneye gerçekten ulaşılmıştır ve o yolu seçen kişi
bunu bilerek yapmak zorunda değildir. Tanım yazılmadığında yol, kimse tercih etmeden de
seçilebilir; ölçütün geçen dönem hangi pencereyle hesaplandığı yazılı olmadığı için bu dönem
başka bir pencere kullanılması fark edilmez. Oynatılabilirlik bir kişilik özelliği değil, tanımın
bir özelliğidir.

#### Özet

- Oynatılabilirlik sayılabilir bir niceliktir: ölçüt, ölçtüğü şeye hiç dokunulmadan kaç yoldan ve
  kaç puan oynatılabiliyor.
- Yüzde 60,948 olan kapsama oranı altı yoldan denendiğinde beşi 0,05 puanlık eşiği aşar ve
  rapora yazılan sayı 60,948 ile 93,766 arasında herhangi bir yerde durabilir.
- En büyük iki oynama pencere genişletme (20,843 puan) ile hedefe oranlamadır (32,818 puan);
  ikincisi sayının birimini değiştirir ve birim yazılmazsa okuyucu onu kapsama diye geri okur.
- Altı maddelik yeniden tanım eşiği aşan yolu 5'ten 0'a indirir; eşiği aşmayan yol kapanmış
  sayılmaz, çünkü 0,037 puanlık oynaması tanımdan değil bölgelerin boy dağılımından gelir.
- Yollar birbirini dışlamaz: ilk üçü birlikte uygulandığında sayı aynı birimde kalarak 87,381
  olur (1.101 abone, 1.260 abonede) ve 26,433 puan yukarı taşınır.
- Ölçüm ağında 106 okuma gerçekten kaybolduğunda sıkı tanım 7,675 puan düşer; gevşek tanımın altı
  yolundan ikisi aynı düşüşte sayıyı düşüş öncesi değerin üstünde tutar.

#### Sonraki Adım

Bu ders ölçütün tanımını sabitledi, ama tanımın sabitlenmesi sayının sabitlenmesi değildir. Sıkı
tanım "pencere dönem 3'te kapanır" diyor; kapanan pencereye ait bir okuma rapor üretildikten sonra
sisteme girdiğinde aynı tanım aynı kümeye uygulanıp başka bir sayı verir. Rapor bir kaynağın
üstünde durur ve o kaynak rapora ya anlık sorguyla, ya dönemsel bir çekmeyle, ya da önceden
toplanmış bir modelle bağlanır. Sonraki ders üç bağlanma düzenini aynı ölçütle koşturur, aralarındaki
farkı sayar ve rapor veri modelinin hangi soruları sonsuza dek kapattığını gösterir.

### İş Zekâsı Araçları

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/is-zekasi-araclari

Aynı ölçüt üç yenileme düzeninde farklı bir değer verir ve fark sayılır: dönem 3 kapanışından on iki gün sonra üretilen raporda okuma oranı anlık sorguyla 56,659, dönemsel çekmeyle 48,081, önceden toplanmış modelle 36,042 çıkar; kaynağın nihai değeri 63,055'tir. Aynı gecikme ortalama tüketimi yalnız 0,038 m³ oynatır. Rapor veri modelinin sabitlediği tane ve alan kümesi altı sorunun üçünü kalıcı olarak kapatır: yalnız ortalama saklayan model bir soruda 0,192 m³ yanlış yanıt üretir, toplam ile sayı saklayan model üç soruyu doğru yanıtlar. Üç düzen ve beş rapor günü on beş rapor, beş ayrı değer ve 27,013 puanlık ayrılık üretir.

Önceki ders ölçütün tanımını altı maddeyle sabitledi. Maddelerden biri "pencere dönem 3'te
kapanır" diyordu. Sayaç okumaları pencerenin kapandığı anda hazır olmuyor: bir bölümü aynı gün,
bir bölümü haftalar sonra kaynağa giriyor. Aynı tanım, aynı kümeye, iki ayrı gün uygulandığında
iki ayrı sayı veriyor ve ikisi de tanıma uygun.

Rapor kaynağın üstünde durur ve arada bir araç vardır. Bu araç ürünüyle değil **parçalarıyla**
anılır ve dört parçası vardır: kaynağa bağlanan **veri kaynağı bağlayıcısı**, kaynağın üstüne
kurulan **rapor veri modeli**, modelin ne sıklıkta tazelendiğini belirleyen **yenileme düzeni** ve
sayıyı okuyucuya çıkaran sunum katmanı. Bu dersin ölçüsü ikinci ile üçüncü parçadan çıkar:
**yenileme gecikmesinin rapora yazdığı sayı** ve **modelin kalıcı olarak kapattığı soru sayısı**.

- **RP13.** Küme K03'ten devralınır ve **kurgudur**: 3.199 satırlık okuma tablosu, 1.329 abone, üç
  dönem, beş bölge. Tohum koda yazılıdır.
- **RP14.** Her okumanın **geliş gecikmesi** de kurgudur ve ayrı bir tohumla çekilir. Gecikme
  okumanın değerini değiştirmez, yalnız hangi anda görünür olduğunu belirler.
- **RP15.** İki ölçüt izlenir: dönem 3 **okuma oranı** (dönem 3 okuması olan abone / kayıtlı
  abone) ve dönem 3 **ortalama tüketimi**.
- **RP16.** Üç yenileme düzeni: **anlık sorgu** kaynağı rapor anında okur, **dönemsel çekme**
  yedişer günde bir tazelenir, **önceden toplanmış model** dönem kapanışında bir kez toplanır ve
  yeniden toplanmaz.
- **RP17.** Rapor veri modeli **bölge-dönem tanesindedir**; iki tasarım denenir ve yalnız
  sakladıkları alanlarla ayrılırlar.
- **RP18.** Doğruluk karşılaştırması `==` ile yapılmaz; eşik $10^{-9}$'dur ve gerekçesi kayan
  noktalı toplamanın basamak birikimidir.

#### Kaynağa Bağlanma

```python
# yenileme.py — MODELDIR. K03'un kurgu kumesi ayni tohumla uretilir; her okumaya
# kurgu bir gelis gecikmesi eklenir ve rapor uc yenileme duzeninde okunur.
import math

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26),
         ("bati", 0.14, 14), ("merkez", 0.18, 23)]


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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, ELENEN = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (ELENEN if r() < 0.046 else ABONE).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {k["abone_no"] for k in ABONE}
OKUMA = {}
for k in ABONE + ELENEN:
    r = uretec(TOHUM + 7000 + k["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            k["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100
            + 0.5) / 100
        if k["abone_no"] in GECERLI:
            OKUMA.setdefault(k["abone_no"], {})[d + 1] = t
        r()

# Her okuma kaynaga donem kapanisindan bir sure sonra girer. Gecikme kurgudur ve
# ayri bir tohumla cekilir; okumanin degerini degistirmez, yalniz gorunurlugunu.
GECIKME = {}
for k in ABONE:
    r = uretec(TOHUM + 120000 + k["abone_no"])
    for d in sorted(OKUMA.get(k["abone_no"], {})):
        GECIKME[(k["abone_no"], d)] = ayrik(r(), [0.58, 0.19, 0.13, 0.07, 0.03]) * 6


def goruntu(gun):
    """Gecikmesi gun degerini asmayan okumalarin gorundugu kaynak goruntusu."""
    g = {}
    for (a, d), gec in GECIKME.items():
        if gec <= gun:
            g.setdefault(a, {})[d] = OKUMA[a][d]
    return g


def olcutler(kume):
    """Iki olcut: donem 3 okuma orani ve donem 3 ortalama tuketimi."""
    d3 = [o[3] for o in kume.values() if 3 in o]
    return len(d3) / len(ABONE) * 100, sum(d3) / len(d3), len(d3)


TAM = goruntu(10 ** 6)
son = olcutler(TAM)
print(f"kurgu kume (tohum {TOHUM}): {len(ABONE)} abone, {len(GECIKME)} okuma")
print(f"gecikme dagilimi (gun): " + ", ".join(
    f"{g}: {sum(1 for v in GECIKME.values() if v == g)}" for g in (0, 6, 12, 18, 24)))
print(f"kaynagin nihai sayilari — okuma orani {son[0]:.3f}, "
      f"donem 3 ortalamasi {son[1]:.3f} m3 ({son[2]} okuma)")
```

```
kurgu kume (tohum 20260218): 1329 abone, 3199 okuma
gecikme dagilimi (gun): 0: 1841, 6: 608, 12: 444, 18: 213, 24: 93
kaynagin nihai sayilari — okuma orani 63.055, donem 3 ortalamasi 18.321 m3 (838 okuma)
```

Üç bin yüz doksan dokuz okumanın 1.841'i kapanış günü kaynakta, kalan 1.358'i sonraki günlerde
giriyor. Kaynağın nihai sayıları da bu listeden okunuyor ve dersin geri kalanı bu iki sayıya
karşı ölçülüyor.

#### Üç Yenileme Düzeni

```python

# Uc yenileme duzeni. Rapor donem 3 kapanisindan 12 gun sonra uretilir; duzenler
# yalniz kaynagi ne kadar geriden gordukleriyle ayrilir.
RAPOR_GUNU = 12
DUZEN = [("anlik sorgu", RAPOR_GUNU, 0),
         ("donemsel cekme (7 gunde bir)", 7, RAPOR_GUNU - 7),
         ("onceden toplanmis model", 0, RAPOR_GUNU)]
print(f"rapor gunu: donem 3 kapanisindan {RAPOR_GUNU} gun sonra\n")
print(f"{'duzen':<30}{'yenileme gecikmesi':>19}{'okuma orani':>13}{'ortalama':>10}"
      f"{'okuma':>7}{'nihaiden fark':>15}")
for ad, kes, gec in DUZEN:
    k, o, n = olcutler(goruntu(kes))
    print(f"{ad:<30}{str(gec) + ' gun':>19}{k:>13.3f}{o:>10.3f}{n:>7}"
          f"{o - son[1]:>+10.3f} m3")
print(f"{'kaynagin nihai degeri':<30}{'-':>19}{son[0]:>13.3f}{son[1]:>10.3f}{son[2]:>7}"
      f"{0.0:>+10.3f} m3")
```

```
rapor gunu: donem 3 kapanisindan 12 gun sonra

duzen                          yenileme gecikmesi  okuma orani  ortalama  okuma  nihaiden fark
anlik sorgu                                 0 gun       56.659    18.128    753    -0.193 m3
donemsel cekme (7 gunde bir)                5 gun       48.081    18.160    639    -0.161 m3
onceden toplanmis model                    12 gun       36.042    18.166    479    -0.155 m3
kaynagin nihai degeri                           -       63.055    18.321    838    +0.000 m3
```

Üç düzen arasındaki seçim bir doğruluk seçimi değil, bir takas. Anlık sorgu en taze sayıyı verir
ve bedelini kaynağa her rapor açılışında yüklenir; önceden toplanmış model kaynağı hiç yormaz ve
bedelini tazelikten öder; dönemsel çekme ikisinin arasındadır. Takasın hangi ucunun seçildiği
sayının kendisine yazılmaz.

Aynı gün, aynı kaynak, aynı ölçüt tanımı: okuma oranı 56,659, 48,081 ve 36,042 çıkıyor. Üçü de
doğru hesaplanmış ve üçü de nihai 63,055'in altında. Anlık sorgunun bile eksik olması gecikmenin
raporda değil kaynağın kendisinde olduğunu gösteriyor: rapor gününde o okumalar henüz girmemiş.

İkinci sütun asıl dersi taşıyor. Aynı gecikme ortalama tüketimi 18,128 ile 18,166 arasında,
yani yalnız 0,038 m³ oynatıyor; okuma oranını 20,617 puan oynatıyor. Neden ikisinin **sayım** ve
**oran** olmasıdır: geç gelen okumalar paydayı değil payı büyütür, ama değerleri erken gelenlerden
sistematik olarak farklı değildir. Bir gösterge tablosunda yan yana duran iki ölçütten biri
yenileme düzenine neredeyse duyarsızken öteki ona tamamen bağımlı olabilir ve bu, ölçütün
tanımından değil **türünden** gelir.

#### Rapor Veri Modelinin Sabitlediği

Rapor veri modeli kaynağın kopyası değildir; **tanesi** ve **alan kümesi** seçilmiş bir yapıdır.
İkisi de sonradan gevşetilemez: model bölge-dönem tanesinde toplandığında abone düzeyindeki her
soru kalıcı olarak kapanır.

```python

# Rapor veri modeli: kaynagin uzerine kurulan, tanesi ve alanlari sabitlenmis yapi.
# Iki tasarim denenir; ikisi de bolge-donem tanesindedir, alanlari farklidir.
MODEL_A = {}   # yalniz ortalama saklanir
MODEL_B = {}   # toplam ve sayi saklanir
for b, _, _ in BOLGE:
    v = [TAM[k["abone_no"]][3] for k in ABONE
         if k["bolge"] == b and 3 in TAM.get(k["abone_no"], {})]
    MODEL_A[b] = sum(v) / len(v)
    MODEL_B[b] = (sum(v), len(v))

ham3 = sorted(o[3] for o in TAM.values() if 3 in o)
tek = [k["abone_no"] for k in ABONE if TAM.get(k["abone_no"], {}).get(3, 0.0) > 0][0]
DOGRU = {"kume ortalamasi": sum(ham3) / len(ham3),
         "bolge ortalamasi (dogu)": MODEL_A["dogu"],
         "okuma orani": len(ham3) / len(ABONE) * 100,
         "ortanca tuketim": (ham3[len(ham3) // 2 - 1] + ham3[len(ham3) // 2]) / 2,
         "40 m3 ustu abone": float(sum(1 for v in ham3 if v > 40)),
         "tek abonenin okumasi": TAM[tek][3]}
YANIT = {
    "kume ortalamasi": (sum(MODEL_A.values()) / len(MODEL_A),
                        sum(x[0] for x in MODEL_B.values())
                        / sum(x[1] for x in MODEL_B.values())),
    "bolge ortalamasi (dogu)": (MODEL_A["dogu"], MODEL_B["dogu"][0] / MODEL_B["dogu"][1]),
    "okuma orani": (None, sum(x[1] for x in MODEL_B.values()) / len(ABONE) * 100),
    "ortanca tuketim": (None, None), "40 m3 ustu abone": (None, None),
    "tek abonenin okumasi": (None, None)}
EPS = 1e-9


def durum(v, d):
    if v is None:
        return "uretilemez"
    return "dogru" if abs(v - d) < EPS else f"yanlis ({v - d:+.3f})"


print(f"{'soru':<26}{'kaynak (abone-donem tanesi)':>28}{'model A':>18}{'model B':>18}")
sayac = [0, 0, 0]
for s, d in DOGRU.items():
    a, b = YANIT[s]
    for i, v in enumerate((d, a, b)):
        sayac[i] += 1 if v is not None and abs(v - d) < EPS else 0
    print(f"{s:<26}{format(d, '.3f'):>28}{durum(a, d):>18}{durum(b, d):>18}")
print(f"\ndogru yanitlanan soru: kaynak {sayac[0]}/6, model A {sayac[1]}/6, "
      f"model B {sayac[2]}/6")
```

```
soru                       kaynak (abone-donem tanesi)           model A           model B
kume ortalamasi                                 18.321   yanlis (-0.192)             dogru
bolge ortalamasi (dogu)                         22.394             dogru             dogru
okuma orani                                     63.055        uretilemez             dogru
ortanca tuketim                                 17.845        uretilemez        uretilemez
40 m3 ustu abone                                 8.000        uretilemez        uretilemez
tek abonenin okumasi                            21.570        uretilemez        uretilemez

dogru yanitlanan soru: kaynak 6/6, model A 1/6, model B 3/6
```

İki model aynı tanededir ve tek farkları sakladıkları alandır: biri ortalamayı, öteki toplam ile
sayıyı saklar. Bu fark üç soruyu açıp kapatıyor. Toplam ve sayı saklandığında küme ortalaması
bölgelerin toplamları toplanıp sayılarına bölünerek doğru kurulur; yalnız ortalama saklandığında
kurulamaz, çünkü ağırlıklar atılmıştır.

Birinci satır bu dersin en önemli sonucudur. Model A küme ortalaması sorusuna **üretilemez**
demiyor, 0,192 m³ yanlış bir sayı veriyor. Beş bölge ortalamasının ortalaması matematiksel olarak
bir işlem sonucudur ve bir gösterge tablosunda küme ortalaması diye görünür. Üretilemeyen bir yanıt
kendini belli eder; yanlış üretilen yanıt etmez.

Alt üç satır tanenin bedelidir. Ortanca, eşik üstü abone sayısı ve tek bir abonenin okuması iki
modelde de üretilemez, çünkü bunlar toplanabilir alanlar değildir: ortancayı bir bölgenin
ortancasından, küme ortancasını da beş bölgenin ortancasından kurmak olanaksızdır. Rapor veri
modeli bir hız kararı gibi görünür; **sorulabilecek soruların kümesini daraltan bir karardır** ve
altı sorunun üçünü kalıcı olarak kapatmıştır.

#### Aynı Ölçüt, On Beş Rapor

```python

# Ayni olcut, ayni kaynak, bes ayri rapor gunu. Onceden toplanmis model donem
# kapanisinda bir kez toplanir ve yeniden toplanmaz; cekme yediser gunde bir tazelenir.
GUNLER = (0, 6, 12, 24, 40)
KES = {"anlik sorgu": lambda g: g,
       "donemsel cekme": lambda g: 7 * (g // 7),
       "onceden toplanmis model": lambda g: 0}
print(f"{'duzen':<26}" + "".join(f"{'gun ' + str(g):>10}" for g in GUNLER))
degerler = set()
for ad, f in KES.items():
    satir = []
    for g in GUNLER:
        v = olcutler(goruntu(f(g)))[0]
        degerler.add(round(v, 3))
        satir.append(v)
    print(f"{ad:<26}" + "".join(f"{v:>10.3f}" for v in satir))
print(f"{'kaynagin nihai degeri':<26}" + "".join(f"{son[0]:>10.3f}" for g in GUNLER))
print(f"\n{len(KES)} duzen x {len(GUNLER)} rapor gunu = {len(KES) * len(GUNLER)} rapor, "
      f"{len(degerler)} ayri deger; hepsi dogru hesaplanmistir")
print(f"en genis ayrilik: {max(degerler) - min(degerler):.3f} puan "
      f"({min(degerler):.3f} ile {max(degerler):.3f})")
print(f"rapora yazilan sayi bu yuzden tek basina yazilamaz: "
      f"deger, veri kesme ani ve yenileme gecikmesi birlikte yazilir")
```

```
duzen                          gun 0     gun 6    gun 12    gun 24    gun 40
anlik sorgu                   36.042    48.081    56.659    63.055    63.055
donemsel cekme                36.042    36.042    48.081    60.873    63.055
onceden toplanmis model       36.042    36.042    36.042    36.042    36.042
kaynagin nihai degeri         63.055    63.055    63.055    63.055    63.055

3 duzen x 5 rapor gunu = 15 rapor, 5 ayri deger; hepsi dogru hesaplanmistir
en genis ayrilik: 27.013 puan (36.042 ile 63.055)
rapora yazilan sayi bu yuzden tek basina yazilamaz: deger, veri kesme ani ve yenileme gecikmesi birlikte yazilir
```

İlk iki satır yakınsıyor: anlık sorgu yirmi dördüncü günde, dönemsel çekme kırkıncı günde nihai
değere oturuyor. Üçüncü satır hiç yakınsamıyor ve 36,042'de kalıyor, çünkü dönem kapanışında bir
kez toplanmış ve bir daha toplanmamıştır. Bu satır bir kusur değil bir karardır; kusur, o kararın
raporda yazılı olmamasıdır.

On beş raporun beş ayrı değeri var ve en uçtaki ikisi arasında 27,013 puan bulunuyor. Hepsi aynı
kaynaktan, aynı tanımla ve doğru hesapla üretildi. Bir raporun okuyucusu "okuma oranı 36,042" ile
"okuma oranı 63,055" arasındaki farkı sayıya bakarak çözemez; farkı çözen tek şey sayının yanında
duran **veri kesme anı** ile **yenileme gecikmesidir**. Raporda bu ikisi yoksa değerin kendisi geri
okunamaz.

#### Özet

- Bir iş zekâsı aracı dört parçayla anılır: veri kaynağı bağlayıcısı, rapor veri modeli, yenileme
  düzeni ve sunum katmanı. Ölçü ikinci ile üçüncü parçadan çıkar.
- Aynı gün üretilen raporda okuma oranı anlık sorguyla 56,659, dönemsel çekmeyle 48,081, önceden
  toplanmış modelle 36,042 çıkar; kaynağın nihai değeri 63,055'tir ve üç sayı da doğrudur.
- Aynı gecikme ortalama tüketimi yalnız 0,038 m³, okuma oranını 20,617 puan oynatır; duyarlılık
  ölçütün tanımından değil türünden gelir.
- Rapor veri modeli tane ve alan kümesi sabitler: yalnız ortalama saklayan model küme ortalamasını
  0,192 m³ yanlış üretir, toplam ile sayı saklayan model aynı soruyu doğru yanıtlar.
- Toplanabilir olmayan üç soru (ortanca, eşik üstü abone sayısı, tek abonenin okuması) iki modelde
  de üretilemez; tane kararı altı sorunun üçünü kalıcı olarak kapatır.
- Üç düzen ve beş rapor günü on beş rapor, beş ayrı değer ve 27,013 puanlık ayrılık üretir; farkı
  çözen tek şey sayının yanındaki veri kesme anı ile yenileme gecikmesidir.

#### Sonraki Adım

Sayı artık doğru tanımla, doğru modelden ve yazılı bir kesme anıyla geliyor. Okuyucunun elinde
duran şey ise hâlâ bir sayı. Bir kararın çıkması için sayının bir zincire oturması gerekir:
bulgu, karşılaştırma, sebep adayı, belirsizlik ve öneri. Zincirin bir halkası eksik kaldığında
okuyucu onu kendisi tamamlar ve tamamlarken hangi yöne gideceği yazılı değildir. Sonraki ders bu
zinciri kurar ve halkaları teker teker çıkararak okuyucunun yapmak zorunda kaldığı çıkarımı sayar;
K05'in oynama payının bu zincirin tam olarak neresinde durduğu da orada karara bağlanır.

### Veriyle Anlatı Kurma

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/veriyle-anlati-kurma

Bulgudan öneriye giden beş adımlık zincirden bir adım çıkarıldığında okuyucunun kendisinin tamamlamak zorunda kaldığı önerme sayılır: bulgu çıkarılınca 4, karşılaştırma çıkarılınca 3, sebep adayı çıkarılınca 2, belirsizlik çıkarılınca 3, öneri çıkarılınca 3 çıkarım açıkta kalır. Zincirin taşıdığı on bir yanlış varsayılandan dokuzu öneriyi güçlendirir, ikisi zayıflatır — boşluklar rastgele dolmaz. K05'in oynama payının yeri karara bağlanır: pay hiç yazılmazsa 4 çıkarım ve 3 yanlış varsayılan, yalnız sonda bir kısıtlar paragrafında yazılırsa 1 çıkarım ve 1 yanlış varsayılan kalır, bulgunun yanında ve belirsizlik adımında yazıldığında ikisi de sıfırlanır.

Önceki üç ders sayıyı düzeltti: düzen kararı destekleyecek biçimde bölündü, ölçütün tanımı
oynatılamaz hâle getirildi, sayının yanına veri kesme anı ile yenileme gecikmesi yazıldı. Okuyucunun
elinde duran şey hâlâ bir sayı. Bir sayının karara dönüşmesi için bir zincire oturması gerekir ve
zincirin halkaları eksikse okuyucu onları kendisi tamamlar.

Bu dersin ölçüsü **atlanan adımın bıraktığı boşluktur**: bir halka çıkarıldığında okuyucunun
yapmak zorunda kaldığı çıkarım sayısı. Yanına ikinci bir sayı yazılır — bu çıkarımların kaçının
varsayılanı kümeye göre yanlıştır ve o yanlış varsayılanların kaçı öneriyi **güçlendirir**.

- **RP19.** Küme K03'ten devralınır ve **kurgudur**: 3.199 satırlık okuma tablosu, 1.329 abone,
  üç dönem, beş bölge. Tohum koda yazılıdır.
- **RP20.** Bulgu K05'ten gelir: doğu ortalaması 24,674 m³ (557 okuma), kuzey 20,210 m³ (921 okuma),
  fark 4,463 m³. **Sınama hesabı burada tekrarlanmaz**, sayılar oradan okunur.
- **RP21.** Payın iki sayısı da K05'ten okunur: bölge etiketleri 2.000 kez yeniden karıştırıldığında
  gözlenen fark hiçbir koşumda aşılmadı; kütle, örneklem, sıfır okuma ve dönem sayısı üzerine
  verilen dört savunulabilir kararın açıklığı 0,206 m³'tür.
- **RP22.** Anlatı zinciri **kurgudur**: beş adım, on altı önerme, on beşi önerinin savunulması
  için gerekli.
- **RP23.** Bir adım çıkarıldığında okuyucunun o adımın önermelerini kendisi tamamladığı
  varsayılır. Her önermenin varsayılanı ve varsayılanın yönü ders içinde yazılıdır.
- **RP24.** Öneri de **kurgudur**: doğu bölgesinde tarife basamağı sınırının gözden geçirilmesi.

#### Zincir

```python
# anlati.py — MODELDIR. K03'un kurgu kumesi ayni tohumla uretilir; uzerine bulgudan
# oneriye giden bes adimlik bir anlati zinciri kurulur.
import math

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26),
         ("bati", 0.14, 14), ("merkez", 0.18, 23)]


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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, ELENEN = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (ELENEN if r() < 0.046 else ABONE).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {k["abone_no"] for k in ABONE}
OKUMA = {}
for k in ABONE + ELENEN:
    r = uretec(TOHUM + 7000 + k["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            k["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100
            + 0.5) / 100
        if k["abone_no"] in GECERLI:
            OKUMA.setdefault(k["abone_no"], {})[d + 1] = t
        r()

ORT = {}
for b, _, _ in BOLGE:
    v = [t for k in ABONE if k["bolge"] == b
         for t in OKUMA.get(k["abone_no"], {}).values()]
    ORT[b] = (sum(v) / len(v), len(v))
FARK = ORT["dogu"][0] - ORT["kuzey"][0]
EN_GENIS = max(ORT.values())[0] - min(ORT.values())[0]
UC = sorted(ORT, key=lambda b: -ORT[b][0])

# Anlati zinciri: her adim bir onerme kumesi tasir. Onerme alanlari: kod, adim,
# icerik, adim yoksa okuyucunun dustugu varsayilan, varsayilan kumeye gore yanlis mi,
# onerinin savunulmasi icin gerekli mi, varsayilan oneriyi guclendiriyor mu (+1/-1).
ONERME = [
    ("O1", "bulgu", f"dogu ortalamasi {ORT['dogu'][0]:.3f} m3",
     "sayi bilinmiyor", False, True, 0),
    ("O2", "bulgu", f"kuzey ortalamasi {ORT['kuzey'][0]:.3f} m3",
     "sayi bilinmiyor", False, True, 0),
    ("O3", "bulgu", f"sayilar {ORT['dogu'][1]} ve {ORT['kuzey'][1]} okumaya dayaniyor",
     "gozlem sayisi buyuktur", True, True, 1),
    ("O4", "bulgu", "bulgudaki sayi payiyla birlikte yazildi",
     "sayi kesindir", True, True, 1),
    ("O5", "karsilastirma", f"fark {FARK:.3f} m3", "fark bilinmiyor", False, True, 0),
    ("O6", "karsilastirma", f"fark kuzeyin yuzde {FARK / ORT['kuzey'][0] * 100:.1f}'i",
     "fark kucuktur", True, True, -1),
    ("O7", "karsilastirma",
     f"bes bolgenin en genis farki {EN_GENIS:.3f} m3 ({UC[0]}-{UC[-1]})",
     "fark tek onemli farktir", True, True, 1),
    ("O8", "sebep adayi", "aday: dogu taban tuketimi daha yuksek",
     "sebep yoktur", False, True, 0),
    ("O9", "sebep adayi", "rakip aday: okuma kapsamasi farki",
     "tek sebep vardir", True, True, 1),
    ("O10", "sebep adayi", "rakip aday: tarife basamagi dagilimi",
     "tek sebep vardir", True, False, 1),
    ("O11", "belirsizlik", "K05: 2.000 karistirmada asilmadi", "pay olculmedi", False, True, 0),
    ("O12", "belirsizlik", "K05: dort kararin acikligi 0,206 m3", "pay sifirdir", True, True, 1),
    ("O13", "belirsizlik", "payin kapsamadigi adlandirildi",
     "pay her seyi kapsar", True, True, 1),
    ("O14", "oneri", "onerilen eylem yazildi", "eylem okuyucunun secimidir", True, True, -1),
    ("O15", "oneri", "eylemin geri alinabilirligi yazildi",
     "eylem geri alinabilir", True, True, 1),
    ("O16", "oneri", "eylem hangi olcutle olculecek", "olcum yapilmayacak", True, True, 1),
]
ADIM = ["bulgu", "karsilastirma", "sebep adayi", "belirsizlik", "oneri"]
print(f"kurgu kume (tohum {TOHUM}): {len(ABONE)} abone, "
      f"{sum(len(v) for v in OKUMA.values())} okuma")
print(f"bulgu: dogu {ORT['dogu'][0]:.3f} m3 ({ORT['dogu'][1]} okuma), "
      f"kuzey {ORT['kuzey'][0]:.3f} m3 ({ORT['kuzey'][1]} okuma), fark {FARK:.3f} m3")
print(f"zincir: {len(ADIM)} adim, {len(ONERME)} onerme, "
      f"{sum(1 for o in ONERME if o[5])} tanesi onerinin savunulmasi icin gerekli")
for a in ADIM:
    print(f"  {a:<15} " + ", ".join(o[0] for o in ONERME if o[1] == a))
```

```
kurgu kume (tohum 20260218): 1329 abone, 3199 okuma
bulgu: dogu 24.674 m3 (557 okuma), kuzey 20.210 m3 (921 okuma), fark 4.463 m3
zincir: 5 adim, 16 onerme, 15 tanesi onerinin savunulmasi icin gerekli
  bulgu           O1, O2, O3, O4
  karsilastirma   O5, O6, O7
  sebep adayi     O8, O9, O10
  belirsizlik     O11, O12, O13
  oneri           O14, O15, O16
```

Anlatı burada bir üslup değil bir **önerme kümesidir**. Öneri savunulabilir olacaksa arkasında
duran önermelerin okuyucuya verilmiş olması gerekir; verilmeyen her önermeyi okuyucu kendisi
üretir. Modelin yaptığı iş bu üretimi saymaktır.

Üçüncü adımın adı da bilerek "sebep" değil **sebep adayıdır**. Rapor doğu bölgesinin taban
tüketiminin daha yüksek olduğunu bir aday olarak yazar, iki rakip adayı da yanına koyar ve
aralarında karar vermez; kümede bu kararı verecek bir düzenek yoktur. Adayları yazmak bir kaçamak
değil, okuyucunun tek adaya düşmesini engelleyen tek yoldur.

#### Atlanan Adım

```python

# Zincirden bir adim cikarilir ve okuyucunun kendisinin tamamlamasi gereken
# gerekli onerme sayilir. Yanlis varsayilan: kumeye gore dogru olmayan tamamlama.
def bosluk(cikan=None, elenen=frozenset()):
    eksik = [o for o in ONERME if o[5] and (o[1] == cikan or o[0] in elenen)]
    yanlis = [o for o in eksik if o[4]]
    return eksik, yanlis, sum(1 for o in yanlis if o[6] > 0)


print(f"{'cikarilan adim':<22}{'okuyucunun cikarimi':>20}{'yanlis varsayilan':>19}"
      f"{'oneriyi guclendiren':>21}")
for a in [None] + ADIM:
    e, y, g = bosluk(a)
    print(f"{a or 'hicbiri (tam zincir)':<22}{len(e):>20}{len(y):>19}{g:>21}")
print(f"\ntum zincirdeki yanlis varsayilanlar ve yonleri")
for o in ONERME:
    if o[4]:
        print(f"  {o[0]:<4} {o[1]:<14} okuyucu su varsayilana duser: {o[3]:<28}"
              f"{'oneriyi guclendirir' if o[6] > 0 else 'oneriyi zayiflatir'}")
tumu = [o for o in ONERME if o[4]]
print(f"toplam {len(tumu)} yanlis varsayilandan {sum(1 for o in tumu if o[6] > 0)} tanesi "
      f"oneriyi guclendiriyor, {sum(1 for o in tumu if o[6] < 0)} tanesi zayiflatiyor")
```

```
cikarilan adim         okuyucunun cikarimi  yanlis varsayilan  oneriyi guclendiren
hicbiri (tam zincir)                     0                  0                    0
bulgu                                    4                  2                    2
karsilastirma                            3                  2                    1
sebep adayi                              2                  1                    1
belirsizlik                              3                  2                    2
oneri                                    3                  3                    2

tum zincirdeki yanlis varsayilanlar ve yonleri
  O3   bulgu          okuyucu su varsayilana duser: gozlem sayisi buyuktur      oneriyi guclendirir
  O4   bulgu          okuyucu su varsayilana duser: sayi kesindir               oneriyi guclendirir
  O6   karsilastirma  okuyucu su varsayilana duser: fark kucuktur               oneriyi zayiflatir
  O7   karsilastirma  okuyucu su varsayilana duser: fark tek onemli farktir     oneriyi guclendirir
  O9   sebep adayi    okuyucu su varsayilana duser: tek sebep vardir            oneriyi guclendirir
  O10  sebep adayi    okuyucu su varsayilana duser: tek sebep vardir            oneriyi guclendirir
  O12  belirsizlik    okuyucu su varsayilana duser: pay sifirdir                oneriyi guclendirir
  O13  belirsizlik    okuyucu su varsayilana duser: pay her seyi kapsar         oneriyi guclendirir
  O14  oneri          okuyucu su varsayilana duser: eylem okuyucunun secimidir  oneriyi zayiflatir
  O15  oneri          okuyucu su varsayilana duser: eylem geri alinabilir       oneriyi guclendirir
  O16  oneri          okuyucu su varsayilana duser: olcum yapilmayacak          oneriyi guclendirir
toplam 11 yanlis varsayilandan 9 tanesi oneriyi guclendiriyor, 2 tanesi zayiflatiyor
```

Tablonun ilk satırı zincirin tanımını doğruluyor: tam zincirde okuyucuya hiçbir çıkarım kalmıyor,
çünkü önerinin savunulması için gereken on beş önermenin on beşi de yazılmış durumda. Bu satır bir
başarı ölçüsü değil, ölçüm aracının kendi sıfır noktasıdır; geri kalan beş satır ona göre okunur.

Beş adımın hiçbiri ücretsiz değil. Bulgu çıkarıldığında dört, belirsizlik ve öneri çıkarıldığında
üçer, karşılaştırma çıkarıldığında üç, sebep adayı çıkarıldığında iki çıkarım okuyucuya kalıyor.
Sayılar birbirine yakın ve tek başına okunduğunda hangi adımın atlanmasının daha pahalı olduğunu
söylemiyor.

Ayrımı ikinci ve üçüncü sütun yapıyor. On bir yanlış varsayılanın dokuzu öneriyi
**güçlendiriyor**, yalnız ikisi zayıflatıyor. Boşluklar rastgele dolmuyor: okuyucu bir sayının
yanında pay göremediğinde payı sıfır sayıyor, rakip bir sebep adayı göremediğinde tek sebep
olduğunu varsayıyor, gözlem sayısı yazılmadığında sayının büyük bir kümeden geldiğini kabul ediyor.
Üçü de öneriyi olduğundan sağlam gösteriyor.

Bunun sonucu yöntemsel bir kuraldır. Bir anlatı adımını atlamak, öneriyi savunmayı **kolaylaştırır**
ve bu kolaylık yazarın niyetinden bağımsızdır. Zayıflatan iki varsayılan da bu kuralı bozmuyor,
tersine açıklıyor: okuyucu farkın büyüklüğünü görmediğinde onu küçük sayar, önerilen eylem
yazılmadığında eylemi kendisi seçer. Bu ikisi yazarın işine yaramadığı için atlanmaz; atlanan
adımlar, atlanınca işe yarayan adımlardır.

#### Payın Yeri

Bu noktada K05'in oynama payının zincirin neresinde duracağı karara bağlanabilir. Üç yerleşim
denenir.

```python

# Payin anlatidaki yeri uc yerlesimle denenir. Pay K05'te olculdu ve burada
# yeniden hesaplanmaz; sorulan sey payin zincirin hangi adiminda durdugudur.
YERLESIM = [("pay hic yazilmaz", {"O4", "O11", "O12", "O13"}),
            ("pay yalniz sonda, kisitlar paragrafinda", {"O4"}),
            ("pay bulgunun yaninda ve belirsizlik adiminda", set())]
print(f"{'payin yerlesimi':<46}{'cikarim':>9}{'yanlis':>8}{'guclendiren':>13}")
for ad, el in YERLESIM:
    e, y, g = bosluk(elenen=el)
    print(f"{ad:<46}{len(e):>9}{len(y):>8}{g:>13}")

print("\n--- ucuncu yerlesimin zinciri ---------------------------------")
for a in ADIM:
    print(f"  [{a}]")
    for o in ONERME:
        if o[1] == a:
            print(f"     {o[0]:<4} {o[2]}{'' if o[5] else '   (gerekli degil)'}")
```

```
payin yerlesimi                                 cikarim  yanlis  guclendiren
pay hic yazilmaz                                      4       3            3
pay yalniz sonda, kisitlar paragrafinda               1       1            1
pay bulgunun yaninda ve belirsizlik adiminda          0       0            0

--- ucuncu yerlesimin zinciri ---------------------------------
  [bulgu]
     O1   dogu ortalamasi 24.674 m3
     O2   kuzey ortalamasi 20.210 m3
     O3   sayilar 557 ve 921 okumaya dayaniyor
     O4   bulgudaki sayi payiyla birlikte yazildi
  [karsilastirma]
     O5   fark 4.463 m3
     O6   fark kuzeyin yuzde 22.1'i
     O7   bes bolgenin en genis farki 10.896 m3 (dogu-bati)
  [sebep adayi]
     O8   aday: dogu taban tuketimi daha yuksek
     O9   rakip aday: okuma kapsamasi farki
     O10  rakip aday: tarife basamagi dagilimi   (gerekli degil)
  [belirsizlik]
     O11  K05: 2.000 karistirmada asilmadi
     O12  K05: dort kararin acikligi 0,206 m3
     O13  payin kapsamadigi adlandirildi
  [oneri]
     O14  onerilen eylem yazildi
     O15  eylemin geri alinabilirligi yazildi
     O16  eylem hangi olcutle olculecek
```

İkinci yerleşim yaygın olanıdır ve bir çıkarım açık bırakıyor. Pay yazılmıştır, ama sonda
yazılmıştır: okuyucu bulguyu okurken sayının yanında hiçbir aralık görmemiş ve "sayı kesindir"
varsayılanını çoktan kurmuştur. Sonda gelen kısıtlar paragrafı bu varsayılanı geri almaz; yalnız
yazarın kaydını temizler. Payın bir yeri değil **iki yeri** vardır ve birincisi bulgudaki sayının
yanıdır.

İkincisi belirsizlik adımıdır ve orada pay iki farklı şey söyler. K05'in 2.000 yeniden
karıştırmada aşılmayan farkı tek bir soruyu kapatır: bölge etiketleri rastgele dağılsaydı bu fark
çıkar mıydı. Dört savunulabilir kararın 0,206 m³'lük açıklığı bambaşka bir belirsizliktir ve
karıştırma payı onu hiç ölçmez. Bu yüzden zincirde üçüncü bir önerme duruyor: **payın kapsamadığı
adlandırılır.** Adlandırılmadığında okuyucunun düştüğü varsayılan "pay her şeyi kapsar" olur ve
bu varsayılan da öneriyi güçlendirenler arasındadır.

Zincirin son üç önermesi öneriyi bir eyleme bağlar: eylem yazılır, geri alınabilir olup olmadığı
yazılır, hangi ölçütle ölçüleceği yazılır. Sonuncusu önceki iki dersi buraya bağlar. Ölçüt yazılmazsa
öneri hiç sınanmaz; ölçüt yazılırsa tanımının sıkı olması gerekir, yoksa öneriyi değerlendiren
sayı ölçtüğü şeyi oynatmadan oynatılabilir.

#### Özet

- Anlatı bir üslup değil beş adımlık bir önerme zinciridir: bulgu, karşılaştırma, sebep adayı,
  belirsizlik, öneri. Zincir on altı önerme taşır ve on beşi önerinin savunulması için gereklidir.
- Bir adım çıkarıldığında okuyucuya kalan çıkarım sayısı 2 ile 4 arasındadır; tek başına bu sayı
  hangi adımın daha pahalı olduğunu söylemez.
- Zincirin on bir yanlış varsayılanından dokuzu öneriyi güçlendirir, ikisi zayıflatır; bir adımı
  atlamak öneriyi savunmayı kolaylaştırır ve bu kolaylık yazarın niyetinden bağımsızdır.
- Oynama payının bir değil iki yeri vardır: bulgudaki sayının yanı ve belirsizlik adımı. Sonda
  ayrı bir kısıtlar paragrafı 1 çıkarım açık bırakır ve o çıkarım "sayı kesindir" varsayılanıdır.
- Belirsizlik adımı payın kapsamadığını da adlandırır: K05'in 2.000 karıştırmalık payı yalnız
  etiketlerin rastgele dağılması sorusunu kapatır, dört kararın 0,206 m³'lük açıklığını ölçmez.
- Üçüncü adım sebebi değil sebep adaylarını yazar; zincirin son üç önermesi öneriyi bir eyleme,
  eylemin geri alınabilirliğine ve onu sınayacak ölçüte bağlar.

#### Sonraki Adım

Rapor artık bir zincir üzerinde duruyor: sayılar geri okunabilen bir düzende, ölçütlerin tanımı
sıkı, kaynağın kesme anı yazılı, anlatının halkaları tam. Bunların hepsi tek bir raporun **bir
kez** üretilmesiyle ilgili. Aynı raporun üç ay sonra, başka bir kişi tarafından, aynı sayıları
verecek biçimde yeniden üretilmesi hiç sorulmadı. Sonraki ders bunu sayar: aynı raporu ikinci kez
üretmek için kaç kalem sabit olmalı, biri eksik kaldığında sayı ne kadar oynuyor ve M26/K03 ile
M26/K04'ün saydığı kalemlerin üstüne raporun eklediği kaç kalem var.

### Rapor Yeniden Üretilebilirliği

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/rapor-yeniden-uretilebilirligi

Aynı raporu ikinci kez üretmek için sabitlenmesi gereken kalem sayılır: M26/K03'ün hazırlama defterindeki 39 karar ile M26/K04'ün 9 ortam kaleminin üstüne raporun kendi 11 kalemi eklenir ve toplam 59 olur. On bir kalemin 10'u tek başına serbest bırakıldığında raporu oynatır — okuma oranı 36,042 ile 100,000 arasında herhangi bir yere gider; oynatmayan tek kalem başka bir kalemle birlikte serbest bırakıldığında 87,168'i 94,432'ye taşır. Veri kesme anı ile yenileme düzeni yedi satırlık raporun yedi satırını da değiştirirken sunum kalemleri yalnız birini değiştirir; on bir kalem toplam 26 satır oynatır.

Önceki ders raporu bir önerme zincirine oturttu ve zincirin halkalarını saydı. O rapor bir kez
üretildi. Aynı raporu üç ay sonra, başka bir kişinin, aynı sayıları verecek biçimde yeniden
üretmesi hiç sorulmadı. Rapor bir dosya değil bir **koşumun ürünüdür** ve koşumun her adımında
bir karar vardır.

Bu sorunun bir bölümü daha önce kapandı. Veri Toplama ve Hazırlama kursu hazırlama hattını on yedi
adım ve otuz dokuz karar olarak yazdı; koşum defteri tutulduğunda otuz dokuz kararın otuz dokuzu
koşumdan sonra okunabilir oldu. Python ile Veri Analizi kursu bir adım öteye gitti ve çalışma
ortamını dokuz kalemlik bir liste olarak yazdı: kilit dosyasıyla on yedi adımın on biri iki
makinede aynı sonucu veriyordu, kalan altısı kilit dosyasında hiç yazmayan beş kaleme bağlıydı.
**İki hesap da burada tekrarlanmaz**, girdi olarak alınır. Bu dersin eklediği şey **raporun
kendisidir**: hat aynı, ortam aynı olsa bile rapor aynı olmayabilir.

- **RP25.** Küme K03'ten devralınır ve **kurgudur**: 3.199 satırlık okuma tablosu, 1.329 abone,
  üç dönem, beş bölge. Tohum koda yazılıdır.
- **RP26.** Okumaların **geliş gecikmeleri** de kurgudur ve önceki dersin tohumuyla üretilir.
- **RP27.** Raporun kalemleri **kurgudur**: on bir kalem, her birinin bir sabit değeri ve eşit
  ölçüde savunulabilir bir alternatifi vardır.
- **RP28.** Eşikler: oran için **0,05 puan** (paydadaki tek abone 0,075 puan eder), ortalama için
  **0,0005 m³** — ikincisi M26/K04'ün kullandığı eşiktir. Kayan noktalı karşılaştırma `==` ile
  yapılmaz.
- **RP29.** M26/K03'ün 39 kararı ve M26/K04'ün 9 ortam kalemi **girdi** olarak alınır.
- **RP30.** Rapor **yedi satırdır**: iki başlık ve beş bölge satırı.

#### Raporun Kalemleri

```python
# yeniden.py — MODELDIR. K03'un kurgu kumesi ayni tohumla uretilir; uzerine kurgu
# gelis gecikmeleri ve raporu ureten kalem listesi kurulur.
import math

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26),
         ("bati", 0.14, 14), ("merkez", 0.18, 23)]


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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, ELENEN = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    (ELENEN if r() < 0.046 else ABONE).append(
        {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2]})
GECERLI = {k["abone_no"] for k in ABONE}
OKUMA = {}
for k in ABONE + ELENEN:
    r = uretec(TOHUM + 7000 + k["abone_no"])
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        t = 0.0 if r() < 0.038 else math.floor(
            k["taban"] * math.exp((r() + r() + r() - 1.5) * 0.62) * (1 - d * 0.05) * 100
            + 0.5) / 100
        if k["abone_no"] in GECERLI:
            OKUMA.setdefault(k["abone_no"], {})[d + 1] = t
        r()

# Her okuma kaynaga donem kapanisindan bir sure sonra girer. Gecikme kurgudur ve
# ayri bir tohumla cekilir; okumanin degerini degistirmez, yalniz gorunurlugunu.
GECIKME = {}
for k in ABONE:
    r = uretec(TOHUM + 120000 + k["abone_no"])
    for d in sorted(OKUMA.get(k["abone_no"], {})):
        GECIKME[(k["abone_no"], d)] = ayrik(r(), [0.58, 0.19, 0.13, 0.07, 0.03]) * 6


def goruntu(gun):
    """Gecikmesi gun degerini asmayan okumalarin gorundugu kaynak goruntusu."""
    g = {}
    for (a, d), gec in GECIKME.items():
        if gec <= gun:
            g.setdefault(a, {})[d] = OKUMA[a][d]
    return g


# Raporun kendi kalemleri. Her kalemin sabit degeri ve esit olcude savunulabilir
# bir alternatifi vardir; alternatif secilirse rapor ayni raporu vermez.
KALEM = [("veri kesme ani", "12 gun", "40 gun"),
         ("yenileme duzeni", "anlik sorgu", "onceden toplanmis model"),
         ("payda tanimi", "kayitli abone", "okumasi olan abone"),
         ("sifir okuma", "okuma sayilir", "okuma sayilmaz"),
         ("gozlem penceresi", "donem 3", "donem 2 ya da 3"),
         ("abone suzgeci", "tum aboneler", "uc donemi tam olanlar"),
         ("bolge suzgeci", "bes bolge", "merkez haric"),
         ("toplulastirma agirligi", "abone agirlikli", "bolge agirliksiz"),
         ("sunum ondaligi", "uc ondalik", "tam sayi"),
         ("sunum birimi", "oran", "hedefin yuzdesi"),
         ("hedef degeri", "65", "60")]
SABIT = {a: b for a, b, _ in KALEM}


def rapor(k):
    """Kalem listesinden iki basligi ureten rapor. Her karar bir kalemden okunur."""
    kes = 12 if k["veri kesme ani"] == "12 gun" else 40
    kes = 0 if k["yenileme duzeni"] != "anlik sorgu" else kes
    kume = goruntu(kes)
    sec = [a for a in ABONE if k["bolge suzgeci"] == "bes bolge" or a["bolge"] != "merkez"]
    if k["abone suzgeci"] != "tum aboneler":
        sec = [a for a in sec if len(kume.get(a["abone_no"], {})) == 3]
    don = (2, 3) if k["gozlem penceresi"] != "donem 3" else (3,)
    pay, deger, bolge = [], [], {}
    for a in sec:
        o = kume.get(a["abone_no"], {})
        v = [o[d] for d in don if d in o and (k["sifir okuma"] == "okuma sayilir" or o[d] > 0)]
        bolge.setdefault(a["bolge"], [0, 0])
        if v:
            pay.append(a)
            deger.extend(v)
            bolge[a["bolge"]][0] += 1
        bolge[a["bolge"]][1] += 1
    payda = len(sec) if k["payda tanimi"] == "kayitli abone" else \
        len([a for a in sec if kume.get(a["abone_no"])])
    oran = (sum(x[0] / x[1] for x in bolge.values()) / len(bolge) * 100
            if k["toplulastirma agirligi"] != "abone agirlikli" else len(pay) / payda * 100)
    if k["sunum birimi"] != "oran":
        oran = oran / float(k["hedef degeri"]) * 100
    ond = 3 if k["sunum ondaligi"] == "uc ondalik" else 0
    return round(oran, ond), round(sum(deger) / len(deger), 3)


ESIK_ORAN, ESIK_M3 = 0.05, 0.0005
t_oran, t_ort = rapor(SABIT)
print(f"kurgu kume (tohum {TOHUM}): {len(ABONE)} abone, {len(GECIKME)} okuma")
print(f"raporun kalemleri: {len(KALEM)}")
print(f"sabit kalemlerle rapor: okuma orani {t_oran:.3f}, "
      f"donem 3 ortalamasi {t_ort:.3f} m3")
print(f"esikler: oran {ESIK_ORAN} puan, ortalama {ESIK_M3} m3")
```

```
kurgu kume (tohum 20260218): 1329 abone, 3199 okuma
raporun kalemleri: 11
sabit kalemlerle rapor: okuma orani 56.659, donem 3 ortalamasi 18.128 m3
esikler: oran 0.05 puan, ortalama 0.0005 m3
```

On bir kalemin hiçbiri bir kod satırında gizli değil; hepsi rapor işlevinin okuduğu bir listede
duruyor. Bu, sabitlemenin ilk koşuludur: bir kalem koda gömülüyse serbest mi sabit mi olduğu
sorulamaz.

#### Bir Kalem Serbest Bırakıldığında

```python

# Her kalem tek tek serbest birakilir: alternatif deger secilir, oteki on kalem
# sabit kalir ve rapor yeniden uretilir.
def oynatan(o, m):
    return abs(o - t_oran) > ESIK_ORAN or abs(m - t_ort) > ESIK_M3


print(f"{'serbest birakilan kalem':<24}{'alternatif':<24}{'okuma orani':>12}"
      f"{'ortalama':>10}{'oynatti mi':>12}")
oyn = 0
for ad, sab, alt in KALEM:
    k = dict(SABIT)
    k[ad] = alt
    o, m = rapor(k)
    oyn += 1 if oynatan(o, m) else 0
    print(f"{ad:<24}{alt:<24}{o:>12.3f}{m:>10.3f}"
          f"{('evet' if oynatan(o, m) else 'hayir'):>12}")
print(f"\n{len(KALEM)} kalemin {oyn}'i tek basina serbest birakildiginda raporu oynatiyor")
ikili = dict(SABIT)
ikili["sunum birimi"], ikili["hedef degeri"] = "hedefin yuzdesi", "60"
o, m = rapor(ikili)
print(f"tek basina oynatmayan 'hedef degeri', 'sunum birimi' ile birlikte serbest "
      f"birakildiginda: {o:.3f}")
```

```
serbest birakilan kalem alternatif               okuma orani  ortalama  oynatti mi
veri kesme ani          40 gun                        63.055    18.321        evet
yenileme duzeni         onceden toplanmis model       36.042    18.166        evet
payda tanimi            okumasi olan abone            60.531    18.128        evet
sifir okuma             okuma sayilmaz                54.628    18.803        evet
gozlem penceresi        donem 2 ya da 3               80.512    19.020        evet
abone suzgeci           uc donemi tam olanlar        100.000    18.160        evet
bolge suzgeci           merkez haric                  55.842    17.476        evet
toplulastirma agirligi  bolge agirliksiz              56.746    18.128        evet
sunum ondaligi          tam sayi                      57.000    18.128        evet
sunum birimi            hedefin yuzdesi               87.168    18.128        evet
hedef degeri            60                            56.659    18.128       hayir

11 kalemin 10'i tek basina serbest birakildiginda raporu oynatiyor
tek basina oynatmayan 'hedef degeri', 'sunum birimi' ile birlikte serbest birakildiginda: 94.432
```

Tek bir kalem serbest kaldığında okuma oranı 36,042 ile 100,000 arasında herhangi bir yere
gidiyor. Öteki on kalem sabit; oynatan tek şey serbest bırakılanı.

Altıncı satır ayrıca okunmayı hak ediyor. "Üç dönemi tam olan aboneler" süzgeci makul bir cümledir
ve ölçütü tam olarak 100,000 yapar, çünkü süzgeç paydayı da süzer: dönem 3 okuması olmayan abone
zaten kümede kalmaz. Ölçüt artık hiçbir şey ölçmüyor ve rapor bunu hiçbir yerde söylemiyor.
**Bir süzgeç, ölçütün paydasına dokunduğunda ölçütü ölçüt olmaktan çıkarabilir.**

Son satır ise sabitleme kararının nasıl verileceğini belirliyor. Hedef değeri tek başına serbest
bırakıldığında rapor kımıldamıyor, çünkü sunum birimi "oran" olduğu sürece hedef hiçbir hesaba
girmiyor. Sunum birimiyle birlikte serbest bırakıldığında aynı kalem 87,168'i 94,432'ye taşıyor.
**Bu koşumda oynatmayan kalem, sabitlenmesi gerekmeyen kalem değildir.** Kalemler tek tek değil,
birlikte oynatır ve bir kalemin etkisiz görünmesi başka bir kalemin sabit olmasına bağlı olabilir.

#### İkinci Üretim

```python

# Rapor yedi satirdir: iki baslik ve bes bolge satiri. Ikinci uretim ayni kalem
# listesiyle kosar; sonra her kalem tek tek serbest birakilip kac satir degistigi sayilir.
def satirlar(k):
    o, m = rapor(k)
    s = [f"okuma orani {o:.3f}", f"donem 3 ortalamasi {m:.3f} m3"]
    kes = 12 if k["veri kesme ani"] == "12 gun" else 40
    kes = 0 if k["yenileme duzeni"] != "anlik sorgu" else kes
    kume = goruntu(kes)
    for b, _, _ in BOLGE:
        v = [kume[a["abone_no"]][3] for a in ABONE
             if a["bolge"] == b and 3 in kume.get(a["abone_no"], {})]
        s.append(f"{b} ortalamasi {sum(v) / len(v):.3f} m3 ({len(v)} okuma)")
    return s


ilk = satirlar(SABIT)
print(f"ikinci uretim ayni kalem listesiyle: "
      f"{sum(1 for a, b in zip(ilk, satirlar(dict(SABIT))) if a == b)}/{len(ilk)} satir ayni")
print(f"\n{'serbest birakilan kalem':<24}{'degisen satir':>14}   degisenler")
toplam = 0
for ad, sab, alt in KALEM:
    k = dict(SABIT)
    k[ad] = alt
    fark = [i for i, (a, b) in enumerate(zip(ilk, satirlar(k))) if a != b]
    toplam += len(fark)
    print(f"{ad:<24}{len(fark):>14}   {', '.join(str(i + 1) for i in fark) or '-'}")
print(f"\n{len(KALEM)} kalemin her biri tek basina serbest birakildiginda toplam "
      f"{toplam} satir degisiyor ({len(ilk)} satirlik raporda)")
DEFTER = [("M26/K03 hazirlama defteri", 39, "hattin 17 adimindaki kararlar"),
          ("M26/K04 calisma ortami", 9, "kosum ortaminin kalemleri"),
          ("bu ders — rapor", len(KALEM), "raporun kendi kalemleri")]
print(f"\n{'kaynak':<28}{'kalem':>7}   ne sabitler")
for a, n, s in DEFTER:
    print(f"{a:<28}{n:>7}   {s}")
print(f"{'toplam':<28}{sum(n for _, n, _ in DEFTER):>7}")
```

```
ikinci uretim ayni kalem listesiyle: 7/7 satir ayni

serbest birakilan kalem  degisen satir   degisenler
veri kesme ani                       7   1, 2, 3, 4, 5, 6, 7
yenileme duzeni                      7   1, 2, 3, 4, 5, 6, 7
payda tanimi                         1   1
sifir okuma                          2   1, 2
gozlem penceresi                     2   1, 2
abone suzgeci                        2   1, 2
bolge suzgeci                        2   1, 2
toplulastirma agirligi               1   1
sunum ondaligi                       1   1
sunum birimi                         1   1
hedef degeri                         0   -

11 kalemin her biri tek basina serbest birakildiginda toplam 26 satir degisiyor (7 satirlik raporda)

kaynak                        kalem   ne sabitler
M26/K03 hazirlama defteri        39   hattin 17 adimindaki kararlar
M26/K04 calisma ortami            9   kosum ortaminin kalemleri
bu ders — rapor                  11   raporun kendi kalemleri
toplam                           59
```

İkinci üretim yedi satırın yedisinde aynı sonucu veriyor. Bu, kalem listesinin taşınmasının
yeterli olduğunu gösteriyor; sabitlenen şey kodun kendisi değil, kodun okuduğu listedir.

Kalemler eşit ağırlıkta değil. Veri kesme anı ile yenileme düzeni yedi satırın yedisini birden
değiştiriyor, çünkü ikisi de kaynağın hangi görüntüsünün okunacağını belirliyor ve o görüntü
raporun her satırının altında duruyor. Sunum kalemleri yalnız bir satırı değiştiriyor. Aradaki
fark bir öncelik listesi verir: raporun yanında taşınacak tek bir kalem seçilecekse o kalem veri
kesme anıdır.

Toplam elli dokuz kalem üç ayrı yerden geliyor ve üçü birbirinin yerine geçmiyor. Hazırlama
defterinin otuz dokuz kararı hattın adımlarını sabitler ama raporun kesme anını bilmez. Çalışma
ortamının dokuz kalemi koşumu sabitler ama raporun süzgecini bilmez. Raporun on bir kalemi ise
ötekilerin ikisi de sabit olmadan hiçbir şey garanti etmez: aynı kalem listesiyle koşan iki rapor,
altındaki hat ya da ortam değiştiyse yine ayrışır.

#### Özet

- Bir raporun yeniden üretilmesi üç kaynaktan gelen 59 kalemin sabitlenmesini ister: hazırlama
  defterinin 39 kararı, çalışma ortamının 9 kalemi ve raporun kendi 11 kalemi.
- On bir kalemin 10'u tek başına serbest bırakıldığında raporu oynatır; okuma oranı 36,042 ile
  100,000 arasında herhangi bir yere gider.
- Tek başına oynatmayan kalem sabitlenmesi gerekmeyen kalem değildir: hedef değeri sunum birimiyle
  birlikte serbest bırakıldığında 87,168'i 94,432'ye taşır.
- Bir süzgeç ölçütün paydasına dokunduğunda ölçütü ölçüt olmaktan çıkarabilir: "üç dönemi tam olan
  aboneler" süzgeci okuma oranını tanım gereği 100,000 yapar.
- Kalemler eşit ağırlıkta değildir: veri kesme anı ile yenileme düzeni yedi satırın yedisini,
  sunum kalemleri yalnız birini değiştirir; on bir kalem toplam 26 satır oynatır.
- Kalem listesi taşındığında ikinci üretim yedi satırın yedisinde aynıdır; sabitlenen şey kod
  değil, kodun okuduğu listedir.

#### Sonraki Adım

Konu beş derste bir raporu baştan sona kurdu ve her adımda kararı yazdı. Aynı on iki ölçüt üç
düzende kodlandığında bir bakışta 1, 2 ve 9 ölçüt geri okundu ve desteklenen karar sayısı 0, 0 ve
3 çıktı. Yüzde 60,948'lik bir ölçüt, ölçüm ağında tek bir okuma değişmeden altı yoldan denendi ve
beşi eşiği aştı. Aynı ölçüt üç yenileme düzeninde 56,659, 48,081 ve 36,042 verdi. Bulgudan öneriye
giden zincirin on bir yanlış varsayılanından dokuzu öneriyi güçlendirdi. Ve raporun ikinci kez
üretilmesi 59 kalem istedi.

Bütün bu işler sessiz bir varsayımla yapıldı: küme tek bir makineye sığıyor. Üç bin yüz doksan
dokuz satır her blokta baştan sona okundu, her toplulaştırma tek bir döngüde yapıldı, bir süzgeç
denemek saniyeler sürdü. Ölçüm ağı büyüdüğünde bu varsayım düşer ve düştüğü anda raporun kendisi
değil **hesabın kendisi** taşınmak zorunda kalır. Sonraki konu bunu sorar: küme tek makineye
sığmadığında raporun hangi kısmı bölünebiliyor, bölünen kısım hangi geçişlerde kaç kayıt taşıyor
ve bölünemeyen pay neye mal oluyor.

## Ölçekli Analiz

Aynı raporun tek makineye sığmayan bir küme üzerinde kurulduğunda hangi kısmının bölünebildiğinin sayılması: hacim, hız ve çeşitliliğin ölçülebilir birimlere indirgenmesi, eşle-indirge modelinde geçiş sayısı ile taşınan kayıt, küme üzerinde veri çerçevesi işlemenin eklediği eşgüdüm kalemi, sütunlu dosya düzeninin okuttuğu değer sayısı ve satır kümesi atlama kazancı, bölünemeyen payın hızlanmaya koyduğu tavan ve dağıtımın gereksiz olduğu iş boyutu.

### Büyük Veri Kavramı

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/buyuk-veri-kavrami

Hacim, hız ve çeşitliliğin nitel iddia yerine sayılabilir birimlere indirgenmesi: kurgu ölçüm ağının 3.199 satırı bellekte 914.929 bayt tutar, satır başına 286,0 bayttır ve tek makineye sığmama eşiği çalışma alanı ile ara sonuç payı kararlarına göre 7.508.560 ile 240.273.951 satır arasında değişir. Aynı 1.260 sayaç dönemsel okumada 1.260, on beş dakikalık okumada 3.628.800 satır/dönem üretir; eşiğe kalan dönem 17.875'ten 7'ye, dönem başına yüzde 4 büyüyen ağda 168'den 6'ya iner. Beş kaynak şemasının 18 alanının 9'u birleştirilemez ve iki eşleme tablosu bu sayıyı 6'ya indirir; anahtar yokluğu kapanır, birim ve zaman biçimi çelişkisi kapanmaz.

Rapor doğru kuruldu. Ölçütler bir bakışta geri okunabilecek biçimde dizildi, oynatılabilirlikleri
sınandı, anlatı bulgudan öneriye giden adımları eksiksiz taşıdı ve raporun ikinci kez üretilmesi
için sabitlenmesi gereken kalemler sayıldı. Bütün bu işin altında yazılmamış tek bir varsayım
duruyor: küme tek bir makineye sığıyor. Kaynak açılıyor, satırlar belleğe alınıyor, hesap
yapılıyor ve sonuç yazılıyor.

Bu konu o varsayımı kaldırır. İlk soru en kaba olanıdır: bir küme ne zaman "tek makineye sığmaz"
sayılır. Cevap bir sıfat değil bir sayıdır ve o sayıyı üreten kararlar yazılmadıkça sayı da
yoktur. Ders hacim, hız ve çeşitliliği üç ayrı nitel iddia olarak değil, **altı sayılabilir birim**
olarak kurar: bayt ve satır, satır/dönem ve gecikme, ayrı şema sayısı ve birleştirilemeyen alan
sayısı.

- **OA1.** Küme K03–K05'ten devralınır ve **kurgudur**: 3.199 satır, 1.329 abone, üç dönem, beş
  bölge, tarife basamakları 10/25/40 m³. Aynı tohumla üretilir ve çıktı koşumdan koşuma aynıdır.
  **Modeldir.**
- **OA2.** Satır başına bayt tek bir gösterimle ölçülür. Alan tiplerinin ayırdığı baytı hangi
  kararın belirlediği M26/K04'ün başarım konusunda ölçüldü; burada o karar **girdidir** ve tek bir
  sayı olarak alınır.
- **OA3.** Tek makinenin çalışma alanı bir varsayımdır ve tabloda üç değerle sınanır. Ara sonuç
  payı ikinci karardır: kümenin yanında kümenin kendisi kadar geçici yer istenip istenmediği.
- **OA4.** Hız iki sayıdır: **satır/dönem** ve **gecikme**. Gecikme, dönemsel yenilenen bir raporda
  bir okumanın en çok kaç okuma aralığı beklediğidir.
- **OA5.** Çeşitlilik iki sayıdır: **ayrı şema sayısı** ve **birleştirilemeyen alan sayısı**. Bir
  alan ortak anahtarı olmayan bir şemadaysa, eşlemesiz serbest metinse, birimi çevrilmesi gereken
  bir nicelikse ya da zaman biçimi kümenin dönem eksenine oturmuyorsa birleşmez.
- **OA6.** Hiçbir dağıtık işleme motoru, dosya biçimi ya da satıcı adı yazılmaz; araçlar türleriyle
  anılır.

#### Hacim: Bayt ve Satır

```python
# kume.py — bolgesel olcum aginin KURGU kumesi. MODELDIR: gercek bir kume
# yerine ayni yapiyi ureten bir uretec yazildi; tohum gorunur ve cikti
# kosumdan kosuma aynidir. 3199 satir, 1329 abone, uc donem, bes bolge.
import math, sys

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26),
         ("bati", 0.14, 14), ("merkez", 0.18, 23)]
ALAN = ("abone_no", "bolge", "donem", "hane", "memnuniyet", "tuketim_m3")


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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


KUME = []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() < 0.046:                      # kayda girememis sayac
        continue
    no, q = 10001 + i, uretec(TOHUM + 7000 + 10001 + i)
    for d in range(ayrik(q(), [0.05, 0.12, 0.21, 0.62])):
        v = 0.0 if q() < 0.038 else math.floor(
            b[2] * math.exp((q() + q() + q() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        KUME.append((no, b[0], d + 1, hane, memnun, v))
        q()

SAYAC = len({s[0] for s in KUME})
bayt = sys.getsizeof(KUME) + sum(
    sys.getsizeof(s) + sum(sys.getsizeof(a) for a in s) for s in KUME)
SATIR_BAYT = bayt / len(KUME)
print(f"tohum {TOHUM}; {len(KUME)} satir, {len(ALAN)} alan, {SAYAC} okuyan sayac")
print(f"bellekte {bayt} bayt, satir basina {SATIR_BAYT:.1f} bayt")

GIB = 1024 ** 3
print(f"{'calisma alani':<16}{'ara sonuc payi':>16}{'kumeye kalan':>14}"
      f"{'esik satir':>14}{'kat':>8}")
for gib in (4, 12, 64):
    for pay, ad in ((1, "yok"), (2, "kume kadar")):
        kalan = gib * GIB / pay
        esik = int(kalan / SATIR_BAYT)
        print(f"{str(gib) + ' GiB':<16}{ad:>16}{gib // pay:>11} GiB"
              f"{esik:>14}{esik // len(KUME):>8}")
```

```
tohum 20260218; 3199 satir, 6 alan, 1260 okuyan sayac
bellekte 914929 bayt, satir basina 286.0 bayt
calisma alani     ara sonuc payi  kumeye kalan    esik satir     kat
4 GiB                        yok          4 GiB      15017121    4694
4 GiB                 kume kadar          2 GiB       7508560    2347
12 GiB                       yok         12 GiB      45051365   14082
12 GiB                kume kadar          6 GiB      22525682    7041
64 GiB                       yok         64 GiB     240273951   75109
64 GiB                kume kadar         32 GiB     120136975   37554
```

Hacim tek bir sayı değil, iki sayının bölümüdür. Pay tarafında çalışma alanı, payda tarafında
satır başına bayt durur ve bölüm bir **satır sayısı** verir. Kurgu küme 914.929 bayt tutuyor,
satır başına 286,0 bayt. Bu sayının kendisi bir tip kararının sonucudur ve o karar başka bir yerde
ölçüldü; burada değişmeyen şey, eşiğin bir sayı olarak yazılabilmesidir.

Tablonun altı satırı iki savunulabilir kararın çarpımıdır. Çalışma alanı üç değer alıyor, ara
sonuç payı iki. En dar karar 7.508.560 satırda eşiğe varıyor, en geniş karar 240.273.951 satırda.
Aradaki oran otuz iki kat ve iki uç da savunulabilir: ara sonuç payı istemeyen bir hesap yalnız
akan toplamlar tutar, kümenin kendisi kadar geçici yer isteyen bir hesap sıralama ya da birleştirme
yapar. **"Bu küme tek makineye sığmaz" cümlesi, altı satırdan hangisinin seçildiği yazılmadan
kurulamaz.**

Kurgu kümenin kendisi hiçbir satırda eşiğe yaklaşmıyor: en dar kararda bile eşiğin 2.347'de biri
kadar. Ölçekli analizin konusu bu küme değil, bu kümenin hangi hızla o eşiğe yürüdüğüdür.

Sonraki iki bölüm eşik olarak orta satırı, 12 GiB çalışma alanı ile küme kadar ara sonuç payını
alır ve 22.525.682 satırı sabitler. Bu bir seçimdir ve seçildiği için yazılıyor; eşik değiştiğinde
sonraki bütün sayılar aynı oranda kayar ve kayma yönü tablodan okunur.

#### Hız: Satır ve Dönem

```python

# hiz.py — hiz iki sayidir: satir/donem ve gecikme. Okuma sikligi bir
# karardir; sayacin kendisi degismeden ureten satir sayisi degisir.
ESIK = int((12 * GIB / 2) / SATIR_BAYT)     # 12 GiB calisma alani, kume kadar ara sonuc payi
BUYUME = 1.04                               # donem basina sayac artisi


def donem_sayisi(hiz, buyume):
    toplam, n, h = len(KUME), 0, hiz
    while toplam < ESIK and n < 10 ** 6:
        toplam, n, h = toplam + h, n + 1, h * buyume
    return n


print(f"esik {ESIK} satir; ag {SAYAC} sayac, donem basina buyume "
      f"yuzde {round((BUYUME - 1) * 100)}")
print(f"{'okuma sikligi':<20}{'okuma/donem':>12}{'satir/donem':>13}"
      f"{'sabit ag':>10}{'buyuyen ag':>12}{'gecikme':>9}")
for ad, sik in (("donemsel", 1), ("gunluk", 30), ("saatlik", 720),
                ("on bes dakikalik", 2880)):
    hiz = SAYAC * sik
    print(f"{ad:<20}{sik:>12}{hiz:>13}{donem_sayisi(hiz, 1.0):>10}"
          f"{donem_sayisi(hiz, BUYUME):>12}{sik:>9}")
```

```
esik 22525682 satir; ag 1260 sayac, donem basina buyume yuzde 4
okuma sikligi        okuma/donem  satir/donem  sabit ag  buyuyen ag  gecikme
donemsel                       1         1260     17875         168        1
gunluk                        30        37800       596          82       30
saatlik                      720       907200        25          18      720
on bes dakikalik            2880      3628800         7           6     2880
```

Hız, ağın büyümesi değildir. Tablodaki dört satırda **aynı 1.260 sayaç** var; değişen tek şey her
sayacın kaç kez okunduğu. Dönemsel okumada ağ 1.260 satır/dönem üretiyor ve eşiğe 17.875 dönem
uzakta; on beş dakikalık okumada aynı ağ 3.628.800 satır/dönem üretiyor ve eşik 7 dönem sonra.
Okuma sıklığı bir donanım gerçeği gibi görünür, oysa bir karardır ve bu kararın eşiğe kalan dönem
üzerindeki etkisi 2.554 kattır.

Beşinci sütun ikinci kararı ekliyor. Ağ dönem başına yüzde 4 büyüdüğünde dönemsel okumadaki 17.875
dönem 168 döneme iniyor. Yüzde 4'lük bir büyüme dönem dönem bakıldığında görünmez; birikimli
bakıldığında eşiği yüz kat yakınlaştırıyor. Hızın iki kaynağı vardır ve ikisi çarpılır: sıklık ile
büyüme.

Son sütun hızın ikinci birimidir. Rapor dönem başına bir kez yenileniyorsa, bir okumanın rapora
girmesi en çok bir dönem sürer; ama bu bekleme **okuma aralığı** cinsinden yazıldığında sayı
değişir. Dönemsel okumada bekleme 1 okuma aralığı, on beş dakikalık okumada 2.880 okuma aralığıdır
ve o sırada rapor dışında bekleyen satır 1.260'tan 3.628.800'e çıkar. Sıklığı artırmak veriyi
tazelemez; tazelenmemiş veri miktarını büyütür.

#### Çeşitlilik: Şema ve Alan

```python
# cesitlilik.py — cesitlilik iki sayidir: ayri sema sayisi ve birlestirilemeyen
# alan sayisi. MODELDIR: bes kaynagin semasi alan adi, birim ve zaman bicimi
# olarak yazildi; kaynaklarin kendisi uretilmedi.
ORTAK = "abone_no"
SEMA = {
    "abone kaydi": [("abone_no", "-", "-"), ("bolge", "ad", "-"),
                    ("hane", "kisi", "-"), ("memnuniyet", "1-5", "-")],
    "sayac okumasi": [("abone_no", "-", "-"), ("donem", "-", "donem no"),
                      ("tuketim_m3", "m3", "-")],
    "saha ziyareti": [("sayac_seri", "-", "-"), ("okunan", "litre", "-"),
                      ("bolge_kodu", "sayi", "-"), ("ziyaret_gunu", "-", "gun")],
    "destek talebi": [("talep_no", "-", "-"), ("abone_no", "-", "-"),
                      ("acilis", "-", "zaman damgasi"), ("konu", "metin", "-")],
    "tarife tablosu": [("basamak_metni", "metin", "-"), ("birim_ucret", "para", "-"),
                       ("gecerlilik", "-", "tarih araligi")],
}


def durum(sema, birim, zaman):
    if ORTAK not in [a for a, _, _ in SEMA[sema]]:
        return "anahtar yok"
    if birim == "litre":
        return "birim cevrilir"
    if birim == "metin":
        return "eslemesiz metin"
    if zaman not in ("-", "donem no"):
        return "zaman bicimi"
    return "birlesir"


def say(baslik):
    sebep, kopuk = {}, 0
    print(f"{baslik:<16}{'alan':>6}{'ortak anahtar':>15}{'birlesmeyen':>13}"
          f"{'sebep':>18}")
    for ad, alanlar in SEMA.items():
        d = [durum(ad, b, z) for _, b, z in alanlar]
        n = sum(1 for x in d if x != "birlesir")
        for x in d:
            if x != "birlesir":
                sebep[x] = sebep.get(x, 0) + 1
        kopuk += n
        print(f"{ad:<16}{len(alanlar):>6}"
              f"{('var' if ORTAK in [a for a, _, _ in alanlar] else 'yok'):>15}"
              f"{n:>13}{(sorted(set(x for x in d if x != 'birlesir')) or ['-'])[0]:>18}")
    print(f"ayri sema {len(SEMA)}, toplam alan "
          f"{sum(len(v) for v in SEMA.values())}, birlestirilemeyen alan {kopuk}")
    print("sebep dagilimi:", ", ".join(f"{k} {v}" for k, v in sorted(sebep.items())))
    return kopuk


k1 = say("sema (once)")
SEMA["saha ziyareti"].append(("abone_no", "-", "-"))     # sayac_seri esleme tablosu
SEMA["tarife tablosu"].append(("abone_no", "-", "-"))    # basamak esleme tablosu
print()
k2 = say("sema (sonra)")
print(f"iki esleme tablosu birlestirilemeyen alani {k1}'dan {k2}'ya indirir; "
      f"anahtar yoklugu kapanir, birim ve zaman bicimi kapanmaz")
```

```
sema (once)       alan  ortak anahtar  birlesmeyen             sebep
abone kaydi          4            var            0                 -
sayac okumasi        3            var            0                 -
saha ziyareti        4            yok            4       anahtar yok
destek talebi        4            var            2   eslemesiz metin
tarife tablosu       3            yok            3       anahtar yok
ayri sema 5, toplam alan 18, birlestirilemeyen alan 9
sebep dagilimi: anahtar yok 7, eslemesiz metin 1, zaman bicimi 1

sema (sonra)      alan  ortak anahtar  birlesmeyen             sebep
abone kaydi          4            var            0                 -
sayac okumasi        3            var            0                 -
saha ziyareti        5            var            2    birim cevrilir
destek talebi        4            var            2   eslemesiz metin
tarife tablosu       4            var            2   eslemesiz metin
ayri sema 5, toplam alan 20, birlestirilemeyen alan 6
sebep dagilimi: birim cevrilir 1, eslemesiz metin 2, zaman bicimi 3
iki esleme tablosu birlestirilemeyen alani 9'dan 6'ya indirir; anahtar yoklugu kapanir, birim ve zaman bicimi kapanmaz
```

Ayrı şema sayısı çeşitliliği ölçmez. Beş kaynak beş şema taşıyor ve bu sayı iki koşumda da beş
kalıyor; oynayan sayı **birleştirilemeyen alan sayısıdır**: 18 alanın 9'u kümeye bağlanamıyor.
Sebep dağılımı bu dokuzu üçe ayırıyor — 7 alan ortak anahtarı olmayan iki şemadan geliyor, 1 alan
eşlemesiz serbest metin, 1 alan kümenin dönem eksenine oturmayan bir zaman biçimi taşıyor.

İkinci koşum bir karar uyguluyor: sayaç seri numarasını abone numarasına çeviren bir eşleme tablosu
ile tarife basamağı metnini kümenin basamak adlarına çeviren ikinci bir eşleme tablosu kuruluyor.
Birleştirilemeyen alan 9'dan 6'ya iniyor, ama sebep dağılımı yer değiştiriyor: anahtar yokluğu
tümüyle kapanıyor, buna karşılık zaman biçimi çelişkisi 1'den 3'e çıkıyor, çünkü artık bağlanabilen
iki şemanın kendi zaman alanları da sayıya giriyor. Eşleme bir alanı bağlar, birimini ve zaman
eksenini hizalamaz.

Sayının yönü de dikkate değer. İkinci koşumda toplam alan 18'den 20'ye çıkıyor, çünkü eşleme iki
şemaya birer alan ekliyor; birleştirilemeyen alan oranı yüzde 50'den yüzde 30'a iniyor ama mutlak
sayı yalnız üç azalıyor. Çeşitliliği kapatmanın bedeli, kapanan her alan için bakımı süren bir
eşleme tablosudur ve bu tablolar kümenin kendisinde görünmez.

Üç birim yan yana konduğunda kümenin ölçek tablosu çıkıyor. Hacim eşiği 22.525.682 satır, hız on
beş dakikalık okumada 3.628.800 satır/dönem, çeşitlilik 5 şema ve 6 birleştirilemeyen alan. Bu üç
sayının hiçbiri "büyük" ya da "küçük" değildir; her biri bir karar çiftinin bölümüdür ve kararlar
tablonun içinde yazılıdır.

#### Özet

- Hacim iki sayının bölümüdür: kurgu küme satır başına 286,0 bayt tutar ve tek makineye sığmama
  eşiği çalışma alanı ile ara sonuç payı kararlarına göre 7.508.560 ile 240.273.951 satır arasında
  otuz iki kat oynar.
- Hız ağın büyümesi değil okuma sıklığıdır: aynı 1.260 sayaç dönemsel okumada 1.260, on beş
  dakikalık okumada 3.628.800 satır/dönem üretir ve eşiğe kalan dönem 17.875'ten 7'ye iner.
- Dönem başına yüzde 4'lük bir ağ büyümesi dönemsel okumada eşiği 17.875 dönemden 168 döneme
  yaklaştırır; sıklık ile büyüme çarpılır.
- Gecikme hızın ikinci birimidir: dönemsel yenilenen bir raporda bekleme dönemsel okumada 1, on beş
  dakikalık okumada 2.880 okuma aralığıdır ve bekleyen satır 3.628.800'e çıkar.
- Çeşitliliği ayrı şema sayısı ölçmez; beş şema iki koşumda da beştir, oynayan sayı 18 alanın
  birleştirilemeyen 9'udur ve iki eşleme tablosu bu sayıyı 6'ya indirir.
- Eşleme anahtar yokluğunu kapatır, birim ve zaman biçimi çelişkisini kapatmaz: zaman biçimi
  sebebiyle birleşmeyen alan 1'den 3'e çıkar.

#### Sonraki Adım

Bu ders kümenin tek makineye sığmadığı noktayı bir satır sayısı olarak yazdı, ama o noktadan sonra
hesabın ne olacağını sormadı. Küme eşiği geçtiğinde iş bölünmek zorundadır ve bölmenin ilk sorusu
şudur: bir toplulaştırma hangi parçalara ayrılabilir ve parçalar arasında kaç kayıt taşınır. Sonraki
ders dağıtık hesaplamanın en yalın soyutlamasını, eşle ve indirge işlevlerini ders içinde yazar ve
koşturur; ölçü **karışım geçişinde taşınan kayıt sayısıdır** — aynı toplulaştırma önce indirgeyip
sonra karıştırdığında ve tersinde kaç kayıt taşınıyor ve hangi hesap bu modele hiç girmiyor.

### Eşle-İndirge Modeli

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/esle-indirge-modeli

Dağıtık hesaplamanın en yalın soyutlaması üç işlevle kurulup koşturuluyor ve ölçü karışım geçişinde taşınan kayıt sayısı oluyor: sekiz kaynak parçasına yayılmış kurgu kümede bölge ortalaması önce karıştırıp sonra indirgeyen sırada 3.199, önce yerel indirgeyip sonra karıştıran sırada 40 kayıt taşır ve kazanç 79,97 kattır. Kazanç anahtar sayısına bağlıdır ve abone anahtarında 2,53 kata iner. Aynı soru iki anahtarla iki değer verir: doğu ortalaması satır ağırlıklı 24,674 m³, abone ortalamalarının ortalaması olarak 24,814 m³'tür ve iki geçişin taşıdığı kayıt 40'tan 1.305'e çıkar. İndirge işlevi birleşmeli değilse hesap modele girmez: toplam ve en büyük yedi ayrı parça sayısında da değişmez, parça ortancalarının ortancası yedi ayrı değer verir ve açıklığı 0,2175 m³'tür.

Önceki ders kümenin tek makineye sığmadığı noktayı bir satır sayısı olarak yazdı: seçilen kararla
22.525.682 satır. O noktadan sonra hesap bölünmek zorundadır, ama "bölünmek" tek başına bir tarif
değildir. Bölünen şey nedir, parçalar arasında ne gider ve kaç kayıt gider.

Bu ders dağıtık hesaplamanın en yalın soyutlamasını kurar ve koşturur. Soyutlama üç işlevdir:
**eşle** bir satırı bir anahtar ile bir ara değere çevirir, **karışım geçişi** ara değerleri
anahtara göre indirgeyicilere taşır, **indirge** aynı anahtarın ara değerlerini tek bir değere
kapatır. Dersin ölçüsü orta adımdadır: karışım geçişinde kaç kayıt taşınıyor. Yanında duran ikinci
sayı, o kaydı taşımadan önce indirgemenin ne kadarının yapıldığıdır.

- **OA7.** Küme K03–K05'ten devralınır ve **kurgudur**: 3.199 satır, 1.329 abone, üç dönem, beş
  bölge, tarife basamakları 10/25/40 m³. Tohum 20260218 ve çıktı koşumdan koşuma aynıdır.
  **Modeldir.**
- **OA8.** Hiçbir dağıtık işleme motoru kurulmaz; kaynak **sekiz parçaya yayılmış** sayılır ve üç
  işlev standart kitaplıkla yazılır. **Modeldir.**
- **OA9.** Karışım geçişinde taşınan kayıt, indirgeyicilere gönderilen (anahtar, ara değer)
  çiftlerinin sayısıdır. Ham milisaniye ölçülmez.
- **OA10.** Ara değer bir karardır. Ortalama için ara değer tek bir sayı değil, **(toplam, sayı)**
  çiftidir; bu seçim indirge işlevini birleşmeli kılar.
- **OA11.** Bir hesabın modele girmesi, indirge işlevinin kısmi sonuçlar üzerinde **birleşmeli ve
  değişmeli** olmasına bağlıdır. Sınama koşularak yapılır: aynı hesap yedi ayrı parça sayısıyla
  koşulur ve kaç ayrı sayı çıktığı sayılır.
- **OA12.** Parça sınırı satır sayısına göre çekilir; parçalar kaynağın verdiği sıradadır.

#### Üç İşlev

```python
# esle_indirge.py — bolgesel olcum aginin KURGU kumesi ve uc islev: esle,
# karisim gecisi, indirge. MODELDIR: hicbir dagitik motor kurulmaz; kaynak
# PARCA tane parcaya yayilmis sayilir ve karisim gecisinde tasinan kayit sayilir.
import math

TOHUM, HAM, M32, PARCA = 20260218, 1400, 0xFFFFFFFF, 8
BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26),
         ("bati", 0.14, 14), ("merkez", 0.18, 23)]


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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


def basamak(v):
    return "0-10" if v <= 10 else "10-25" if v <= 25 else "25-40" if v <= 40 else "40 ustu"


KUME = []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() < 0.046:                      # kayda girememis sayac
        continue
    no, q = 10001 + i, uretec(TOHUM + 7000 + 10001 + i)
    for d in range(ayrik(q(), [0.05, 0.12, 0.21, 0.62])):
        v = 0.0 if q() < 0.038 else math.floor(
            b[2] * math.exp((q() + q() + q() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        KUME.append((no, b[0], d + 1, hane, memnun, v, basamak(v)))
        q()


def parcala(kume, p):
    n = len(kume)
    return [kume[i * n // p:(i + 1) * n // p] for i in range(p)]


def esle(satir, anahtar):
    return (anahtar(satir), (satir[5], 1))        # ara deger: (toplam, sayi)


def birlestir(a, b):
    return (a[0] + b[0], a[1] + b[1])


def kos(parcalar, anahtar, birlestirici):
    """Esle -> (varsa yerel indirge) -> karisim gecisi -> indirge.
    Donen ikinci sayi karisim gecisinde tasinan kayit sayisidir."""
    tasinan = []
    for p in parcalar:
        ciftler = [esle(s, anahtar) for s in p]
        if birlestirici:
            yerel = {}
            for k, v in ciftler:
                yerel[k] = birlestir(yerel[k], v) if k in yerel else v
            ciftler = sorted(yerel.items(), key=lambda x: str(x[0]))
        tasinan += ciftler
    kova = {}
    for k, v in tasinan:
        kova[k] = birlestir(kova[k], v) if k in kova else v
    return {k: (v[0] / v[1], v[1]) for k, v in kova.items()}, len(tasinan)


P = parcala(KUME, PARCA)
sonuc, tasinan = kos(P, lambda s: s[1], False)
print(f"tohum {TOHUM}; {len(KUME)} satir, {PARCA} kaynak parcasi "
      f"{[len(x) for x in P]}")
print(f"{'bolge':<10}{'satir':>7}{'ortalama m3':>14}")
for b in sorted(sonuc):
    print(f"{b:<10}{sonuc[b][1]:>7}{sonuc[b][0]:>14.3f}")
print(f"dogu - kuzey = {sonuc['dogu'][0] - sonuc['kuzey'][0]:.3f} m3; "
      f"karisim gecisinde tasinan kayit {tasinan}")
```

```
tohum 20260218; 3199 satir, 8 kaynak parcasi [399, 400, 400, 400, 400, 400, 400, 400]
bolge       satir   ortalama m3
bati          476        13.778
dogu          557        24.674
guney         645        16.602
kuzey         921        20.210
merkez        600        22.396
dogu - kuzey = 4.463 m3; karisim gecisinde tasinan kayit 3199
```

Küme sekiz parçaya yayıldı ve hiçbir yerde bütünüyle bir araya gelmedi, ama kursun çıpası
değişmedi: doğu ortalaması 24,674 m³, kuzey 20,210 m³, fark 4,463 m³. Model doğru kurulduğunda
bölünme cevabı oynatmaz; oynattığı şey cevabı üretmek için taşınan kayıttır.

Bu ilk koşumda taşınan kayıt 3.199, yani kümenin tamamı. Eşle her satır için bir çift üretti ve
çiftlerin hepsi indirgeyicilere gitti. İndirge sonra çalıştı ve beş sayı verdi. Beş sayı için 3.199
kayıt taşındı.

Ara değerin ne olduğu bu noktada görünmez bir karardır. Eşle işlevi okumayı tek başına
göndermiyor, yanına 1 sayısını koyup **(toplam, sayı)** çifti gönderiyor. Tek bir sayı gönderilse
indirge işlevi ortalamaların ortalamasını almak zorunda kalırdı ve parça boyutları eşit olmadığı
anda sonuç kayardı. Çift göndermek taşınan kaydı iki katına çıkarmaz — kayıt sayısı aynıdır, kaydın
içi genişler — ama indirge işlevini birleşmeli kılar. Dersin üçüncü bölümü bu özelliğin olmadığı
durumu ölçer.

#### Karışım Geçişinde Taşınan Kayıt

```python

# tasima.py — ayni toplulastirma iki sirada kosulur: once karistirip sonra
# indirgeyen sira ve once yerel indirgeyip sonra karistiran sira. Olcu,
# karisim gecisinde tasinan kayit sayisidir.
ANAHTAR = (("bolge", lambda s: s[1]), ("bolge + donem", lambda s: (s[1], s[2])),
           ("bolge + basamak", lambda s: (s[1], s[6])),
           ("abone_no", lambda s: s[0]))
print(f"{'gruplama anahtari':<18}{'anahtar':>9}{'once karistir':>15}"
      f"{'once indirge':>14}{'kazanc':>9}{'sonuc ayni':>12}")
for ad, f in ANAHTAR:
    a, ta = kos(P, f, False)
    b, tb = kos(P, f, True)
    esit = all(abs(a[k][0] - b[k][0]) < 1e-9 for k in a)
    print(f"{ad:<18}{len(a):>9}{ta:>15}{tb:>14}{ta / tb:>8.2f}x"
          f"{('evet' if esit else 'hayir'):>12}")
print(f"birlestiricinin gonderdigi kayit, parca basina ayri anahtar sayisidir; "
      f"{PARCA} parca ve 5 bolge {PARCA * 5} kayit eder")
```

```
gruplama anahtari   anahtar  once karistir  once indirge   kazanc  sonuc ayni
bolge                     5           3199            40   79.97x        evet
bolge + donem            15           3199           120   26.66x        evet
bolge + basamak          18           3199           137   23.35x        evet
abone_no               1260           3199          1265    2.53x        evet
birlestiricinin gonderdigi kayit, parca basina ayri anahtar sayisidir; 8 parca ve 5 bolge 40 kayit eder
```

Sıra kararı sonucu oynatmıyor, taşımayı oynatıyor. Dört satırın hepsinde son sütun "evet" diyor:
önce karıştırıp sonra indirgemekle önce yerel indirgeyip sonra karıştırmak aynı beş sayıyı veriyor.
Değişen şey karışım geçişinden geçen kayıttır ve bölge anahtarında 3.199'dan 40'a iniyor —
**79,97 kat**.

Kırkın nereden geldiği tek satırlık bir çarpımdır: sekiz parça, her parçada beş bölge. Yerel
indirgeme bir parçadaki bütün satırları o parçanın anahtar sayısına indirir; taşınan kayıt artık
küme boyundan değil, **parça sayısı ile parça içindeki ayrı anahtar sayısından** gelir. Küme bin
kat büyüse de bölge anahtarında taşınan kayıt kırk kalır.

Son satır sınırı çiziyor. Abone anahtarında 1.260 ayrı anahtar var ve satır sayısı 3.199; bir
abonenin satırları en çok üç tane. Yerel indirgeme parça başına ortalama iki buçuk satırı bire
indiriyor ve kazanç 2,53 kata düşüyor. Birleştiricinin kazancı **anahtar sayısının satır sayısına
oranından** gelir; anahtar sayısı satır sayısına yaklaştıkça birleştirici taşıyacak bir şey
bulamaz.

Ortadaki iki satır bu oranın nasıl işlediğini gösteriyor. Bölgeye dönem eklendiğinde anahtar sayısı
beşten on beşe çıkıyor ve taşınan kayıt tam olarak sekiz çarpı on beş, yani 120 oluyor: on beş
anahtarın hepsi sekiz parçanın hepsinde var. Basamak eklendiğinde anahtar sayısı on sekiz, ama
taşınan kayıt 144 değil 137. Aradaki yedi kayıt eksikliği bir kazanç değil bir seyrekliktir:
bölge ile basamağın on sekiz bileşiminden ikisi kümenin tamamında yalnız birkaç satır tutuyor ve
sekiz parçanın yedisinde hiç görünmüyor. Taşınan kayıt, anahtar sayısının değil **parça başına
gerçekten görülen anahtar sayısının** toplamıdır ve bu sayı ancak koşularak bilinir.

#### İki Geçiş ve Modele Girmeyen Hesap

```python

# gecis.py — bazi hesap tek karisim gecisine sigmaz, bazisi hic sigmaz.
# Once iki gecisli hesap, sonra indirge islevinin birlesmeli olup olmadigi
# sinanir: ayni hesap yedi ayri parca sayisiyla kosulur.
import statistics

abone, t1 = kos(P, lambda s: s[0], True)                  # 1. gecis: anahtar abone
BOLGESI = {s[0]: s[1] for s in KUME}
ikinci = [(BOLGESI[k], (v[0], 1)) for k, v in abone.items()]
P2 = parcala(ikinci, PARCA)                               # 2. gecis: anahtar bolge
t2 = sum(len({k for k, _ in p}) for p in P2)
ort2 = {}
for p in P2:
    for k, v in p:
        ort2[k] = birlestir(ort2[k], v) if k in ort2 else v
tek, tt = kos(P, lambda s: s[1], True)
print(f"{'bolge':<10}{'tek gecis: satir agirlikli':>28}"
      f"{'iki gecis: abone ortalamasi':>29}{'fark':>8}")
for b in sorted(tek):
    a2 = ort2[b][0] / ort2[b][1]
    print(f"{b:<10}{tek[b][0]:>28.3f}{a2:>29.3f}{a2 - tek[b][0]:>8.3f}")
print(f"tek gecis {tt} kayit tasir; iki gecis {t1} + {t2} = {t1 + t2} kayit tasir")

ortancalar = []
print(f"{'parca':<8}{'toplam m3':>12}{'en buyuk':>10}"
      f"{'parca ortancalarinin ortancasi':>32}")
for p in (2, 3, 4, 5, 8, 16, 32):
    par = parcala(KUME, p)
    o = statistics.median([statistics.median([x[5] for x in y]) for y in par])
    ortancalar.append(o)
    print(f"{p:<8}{sum(sum(x[5] for x in y) for y in par):>12.2f}"
          f"{max(max(x[5] for x in y) for y in par):>10}{o:>32.4f}")
kova = sum(len({x[5] for x in p}) for p in P)
print(f"gercek ortanca {statistics.median([s[5] for s in KUME])} m3; yedi parca "
      f"sayisi {len(set(ortancalar))} ayri deger verir, acikligi "
      f"{max(ortancalar) - min(ortancalar):.4f} m3")
print(f"okumalar iki ondalikli: {len({s[5] for s in KUME})} ayri deger; sayim "
      f"birlestiricisi {kova} kayit tasir, kazanc {len(KUME) / kova:.2f}x")
```

```
bolge       tek gecis: satir agirlikli  iki gecis: abone ortalamasi    fark
bati                            13.778                       13.935   0.158
dogu                            24.674                       24.814   0.140
guney                           16.602                       16.728   0.127
kuzey                           20.210                       20.351   0.141
merkez                          22.396                       22.483   0.087
tek gecis 40 kayit tasir; iki gecis 1265 + 40 = 1305 kayit tasir
parca      toplam m3  en buyuk  parca ortancalarinin ortancasi
2           63060.63     52.47                         18.9900
3           63060.63     52.47                         18.9700
4           63060.63     52.47                         18.9250
5           63060.63     52.47                         18.8400
8           63060.63     52.47                         18.9725
16          63060.63     52.47                         18.8900
32          63060.63     52.47                         19.0575
gercek ortanca 18.98 m3; yedi parca sayisi 7 ayri deger verir, acikligi 0.2175 m3
okumalar iki ondalikli: 1788 ayri deger; sayim birlestiricisi 2839 kayit tasir, kazanc 1.13x
```

İlk tablo bu dersin gizlenen kararıdır. "Bölge ortalaması" tek bir soru gibi durur, oysa iki ayrı
hesaptır. Tek geçişte anahtar bölgedir ve her satır eşit ağırlık taşır: doğu 24,674 m³. İki geçişte
önce abone anahtarıyla indirgenir, sonra abone sonuçları bölge anahtarıyla indirgenir ve her abone
eşit ağırlık taşır: doğu 24,814 m³. Fark 0,140 m³ ve beş bölgenin hepsinde aynı yönde. İkisi de
savunulabilir; hangisinin seçildiği yazılmadığında rapordaki sayı bir bulgu değil bir seçimdir.

Bedel de tabloda. Tek geçiş 40 kayıt taşır, iki geçiş 1.265 artı 40, toplam 1.305 kayıt — **otuz
üç kat**. Geçiş sayısı arttıkça taşınan kayıt katlanır, çünkü ara geçişin çıktısı bir sonraki
geçişin girdisi olur ve o çıktı anahtar sayısı kadar büyüktür.

İkinci tablo modele girmenin sınırını çiziyor. Toplam ve en büyük yedi ayrı parça sayısında da
kılını kıpırdatmıyor: 63.060,63 m³ ve 52,47 m³. İndirge işlevleri kısmi sonuçlar üzerinde birleşmeli
ve değişmelidir, dolayısıyla parçalama görünmez kalır. Ortanca ise yedi parça sayısında **yedi ayrı
değer** veriyor; açıklık 0,2175 m³ ve hiçbiri gerçek ortancaya, 18,98 m³'e eşit değil. Parça
ortancalarının ortancası bir yaklaşıklık bile değildir; parça sayısına bağlı bir sayıdır.

Son satır çıkış yolunun bedelini veriyor. Ortanca ara değeri ortanca yerine bir **sayım** olduğunda
modele girer, ama bu kümede kurtarmaz: okumalar iki ondalıkla yazıldığı için 1.788 ayrı değer var
ve sayım birleştiricisi 2.839 kayıt taşıyor, yani kazanç 1,13 kat. Ondalık sayısı bir gösterim
kararıdır ve burada karışım geçişinin boyunu belirliyor: bir ondalıkla yazılan aynı okumalar
birleştiriciyi çalışır kılardı.

#### Özet

- Üç işlev koştu ve küme sekiz parçaya yayıldığı halde çıpa değişmedi: doğu 24,674 m³, kuzey
  20,210 m³, fark 4,463 m³.
- Sıra kararı sonucu değil taşımayı oynatır: bölge anahtarında önce karıştırmak 3.199, önce yerel
  indirgemek 40 kayıt taşır ve kazanç 79,97 kattır.
- Birleştiricinin taşıdığı kayıt parça sayısı ile parça içindeki ayrı anahtar sayısının çarpımıdır;
  abone anahtarında 1.260 anahtar kazancı 2,53 kata indirir.
- Aynı soru iki anahtarla iki değer verir: doğu ortalaması satır ağırlıklı 24,674 m³, abone
  ortalamalarının ortalaması olarak 24,814 m³'tür ve iki geçiş 40 yerine 1.305 kayıt taşır.
- Bir hesap indirge işlevi birleşmeli değilse modele girmez: toplam ve en büyük yedi parça sayısında
  da aynı çıkar, parça ortancalarının ortancası yedi ayrı değer verir ve açıklığı 0,2175 m³'tür.
- Ortanca ara değeri sayıma çevrildiğinde modele girer ama iki ondalıklı okumalarda 1.788 ayrı
  değer bulunur ve kazanç 1,13 kata düşer.

#### Sonraki Adım

Bu ders taşınan kaydı saydı ve taşımanın sıraya bağlı olduğunu gösterdi, ama üç işlevi kimin
koşturduğunu hiç sormadı. Gerçek bir işte parçalar bir yerde tanımlanır, görevler dağıtılır,
başarısız görev yeniden denenir ve sonuçlar toplanır; bunların hiçbiri satır işlemez ve hepsi iş
başına eklenir. Sonraki ders küme üzerinde veri çerçevesi işlemenin **eşgüdüm kalemini** sayar:
bir işlem zinciri kaç aşamaya bölünüyor, aşama başına kaç kalem ekleniyor, yeniden deneme bu sayıyı
kaça çıkarıyor ve hangi iş boyutunda kalem sayısı işlenen satır sayısını geçiyor.

### Dağıtık Analiz Motorları

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/dagitik-analiz-motorlari

Küme üzerinde veri çerçevesi işlemenin iş başına eklediği eşgüdüm kaleminin sayılması: sekiz işlemlik bir zincirin üç geniş işlemi dört aşama sınırı çeker ve sekiz bölümde 100 eşgüdüm kalemi eder; kalemlerin hiçbiri satır işlemez ve sayıları iş boyundan bağımsızdır. Yeniden deneme baskın kalem değildir, yüzde 2'lik başarısızlıkta kalem sayısını yüzde 0-2, yüzde 15'lik başarısızlıkta yüzde 8-23 artırır. Ölçü bir eşgüdüm kalemine düşen satır sayısıdır: 3.199 satırda 61,5, 3.199.000 satırda 31.990 satır düşer ve oran 32.878'de doyar. Kalem başına 10.000 satır hedefi dört aşamalı zincirde 520.000, iki aşamalı zincirde 260.000 satırda karşılanır.

Önceki ders üç işlevi yazdı ve karışım geçişinde taşınan kaydı saydı, ama işlevleri kimin
koşturduğunu sormadı. Gerçek bir küme üzerinde bir veri çerçevesi zinciri yazıldığında zinciri
yazan kişi işlemleri sıralar; motor ise o sıralamayı **aşamalara** böler, her aşama için bölümleri
tanımlar, görevleri dağıtır, başarısız görevi yeniden dener ve sonuçları toplar.

Bu işlerin hiçbiri satır işlemez ve hiçbiri zincirde yazmaz. Zinciri yazan kişi bir sorgu yazdığını
sanır; motor o sorgudan bir görev çizelgesi çıkarır ve çizelgenin uzunluğu sorgunun uzunluğundan
değil, sorgunun hangi noktalarda veriyi yeniden dağıtmak zorunda kaldığından gelir. Dersin ölçüsü
budur: **eşgüdüm kalemi** — iş başına eklenen ve
iş boyuyla büyümeyen, sayılabilir kalemler. Yanında duran ikinci sayı, bir eşgüdüm kalemine kaç
satır düştüğüdür. Dağıtık sistem kuramı, bölümleme ve eşgüdüm M19 ile M22'de kuruldu ve burada
tekrarlanmaz; yazılan tek şey sayılan kalemdir.

- **OA13.** Hiçbir dağıtık işleme motoru kurulmaz ve hiçbir ürün adı yazılmaz. Zincir, aşama ve
  kalem standart kitaplıkla modellenir. **Modeldir.**
- **OA14.** Bir işlem **dar**dır, yani bölüm içinde biter (süzme, seçme, türetme); ya da
  **geniş**tir, yani karışım geçişi ister (gruplama, sıralama, birleştirme). Aşama sınırı geniş
  işlemde çekilir.
- **OA15.** Eşgüdüm kalemi dört türdür: bölüm tanımı, görev gönderimi, görev sonucu ve aşama
  toplama. İlk üçü aşama başına bölüm sayısı kadar, sonuncusu aşama başına birer tane sayılır.
- **OA16.** Bir görevin başarısızlık olasılığı bir varsayımdır ve iki değerle sınanır: 0,02 ve
  0,15. En çok üç deneme yapılır; her yeniden deneme iki kalem ekler. Üreteç kendi yazılır, tohum
  20260218'dir.
- **OA17.** Bölüm sayısı iş boyundan türetilir: parça boyu 400.000 satır, en az dört bölüm. İkisi
  de karardır.
- **OA18.** Kabul edilebilirlik ölçütü bir karardır: bir eşgüdüm kalemine en az 10.000 satır
  düşmelidir.

#### Zincir Kaç Aşamaya Bölünüyor

```python
# zincir.py — bir veri cercevesi zinciri asamalara bolunur. MODELDIR: hicbir
# dagitik motor kurulmaz; zincirin islemleri dar (bolum ici biter) ve genis
# (karisim gecisi ister) diye isaretlenir ve asama siniri genis islemde cekilir.
ZINCIR = [("suz: donem 1-3", "dar"), ("turet: tarife basamagi", "dar"),
          ("grupla: abone", "genis"), ("turet: abone ortalamasi", "dar"),
          ("suz: ortalama > 10 m3", "dar"), ("grupla: bolge", "genis"),
          ("sirala: ortalama", "genis"), ("ilk 3", "dar")]
BOLUM = 8


def asamala(zincir):
    asamalar, su = [], []
    for ad, tur in zincir:
        su.append((ad, tur))
        if tur == "genis":
            asamalar.append(su)
            su = []
    if su:
        asamalar.append(su)
    return asamalar


A = asamala(ZINCIR)
print(f"{len(ZINCIR)} islem, {sum(1 for _, t in ZINCIR if t == 'genis')} genis, "
      f"{len(A)} asama, {BOLUM} bolum")
print(f"{'asama':<7}{'islem':>6}{'sinir':>9}  {'icerik'}")
for i, a in enumerate(A, 1):
    print(f"{i:<7}{len(a):>6}{a[-1][1]:>9}  {' -> '.join(ad for ad, _ in a)}")

# Esgudum kalemi: her asamada her bolum icin bolum tanimi, gorev gonderimi ve
# gorev sonucu; her asama icin bir de asama toplama kalemi.
KALEM = (("bolum tanimi", "bolum"), ("gorev gonderimi", "bolum"),
         ("gorev sonucu", "bolum"), ("asama toplama", "asama"))
print(f"{'kalem turu':<18}{'olcek':>8}{'asama':>7}{'bolum':>7}{'kalem':>7}")
toplam = 0
for ad, olcek in KALEM:
    n = len(A) * BOLUM if olcek == "bolum" else len(A)
    toplam += n
    print(f"{ad:<18}{olcek:>8}{len(A):>7}{BOLUM if olcek == 'bolum' else '-':>7}{n:>7}")
print(f"{'toplam':<18}{'':>8}{'':>7}{'':>7}{toplam:>7}")
```

```
8 islem, 3 genis, 4 asama, 8 bolum
asama   islem    sinir  icerik
1           3    genis  suz: donem 1-3 -> turet: tarife basamagi -> grupla: abone
2           3    genis  turet: abone ortalamasi -> suz: ortalama > 10 m3 -> grupla: bolge
3           1    genis  sirala: ortalama
4           1      dar  ilk 3
kalem turu           olcek  asama  bolum  kalem
bolum tanimi         bolum      4      8     32
gorev gonderimi      bolum      4      8     32
gorev sonucu         bolum      4      8     32
asama toplama        asama      4      -      4
toplam                                      100
```

Zinciri yazan kişi sekiz işlem yazdı; motor dört aşama kurdu. Aşama sayısı zincirde yazmaz, üç
geniş işlemden türer ve zincirin okunuşunda görünmez. İkinci aşamanın üç işlemi bir arada durması
da bir motor kararıdır: iki dar işlem kendinden önceki karışım geçişinin çıktısı üzerinde
çalıştığı için ayrı bir aşamaya gerek kalmıyor.

Bir işlemin dar mı geniş mi sayılacağı da verilmiş bir gerçek değil, bölümleme kararının
sonucudur. Zincirin üçüncü işlemi abone anahtarıyla gruplama; küme abone anahtarına göre
bölümlenmişse bir abonenin bütün satırları zaten aynı bölümdedir ve gruplama karışım geçişi
istemez, yani dar olur. Aynı işlem dönem anahtarına göre bölümlenmiş bir kümede geniştir. Tablodaki
dört aşama, kümenin abone anahtarına göre bölümlenmediği varsayımına dayanıyor; bölümleme anahtarı
değişince aşama sayısı üçe, eşgüdüm kalemi de aynı oranda iner. **Aşama sayısı zincirin değil,
zincir ile bölümlemenin birlikte verdiği sayıdır.**

Alt tablo dersin ölçüsünü veriyor. Dört aşama ve sekiz bölüm, dört kalem türünde toplam **100
eşgüdüm kalemi** ediyor. Bu yüz kalemin hiçbiri bir satır okumaz, bir değer toplamaz, bir grup
kurmaz. Hepsi işin **çevresindedir** ve varlıkları zincirde de sorguda da yazmaz. Kalem sayısı iş
boyundan tümüyle bağımsızdır: aynı zincir üç bin satırda da üç milyar satırda da yüz kalem ister.

Dört kalem türünün ne olduğunu ayırmak da gerekiyor, çünkü üçü bölüm sayısıyla, biri aşama
sayısıyla ölçekleniyor. **Bölüm tanımı**, bir aşamada bir bölümün hangi satırlardan sorumlu
olduğunun belirlenmesidir. **Görev gönderimi**, o tanımın bir çalıştırıcıya verilmesidir. **Görev
sonucu**, çalıştırıcının bitirdiğini bildirmesi ve çıktısının kaydedilmesidir. Bu üçü her bölüm
için ayrı ayrı sayılır ve bölüm sayısıyla çarpılır. **Aşama toplama** ise aşamanın bütün
bölümlerinin bittiğinin görülmesi ve bir sonraki aşamanın açılmasıdır; aşama başına bir tanedir ve
bölüm sayısından etkilenmez. Yüz kalemin doksan altısı ilk üç türden, dördü sonuncudan geliyor.

#### Yeniden Deneme Baskın Kalem Değildir

```python

# deneme.py — gorev basarisizligi ve yeniden deneme kalemi. Uretec kendi
# yazilir, tohum gorunur; cikti kosumdan kosuma aynidir. MODELDIR: gorev
# basarisizlik olasiligi bir varsayimdir ve iki degerle sinanir.
TOHUM, M32, ENCOK = 20260218, 0xFFFFFFFF, 3


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


def kalem_sayisi(asama, bolum):
    return asama * bolum * 3 + asama


def yeniden_deneme(asama, bolum, q, tohum):
    r, yeniden = uretec(tohum), 0
    for _ in range(asama * bolum):
        for _ in range(ENCOK - 1):
            if r() < q:
                yeniden += 1
            else:
                break
    return yeniden


print(f"en cok {ENCOK} deneme; her yeniden deneme 2 kalem ekler "
      f"(yeniden gonderim ve yeniden sonuc)")
print(f"{'bolum':<8}{'gorev':>7}{'temel kalem':>13}{'q=0.02 ek':>11}"
      f"{'q=0.02 artis':>14}{'q=0.15 ek':>11}{'q=0.15 artis':>14}")
for b in (4, 8, 16, 64, 256):
    g, temel = len(A) * b, kalem_sayisi(len(A), b)
    e1 = 2 * yeniden_deneme(len(A), b, 0.02, TOHUM + 91000 + b)
    e2 = 2 * yeniden_deneme(len(A), b, 0.15, TOHUM + 91000 + b)
    print(f"{b:<8}{g:>7}{temel:>13}{e1:>11}{(temel + e1) / temel:>13.2f}x"
          f"{e2:>11}{(temel + e2) / temel:>13.2f}x")
```

```
en cok 3 deneme; her yeniden deneme 2 kalem ekler (yeniden gonderim ve yeniden sonuc)
bolum     gorev  temel kalem  q=0.02 ek  q=0.02 artis  q=0.15 ek  q=0.15 artis
4            16           52          0         1.00x         12         1.23x
8            32          100          0         1.00x          8         1.08x
16           64          196          4         1.02x         24         1.12x
64          256          772          6         1.01x         82         1.11x
256        1024         3076         40         1.01x        336         1.11x
```

Yeniden deneme, dağıtık işlemenin en görünür kalemidir ve tablo onu en küçük kalem olarak
gösteriyor. Yüzde 2'lik başarısızlıkta kalem sayısı en çok yüzde 2 artıyor; yüzde 15'lik
başarısızlıkta bile artış yüzde 8 ile 23 arasında kalıyor. İlk satırdaki 1,23 katın diğerlerinden
yüksek olması bir eğilim değil, on altı görevlik küçük bir sayımın oynamasıdır — aynı oran 1.024
görevde 1,11 kata oturuyor.

Baskın kalem tablonun üçüncü sütunundadır. Bölüm sayısı dörtten 256'ya çıktığında temel kalem
52'den 3.076'ya, altmış kata yakın büyüyor ve bu büyüme başarısızlıktan değil **bölümleme
kararından** geliyor. Bölüm sayısını artırmak paralellik kazandırır ve aynı hareketle eşgüdüm
kalemini doğrusal olarak büyütür. Yeniden deneme bu tablonun gürültüsüdür; kararı veren sayı bölüm
sayısıdır.

Kalemin ne olmadığını yazmak da gerekiyor. Eşgüdüm kalemi bir iş birimi değildir; hesabı
hızlandıran bir kod değişikliği kalem sayısını düşürmez, çünkü kalemler hesabın içinde değil
çevresindedir. Bir bölümün işi on kat hızlansa da o bölüm için hâlâ bir bölüm tanımı, bir görev
gönderimi ve bir görev sonucu sayılır. Kalem sayısını düşüren üç karar vardır ve üçü de zincirin
kendisinde alınır: aşama sayısını azaltmak, bölüm sayısını azaltmak ve gruplama anahtarını
bölümleme anahtarıyla hizalamak.

#### Kalem Başına Düşen Satır

```python

# olcek.py — kalem sayisi is boyundan bagimsizdir, satir sayisi degildir.
# Olcu: bir esgudum kalemine kac satir dusuyor. HEDEF bir karardir.
import math
HEDEF, PARCA_BOYU, EN_AZ = 10000, 400000, 4
ZINCIR_B = [("suz: donem 1-3", "dar"), ("turet: tarife basamagi", "dar"),
            ("grupla: bolge", "genis"), ("ilk 3", "dar")]
B = asamala(ZINCIR_B)


def bolum_sayisi(n):
    return max(EN_AZ, math.ceil(n / PARCA_BOYU))


def kalem(n, asama):
    b = bolum_sayisi(n)
    return kalem_sayisi(asama, b) + 2 * yeniden_deneme(asama, b, 0.02,
                                                       TOHUM + 91000 + b)


print(f"zincir A {len(A)} asama, zincir B {len(B)} asama; parca boyu "
      f"{PARCA_BOYU} satir, en az {EN_AZ} bolum, hedef {HEDEF} satir/kalem")
print(f"{'is boyu':<14}{'bolum':>7}{'A kalem':>9}{'A satir/kalem':>15}"
      f"{'B kalem':>9}{'B satir/kalem':>15}")
for n in (3199, 31990, 319900, 3199000, 31990000, 319900000):
    ka, kb = kalem(n, len(A)), kalem(n, len(B))
    print(f"{n:<14}{bolum_sayisi(n):>7}{ka:>9}{n / ka:>15.1f}{kb:>9}"
          f"{n / kb:>15.1f}")
esik = {}
for ad, asama in (("A", len(A)), ("B", len(B))):
    n = 1000
    while n / kalem(n, asama) < HEDEF and n < 10 ** 9:
        n += 1000
    esik[ad] = n
print(f"hedefe varan en kucuk is boyu: zincir A {esik['A']} satir, "
      f"zincir B {esik['B']} satir; asamayi yariya indirmek esigi "
      f"{esik['A'] / esik['B']:.1f} kat kuculttu")
```

```
zincir A 4 asama, zincir B 2 asama; parca boyu 400000 satir, en az 4 bolum, hedef 10000 satir/kalem
is boyu         bolum  A kalem  A satir/kalem  B kalem  B satir/kalem
3199                4       52           61.5       26          123.0
31990               4       52          615.2       26         1230.4
319900              4       52         6151.9       26        12303.8
3199000             8      100        31990.0       50        63980.0
31990000           80      976        32776.6      490        65285.7
319900000         800     9730        32877.7     4866        65741.9
hedefe varan en kucuk is boyu: zincir A 520000 satir, zincir B 260000 satir; asamayi yariya indirmek esigi 2.0 kat kuculttu
```

Tablo eşgüdümün ne zaman görünmez kaldığını gösteriyor. Kurgu kümenin 3.199 satırında bir eşgüdüm
kalemine 61,5 satır düşüyor: iş, kendisini kuran işten çok daha küçük. Bin kat büyük bir kümede
31.990 satır düşüyor ve oran doyuyor — son iki satırda 32.777 ve 32.878 çıkıyor, çünkü bölüm sayısı
da iş boyuyla birlikte büyüyor ve tavan parça boyunun aşama başına düşen payından geliyor.

Doyum sayısı uydurma değil, iki karardan türer. Bölüm sayısı iş boyunun parça boyuna bölümüdür,
kalem sayısı da aşama çarpı bölüm çarpı üçtür; ikisi birbirini götürünce kalem başına düşen satır
parça boyunun aşama sayısının üç katına bölümüne oturur. Dört aşamada bu 400.000 bölü 12, yani
33.333 eder ve tabloda 32.878 olarak görünür — aradaki fark aşama toplama kalemleri ile yeniden
denemelerden gelir. Parça boyunu büyütmeden ya da aşama sayısını azaltmadan bu tavanın üstüne
çıkılamaz; iş boyunu büyütmek belli bir noktadan sonra oranı hiç oynatmaz.

Kabul ölçütü kalem başına 10.000 satır olarak yazıldığında dört aşamalı zincir bu ölçütü 520.000
satırda karşılıyor, iki aşamalı zincir 260.000 satırda. **Aşama sayısını yarıya indirmek eşiği tam
olarak yarıya indiriyor.** Aşama sayısı bir motor ayrıntısı gibi görünür, oysa zincirin yazılışında
verilen bir karardır: her geniş işlem bir aşama sınırı çeker ve o sınır bütün bölümler için kalem
üretir.

İki zincirin aynı soruyu sormadığını da yazmak gerekiyor. Zincir A önce abone anahtarıyla gruplar,
zincir B doğrudan bölge anahtarıyla; önceki dersin ölçtüğü fark buradan gelir — doğu ortalaması
24,814 m³ ile 24,674 m³ arasında 0,140 m³ oynar. Aşama azaltmak bedava değildir; azalttığı şey
bazen sorunun kendisidir.

Bu ders M26/K04'ün başarım konusundaki eşikle karıştırılmamalıdır. Orada aynı soru **iş birimi**
cinsinden soruldu ve dağıtımın kazanca döndüğü boy dengeli anahtarda 10.833 satır olarak ölçüldü;
ölçülen şey analistin kendi makinesindeki başarımdı. Burada ölçülen şey birim değil **kalemdir** ve
sorulan şey dağıtımın ne zaman ucuzladığı değil, eşgüdümün ne zaman görünmez kaldığıdır. İki eşik
farklı sorulara ait ve birbirinin yerine yazılamaz. Bir işin dağıtılması bu iki eşiğin ikisini de
geçmek zorundadır: kazanç eşiğini geçmeyen bir iş dağıtıldığında yavaşlar, kalem eşiğini geçmeyen
bir iş dağıtıldığında ise çalışır ama işin kendisinden çok çevresine harcanır.

#### Özet

- Sekiz işlemlik bir zincirin üç geniş işlemi dört aşama sınırı çeker; aşama sayısı zincirde yazmaz
  ve motor kararıdır.
- Dört aşama ve sekiz bölüm dört kalem türünde 100 eşgüdüm kalemi eder; kalemlerin hiçbiri satır
  işlemez ve sayıları iş boyundan bağımsızdır.
- Yeniden deneme baskın kalem değildir: yüzde 2'lik başarısızlıkta artış en çok yüzde 2, yüzde
  15'lik başarısızlıkta yüzde 8 ile 23 arasındadır.
- Baskın kalem bölümlemedir: bölüm dörtten 256'ya çıktığında temel kalem 52'den 3.076'ya çıkar.
- Bir eşgüdüm kalemine düşen satır 3.199 satırlık işte 61,5, 3.199.000 satırlık işte 31.990'dır ve
  oran 32.878'de doyar.
- Kalem başına 10.000 satır hedefi dört aşamalı zincirde 520.000, iki aşamalı zincirde 260.000
  satırda karşılanır; aşamayı yarıya indirmek eşiği yarıya indirir ama sorulan soruyu da değiştirir.

#### Sonraki Adım

Bu ders hesabın çevresindeki kalemi saydı, ama hesabın okuduğu veriyi hiç açmadı. Bir aşama
başladığında bölümler kendi paylarını bir yerden okur ve o yerin **düzeni** ne kadar değer
okunacağını belirler. Sonraki ders sütunlu dosya düzenini ele alır ve ölçüyü okunan değere bağlar:
satır kümesi başlıkları ile sütun sınırları kaç değeri atlatıyor, başlığın kendisi kaç değer
okutuyor, aynı sorgu üç düzende kaç değer okuyor ve satır kümesi boyutu bu sayıyı hangi yönde
oynatıyor.

### Sütunlu Depolama Biçimleri

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/sutunlu-depolama-bicimleri

Analitik sorgulara uygun dosya düzeninin okuttuğu değer sayısının ölçülmesi: kurgu kümenin 3.199 satırı ve 7 sütunu 22.393 değer eder; aynı sorgu satır sıralı düz düzende 22.393, sütun sınırlı düzende 6.398, satır kümeli ve başlıklı düzende 2.069 değer okur ve yedi satır kümesinin beşi hiç açılmaz. Satır kümesi boyutu bir yazma kararıdır ve iki yönde de bedeli vardır: 32 satırlık kümede başlık 300 değer okutur, 3.199 satırlık tek kümede hiçbir şey atlanmaz; en az değer okutan boyut 64 satırdır ve 1.174 değerle 19,07 kat verir. Başlıkların hangisinin okunduğu ikinci karardır ve en iyi boyutu 64'ten 128'e taşır. Sütun sınırının kazancı sorgunun istediği sütun sayısıyla erir: yedi sütun isteyen sorguda 1,00 kata iner, satır kümesi atlaması 4,92 kat verir.

Önceki ders hesabın çevresindeki eşgüdüm kalemini saydı ve bir aşamanın kaç kalem eklediğini
yazdı, ama aşama başladığında bölümlerin ne okuduğunu hiç açmadı. Bir bölüm kendi payını bir
dosyadan okur ve o dosyanın **düzeni**, aynı cevabı üretmek için kaç değere dokunulacağını
belirler.

Bu dersin ölçüsü **okunan değer sayısıdır** ve ölçülen şey bir motor değil bir dosyadır.
Ayrım önemlidir: M26/K04'ün başarım konusu aynı ölçü birimini kullanarak **okuma yolunu** ölçtü —
bir motorun süzgeci ne zaman uyguladığı, maddeleştirmeyi ne zaman yaptığı, kümenin hangi sütuna
göre sıralandığı. Burada okuma yolu sabittir ve yazma sırası da sabittir; oynayan tek şey
**dosyanın kendi düzenidir**: değerler sütun sınırlarıyla mı ayrılmış, satır kümelerine mi
bölünmüş, satır kümesi başlığı var mı ve o başlık ne kadar yer tutuyor.

- **OA19.** Küme K03–K05'ten devralınır ve **kurgudur**: 3.199 satır, 1.329 abone, üç dönem, beş
  bölge, tarife basamakları 10/25/40 m³. Yedi sütunla yazılır ve toplam 22.393 değer eder.
  **Modeldir.**
- **OA20.** Hiçbir dosya biçimi ürünü kullanılmaz; üç düzen bellekte modellenir ve okunan her değer
  sayılır. **Modeldir.**
- **OA21.** Yazma sırası sabittir: dosya kaynağın verdiği abone sırasında yazılır. Sıralama kararının
  atlamaya etkisi M26/K04'te ölçüldü ve burada **tekrarlanmaz**.
- **OA22.** Satır kümesi başlığı sütun başına üç değer taşır: en küçük, en büyük ve satır sayısı.
  Başlık dosyaya yazılır ve okunması da sayılır.
- **OA23.** Bir satır kümesi, süzgeç aralığı o kümenin en küçük ile en büyük değeri arasına hiç
  düşmüyorsa **açılmaz**.
- **OA24.** Sorgu sabittir: belirli bir abone aralığındaki toplam tüketim. İki sütun ister ve
  cevabı 8.869,88 m³'tür.

#### Üç Düzen, Tek Sorgu

```python
# duzen.py — ayni KURGU kume uc dosya duzeninde yazilir ve tek bir sorgu ucunde
# de kosulur. MODELDIR: hicbir dosya bicimi urunu kullanilmaz; duzenler bellekte
# modellenir ve olcu, cevabi uretmek icin okunan deger sayisidir.
import math

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [("kuzey", 0.28, 21), ("guney", 0.22, 17), ("dogu", 0.18, 26),
         ("bati", 0.14, 14), ("merkez", 0.18, 23)]
SUTUN = ("abone_no", "bolge", "donem", "hane", "memnuniyet", "tuketim_m3", "basamak")


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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


def basamak(v):
    return "0-10" if v <= 10 else "10-25" if v <= 25 else "25-40" if v <= 40 else "40 ustu"


KUME = []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() < 0.046:
        continue
    no, q = 10001 + i, uretec(TOHUM + 7000 + 10001 + i)
    for d in range(ayrik(q(), [0.05, 0.12, 0.21, 0.62])):
        v = 0.0 if q() < 0.038 else math.floor(
            b[2] * math.exp((q() + q() + q() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100
        KUME.append((no, b[0], d + 1, hane, memnun, v, basamak(v)))
        q()

N, S = len(KUME), len(SUTUN)
ALT, UST = 10601, 10800          # sorgu: bu abone araligindaki toplam tuketim
sec = [s for s in KUME if ALT <= s[0] <= UST]
print(f"tohum {TOHUM}; {N} satir, {S} sutun, {N * S} deger")
print(f"dosya kaynagin verdigi sirada yazildi: abone {min(s[0] for s in KUME)} - "
      f"{max(s[0] for s in KUME)}")
print(f"sorgu araligi {ALT}-{UST}: {len(sec)} satir eslesir, dogru cevap "
      f"{sum(s[5] for s in sec):.2f} m3")
```

```
tohum 20260218; 3199 satir, 7 sutun, 22393 deger
dosya kaynagin verdigi sirada yazildi: abone 10001 - 11399
sorgu araligi 10601-10800: 453 satir eslesir, dogru cevap 8869.88 m3
```

Kümenin sınırı 22.393 değer. Sorgu bu değerlerin 453 satırını ilgilendiriyor ve iki sütununu
istiyor; yani cevabın gerçekten ihtiyaç duyduğu değer 906 tane. Aradaki oran bu dersin bütün
tablolarının çerçevesidir: bir düzen ne kadar iyiyse okunan değer o 906'ya o kadar yaklaşır ve
hiçbir düzen onun altına inemez.

```python

# okuma.py — ayni sorgu uc duzende kosulur. Olcu: okunan deger sayisi.
# D1 satir sirali duz duzen, D2 sutun sinirli tek satir kumesi,
# D3 satir kumeli ve baslikli sutunlu duzen.
ISTENEN = ("abone_no", "tuketim_m3")
SUZGEC, BASLIK_DEGERI = "abone_no", 3      # en kucuk, en buyuk, satir sayisi


def kumeler(r):
    return [KUME[i:i + r] for i in range(0, N, r)]


def d1():
    return N * S, N, "satirin butun alanlari bitisik"


def d2():
    return N * len(ISTENEN), N, "yalniz istenen sutunlar"


def d3(r, hepsi=False):
    g = kumeler(r)
    baslik = BASLIK_DEGERI * len(g) * (S if hepsi else 1)
    acilan = [k for k in g if not (max(x[0] for x in k) < ALT
                                   or min(x[0] for x in k) > UST)]
    veri = sum(len(k) for k in acilan) * len(ISTENEN)
    return baslik + veri, sum(len(k) for k in acilan), len(g) - len(acilan)


print(f"sorgu {len(ISTENEN)} sutun istiyor: {', '.join(ISTENEN)}; "
      f"baslik satir kumesi basina {BASLIK_DEGERI} deger")
print(f"{'duzen':<40}{'okunan deger':>14}{'okunan satir':>14}{'oran':>8}")
t1, s1, _ = d1()
t2, s2, _ = d2()
t3, s3, atlanan = d3(512)
print(f"{'D1 satir sirali duz duzen':<40}{t1:>14}{s1:>14}{t1 / t1:>7.2f}x")
print(f"{'D2 sutun sinirli, tek satir kumesi':<40}{t2:>14}{s2:>14}{t1 / t2:>7.2f}x")
print(f"{'D3 satir kumeli (512) ve baslikli':<40}{t3:>14}{s3:>14}{t1 / t3:>7.2f}x")
print(f"D3'te {len(kumeler(512))} satir kumesinin {atlanan} tanesi hic acilmadi")
```

```
sorgu 2 sutun istiyor: abone_no, tuketim_m3; baslik satir kumesi basina 3 deger
duzen                                     okunan deger  okunan satir    oran
D1 satir sirali duz duzen                        22393          3199   1.00x
D2 sutun sinirli, tek satir kumesi                6398          3199   3.50x
D3 satir kumeli (512) ve baslikli                 2069          1024  10.82x
D3'te 7 satir kumesinin 5 tanesi hic acilmadi
```

Üç düzen aynı cevabı veriyor ve okunan değer 22.393'ten 2.069'a iniyor — **10,82 kat**. Kazanç iki
ayrı düzen özelliğinden gelir ve tablonun her satırı birini ekliyor.

Birinci özellik **sütun sınırıdır**. Satır sıralı düz düzende bir satırın yedi alanı yan yana
yazılmıştır; iki alan okumak için yedisini de geçmek gerekir. Sütun sınırı çekilmiş bir dosyada
her sütun kendi bitişik alanındadır ve yalnız iki tanesi okunur: 22.393 değer 6.398'e iner. Bu
kazanç bir sorgudan ya da bir motordan değil, dosyanın nasıl yazıldığından gelir.

İkinci özellik **satır kümesi ile başlıktır**. Dosya 512 satırlık yedi kümeye bölünmüş ve her
kümenin abone sütunu için en küçük, en büyük ve satır sayısı yazılmış. Sorgu aralığı bir kümenin
sınırlarına hiç değmiyorsa o küme açılmıyor; yedi kümenin beşi açılmadı ve okunan satır 3.199'dan
1.024'e indi. Başlık okumanın bedeli 21 değer, karşılığında kurtardığı 4.350 değer.

Üçüncü sütun atlamanın kalan payını gösteriyor. Sorgu 453 satır istiyor, açılan satır 1.024;
aradaki 571 satır okunuyor ve süzgeç onları teker teker eliyor. Sebep atlamanın **satır kümesi
tanesinde** olmasıdır: bir küme aralığa bir satırla bile değiyorsa tümüyle açılır. Atlamanın
inceliği satır kümesi boyutuyla belirlenir ve bu, bir sonraki bölümün konusudur.

#### Satır Kümesi Boyutu Bir Yazma Kararı

```python

# satir_kumesi.py — satir kumesi boyutu bir YAZMA karari. Kucuk satir kumesi
# ince atlama ve cok baslik, buyuk satir kumesi kaba atlama ve az baslik verir.
# Yazma sirasi sabittir: kaynagin verdigi abone sirasi.
print(f"{'satir kumesi':<14}{'kume':>6}{'baslik':>8}{'atlanan':>9}"
      f"{'acilan satir':>14}{'veri':>7}{'toplam':>8}{'oran':>8}{'tum baslik':>12}")
en_iyi = None
for r in (32, 64, 128, 256, 512, 1024, 3199):
    g = kumeler(r)
    toplam, acilan, atlanan = d3(r)
    tumu = d3(r, True)[0]
    baslik = BASLIK_DEGERI * len(g)
    print(f"{r:<14}{len(g):>6}{baslik:>8}{atlanan:>9}{acilan:>14}"
          f"{acilan * len(ISTENEN):>7}{toplam:>8}{t1 / toplam:>7.2f}x{tumu:>12}")
    if en_iyi is None or toplam < en_iyi[1]:
        en_iyi = (r, toplam)
tum = min(((r, d3(r, True)[0]) for r in (32, 64, 128, 256, 512, 1024, 3199)),
          key=lambda x: x[1])
print(f"yalniz suzgec sutununun basligi okunursa en iyi satir kumesi boyutu "
      f"{en_iyi[0]}: {en_iyi[1]} deger, {t1 / en_iyi[1]:.2f} kat")
print(f"butun sutunlarin basligi okunursa en iyi boyut {tum[0]}: {tum[1]} deger, "
      f"{t1 / tum[1]:.2f} kat; ayni dosya, iki okuma karari, iki ayri en iyi")

# Kazanc, sorgunun kac sutun istedigine baglidir.
print(f"{'istenen sutun':<15}{'D1':>8}{'D2':>8}{'D2 oran':>10}"
      f"{'D3 (128)':>10}{'D3 oran':>10}")
for k in (1, 2, 3, 5, 7):
    ISTENEN = SUTUN[:k]
    a, _, _ = d2()
    b = d3(128)[0]
    print(f"{k:<15}{t1:>8}{a:>8}{t1 / a:>9.2f}x{b:>10}{t1 / b:>9.2f}x")
ISTENEN = ("abone_no", "tuketim_m3")
```

```
satir kumesi    kume  baslik  atlanan  acilan satir   veri  toplam    oran  tum baslik
32               100     300       85           480    960    1260  17.77x        3060
64                50     150       42           512   1024    1174  19.07x        2074
128               25      75       20           640   1280    1355  16.53x        1805
256               13      39       10           768   1536    1575  14.22x        1809
512                7      21        5          1024   2048    2069  10.82x        2195
1024               4      12        3          1024   2048    2060  10.87x        2132
3199               1       3        0          3199   6398    6401   3.50x        6419
yalniz suzgec sutununun basligi okunursa en iyi satir kumesi boyutu 64: 1174 deger, 19.07 kat
butun sutunlarin basligi okunursa en iyi boyut 128: 1805 deger, 12.41 kat; ayni dosya, iki okuma karari, iki ayri en iyi
istenen sutun        D1      D2   D2 oran  D3 (128)   D3 oran
1                 22393    3199     7.00x       715    31.32x
2                 22393    6398     3.50x      1355    16.53x
3                 22393    9597     2.33x      1995    11.22x
5                 22393   15995     1.40x      3275     6.84x
7                 22393   22393     1.00x      4555     4.92x
```

İlk tablo dersin gizlenen kararıdır. Satır kümesi boyutu bir dosya yazılırken seçilir, sorgu
yazılırken değil ve iki yönde de bedeli vardır. Küçük kümede atlama incelir — 32 satırlık kümede
yüz kümenin 85'i atlanıyor ve yalnız 480 satır açılıyor — ama başlık 300 değer okutuyor, veri
payının neredeyse üçte biri. Büyük kümede başlık ucuzlar, 3.199 satırlık tek kümede yalnız üç
değer, ama atlanacak hiçbir şey kalmaz ve okuma 6.401 değere çıkar.

En az değer okutan boyut 64 satırdır: 1.174 değer ve 22.393'e göre **19,07 kat**. Tablonun tepesi
keskin değildir — 32 ile 128 arasındaki bütün boyutlar 16 katın üstünde kalır — ama uçlar pahalıdır
ve iki uç iki ayrı sebeple pahalıdır. Bu tabloda ölçülen şeyin bir motor davranışı olmadığını
tekrar yazmak gerekiyor: yazma sırası yedi satırda da aynıdır, sorgu aynıdır, okuma yolu aynıdır.
Değişen tek şey dosyaya kaç satırda bir başlık yazıldığıdır.

Tavanın nerede olduğunu da yazmak gerekiyor. Cevabın gerçekten ihtiyaç duyduğu değer 906'ydı; en
iyi düzen 1.174 değer okuyor, yani ulaşılabilir en iyiye 1,30 kat uzakta. Kalan fark iki kalemden
oluşuyor: 150 değerlik başlık ve süzgeci geçemeyen 59 satırın 118 değeri. Bu iki kalem hiçbir satır
kümesi boyutunda birlikte sıfırlanmaz, çünkü biri boyutla büyür diğeri boyutla küçülür. Bir dosya
düzeninin işi sorgunun ihtiyacını sıfır bedelle karşılamak değil, iki kalemin toplamını en küçük
tutan boyutu bulmaktır.

Son sütun ikinci kararı ekliyor. Bazı düzenlerde okuyucu yalnız süzgeç sütununun başlığını çeker,
bazılarında satır kümesinin bütün sütun başlıkları tek blok halinde durur ve birlikte okunur.
İkinci durumda başlık yedi kat pahalıdır ve en iyi boyut 64'ten 128'e taşınır: 1.805 değer, 12,41
kat. **Aynı dosya, iki savunulabilir okuma kararı, iki ayrı en iyi boyut.** Bir düzenin "en iyi
satır kümesi boyutu" diye tek bir sayısı yoktur; hangi başlığın okunduğu yazılmadan o sayı
üretilemez.

İkinci tablo kazancın sorguya bağlı olduğunu gösteriyor. Sütun sınırının kazancı istenen sütun
sayısıyla erir ve yedi sütunun hepsi istendiğinde 1,00 kata iner: bütün sütunları okuyan bir sorgu
için sütunlu düzen düz düzenden farksızdır. Satır kümesi atlaması ise erimez, çünkü satır atlamak
sütun seçmekten bağımsızdır; aynı satırda oran 4,92 katta kalır. **Sütun sınırı okunmayacak sütunu
atlatır, satır kümesi başlığı okunmayacak satırı; ikisinin kazancı çarpılır ve biri sıfırlandığında
diğeri sürer.**

Bu ayrım bir dosya düzeni seçilirken hangi sorunun sorulacağını belirler. Az sütun okuyup çok satır
tarayan sorgular sütun sınırından kazanır; belirli bir aralığı okuyan sorgular satır kümesi
başlığından. İkisini de yapmayan bir sorgu — bütün sütunları ve bütün satırları isteyen bir dışa
aktarım — hiçbir düzenden kazanmaz: sütun sınırı 1,00 kat verir, atlanacak satır kümesi bulunmaz
ve okuma başlıkların değeri kadar düz düzenin üstüne çıkar. Düzen kararı sorgudan önce verilir ve
hangi sorgunun ucuza cevaplanacağını o karar belirler.

#### Özet

- Kurgu kümenin 3.199 satırı ve 7 sütunu 22.393 değer eder; sorgunun gerçekten ihtiyaç duyduğu
  değer 453 satırın iki sütunu, yani 906'dır.
- Aynı sorgu satır sıralı düz düzende 22.393, sütun sınırlı düzende 6.398, satır kümeli ve başlıklı
  düzende 2.069 değer okur; yedi satır kümesinin beşi hiç açılmaz ve kazanç 10,82 kattır.
- Satır kümesi boyutunun iki yönlü bedeli vardır: 32 satırlık kümede başlık 300 değer okutur,
  3.199 satırlık tek kümede hiçbir şey atlanmaz; en az değer okutan boyut 64'tür ve 1.174 değerle
  19,07 kat verir.
- Hangi başlıkların okunduğu ikinci karardır: bütün sütun başlıkları okunduğunda en iyi boyut 128'e
  taşınır ve kazanç 12,41 kata iner.
- Sütun sınırının kazancı istenen sütun sayısıyla erir ve yedi sütunlu sorguda 1,00 kata iner;
  satır kümesi atlaması aynı sorguda 4,92 kat vermeyi sürdürür.

#### Sonraki Adım

Dört ders dağıtımın kalemlerini tek tek saydı: taşınan kayıt, eşgüdüm kalemi, okunan değer. Üçü de
azaltılabilir kalemlerdi ve her birinde bir karar bulundu. Geriye azaltılamayan bir pay kalıyor.
Bir işin bölünemeyen kısmı yüzde kaçsa, kaç birim eklenirse eklensin hızlanma bir tavanın üstüne
çıkamaz. Sonraki ders bu tavanı birim sayısını artırarak basar ve dağıtımın ne zaman gereksiz
olduğunu sayıyla yazar; aynı ders kursu ve M26 müfredatını kapatır.

### Paralel İşleme Sınırları

Source: https://academia.sh/tr/kurslar/gorsellestirme-ve-raporlama/paralel-isleme-sinirlari

Dağıtımın maliyeti ve ne zaman gereksiz olduğu, bölünemeyen pay ile hızlanma tavanı üzerinden sayılıyor: kurgu rapor zincirinin 6.753 dokunuşunun 310'u bölünemez, pay yüzde 4,59 ve tavan 21,78 kattır. Bölünemeyen pay yüzde 2 iken tavan 50, yüzde 30 iken 3,33 kattır ve 4.096 birim bile tavanı geçemez. Eklenen birim başına kazanç 0,91'den 0,00011'e iner; 256 birimden sonra her yeni birim binde ikiden az kazandırır. Tavanın iş boyuyla nasıl davrandığı bir varsayım kararıdır: bölünemeyen kalemler sabit sayılırsa tavan 206.388 kata çıkar, eşgüdüm kalemi ile satır kümesi başlığı iş boyuyla büyütülürse 43,64 katta doyar. Kurs kapanışı on sekiz dersin kaynaktaki ve geri okunan sayısını, müfredat kapanışı M26'nın altı kursunu ve 90 dersini tek tabloda toplar.

Dört ders dağıtımın kalemlerini tek tek saydı: karışım geçişinde taşınan kayıt, iş başına eklenen
eşgüdüm kalemi, dosya düzeninin okuttuğu değer. Üçü de azaltılabilir kalemlerdi ve her birinde
savunulabilir iki karar bulundu. Bu ders azaltılamayan payı ölçer.

Bir işin her adımı bölünmez. Kaynağın şeması bir kez okunur, iş planı bir kez çıkarılır, sonuç
tablosu bir kez kurulur ve yazılır; bu adımlar kaç birim eklenirse eklensin bir kez yapılır ve
süreleri kısalmaz. Dersin ölçüsü **bölünemeyen paydır** — işin bölünemeyen kısmının toplam işe
oranı — ve o paydan türeyen ikinci sayı **hızlanma tavanıdır**: birim sayısı sonsuza gitse bile
hızlanmanın çıkabileceği en yüksek kat.

- **OA25.** Rapor zinciri önceki dört dersin saydığı kalemlerden kurulur ve **kurgudur**: satır
  sayısı 3.199, başlık değeri 150 (dördüncü dersin en iyi satır kümesi boyutundan), eşgüdüm kalemi
  100 (üçüncü dersin dört aşama ve sekiz bölümlü zincirinden), karışım geçişinde taşınan kayıt 40
  (ikinci dersten). **Modeldir.**
- **OA26.** İş birimi bir dokunuştur; ham milisaniye yazılmaz ve ölçülen sayı **hızlanma katıdır**.
- **OA27.** Hızlanma, bölünemeyen pay $s$ ve birim sayısı $p$ ile
  $1 / (s + (1 - s) / p)$ olarak hesaplanır; $p$ sonsuza giderken tavan $1 / s$ olur.
- **OA28.** Bölünebilir adımların birimler arasında eşit dağıldığı varsayılır. Bölüm dengesizliği
  M26/K04'te ölçüldü ve burada hesaba katılmaz; bu varsayım tavanı **iyimser** verir.
- **OA29.** Bölünemeyen kalemlerin iş boyuyla büyüyüp büyümediği bir karardır ve iki varsayımla
  sınanır: sabit kalan seri ile üçüncü ve dördüncü dersin formüllerine göre büyüyen seri.
- **OA30.** Tavanın yüzde 90'ına varmak için gereken birim sayısı ikinin katları taranarak bulunur.

#### İşin Bölünemeyen Payı

```python
# tavan.py — isin bolunemeyen payi ve hizlanma tavani. MODELDIR: rapor zinciri
# adimlara ayrilir, her adima bir dokunus sayisi ve bolunup bolunmedigi yazilir;
# olcu hizlanma katidir, ham sure degil.
N = 3199
ADIM = (("kaynak semasinin okunmasi", 150, "hayir"),
        ("is planinin cikarilmasi ve gorev dagitimi", 100, "hayir"),
        ("satirlarin okunmasi ve suzulmesi", N, "evet"),
        ("yerel indirgeme", N, "evet"),
        ("karisim gecisinde tasinan kayit", 40, "evet"),
        ("son indirgeme (5 anahtar)", 5, "evet"),
        ("tek rapor tablosunun kurulmasi ve yazimi", 60, "hayir"))

print(f"{'adim':<42}{'dokunus':>9}{'bolunur':>9}")
for ad, d, b in ADIM:
    print(f"{ad:<42}{d:>9}{b:>9}")
TOPLAM = sum(d for _, d, _ in ADIM)
SERI = sum(d for _, d, b in ADIM if b == "hayir")
S = SERI / TOPLAM
print(f"toplam dokunus {TOPLAM}, bolunemeyen {SERI}, bolunemeyen pay "
      f"{100 * S:.2f} yuzde, hizlanma tavani {1 / S:.2f} kat")
```

```
adim                                        dokunus  bolunur
kaynak semasinin okunmasi                       150    hayir
is planinin cikarilmasi ve gorev dagitimi       100    hayir
satirlarin okunmasi ve suzulmesi               3199     evet
yerel indirgeme                                3199     evet
karisim gecisinde tasinan kayit                  40     evet
son indirgeme (5 anahtar)                         5     evet
tek rapor tablosunun kurulmasi ve yazimi         60    hayir
toplam dokunus 6753, bolunemeyen 310, bolunemeyen pay 4.59 yuzde, hizlanma tavani 21.78 kat
```

Bölünemeyen pay bir kod özelliği değil, bir **adım listesidir**. Tablodaki üç adım toplam 310
dokunuş tutuyor ve 6.753 dokunuşluk işin yüzde 4,59'unu ediyor. Üçünün de bölünmemesinin ayrı bir
sebebi var: şema okuma kaynağın tek bir yerinde durur, iş planı bütün bölümleri görmek zorundadır,
sonuç tablosu tek bir çıktı olmalıdır.

Yüzde 4,59 küçük bir sayı gibi durur. Karşılığı 21,78 kattır: bu işi kaç birime bölerseniz bölün,
hızlanma 21,78 katı geçemez. Yüzde 4,59'luk bir pay hızlanmanın yirmi ikide birini baştan kesiyor.

#### Hızlanma Tavanı

```python

# hizlanma.py — ayni is farkli bolunemeyen paylarla ve artan birim sayisiyla
# kosulur. hizlanma(p) = 1 / (s + (1 - s) / p); tavan p sonsuza giderken 1 / s.
BIRIM = (1, 2, 4, 8, 16, 64, 256, 1024, 4096)


def hizlanma(s, p):
    return 1.0 / (s + (1.0 - s) / p)


def yuzde90(s):
    p = 1
    while hizlanma(s, p) < 0.9 / s and p < 10 ** 7:
        p *= 2
    return p


PAY = (0.02, S, 0.10, 0.30)
print("birim".ljust(8) + "".join(f"s={s:.4f}".rjust(10) for s in PAY))
for p in BIRIM:
    print(f"{p:<8}" + "".join(f"{hizlanma(s, p):>10.2f}" for s in PAY))
print(f"{'tavan':<8}" + "".join(f"{1 / s:>10.2f}" for s in PAY))
print(f"{'p90':<8}" + "".join(f"{yuzde90(s):>10}" for s in PAY))
```

```
birim     s=0.0200  s=0.0459  s=0.1000  s=0.3000
1             1.00      1.00      1.00      1.00
2             1.96      1.91      1.82      1.54
4             3.77      3.52      3.08      2.11
8             7.02      6.05      4.71      2.58
16           12.31      9.48      6.40      2.91
64           28.32     16.44      8.77      3.22
256          41.97     20.15      9.66      3.30
1024         47.72     21.35      9.91      3.33
4096         49.41     21.67      9.98      3.33
tavan        50.00     21.78     10.00      3.33
p90            512       256       128        32
```

Tablonun sağ sütunu en öğreticisidir. Bölünemeyen pay yüzde 30 olduğunda 4.096 birim **3,33 kat**
veriyor: dört bin doksan altı birim, üç buçuk kat. Aynı işin iki birimle hızlanması 1,54 kat; yani
son 4.094 birim yalnız 1,79 kat ekliyor. Payın yüzde 30 olması işin yüzde 70'inin bölünebildiği
anlamına gelir ve bu, kulağa dağıtılabilir bir iş gibi gelir.

Sol sütun aynı şeyi tersinden gösteriyor. Bölünemeyen pay yüzde 2 iken tavan 50 kat ve 4.096 birim
49,41 kat veriyor. Yüzde 2 ile yüzde 30 arasındaki fark on beş kat değil, tavanda **on beş kat**
farktır: 50'ye karşı 3,33. Bölünemeyen pay doğrusal artar, tavan tersine orantılı düşer.

Son satır işin pratik tarafını veriyor. Tavanın yüzde 90'ına varmak için gereken birim sayısı, pay
küçüldükçe büyüyor: yüzde 30'da 32 birim yetiyor, yüzde 2'de 512 birim gerekiyor. Küçük bölünemeyen
pay yüksek tavan verir, ama o tavana varmak için daha çok birim ister.

#### Eklenen Birim ve Tavanın Kendisi

```python

# sinir.py — tavan yaklastikca eklenen birimin kazandirdigi kat erir; ve
# bolunemeyen pay is boyuyla degisir cunku bolunebilir kalemler buyur,
# bolunemeyen kalemler buyumez.
print(f"bolunemeyen pay {100 * S:.2f} yuzde; tavan {1 / S:.2f} kat")
print(f"{'birim':<8}{'hizlanma':>10}{'onceki katindan artis':>23}"
      f"{'eklenen birim basina':>22}{'tavana uzaklik':>16}")
onceki_p, onceki_h = 1, 1.0
for p in BIRIM[1:]:
    h = hizlanma(S, p)
    print(f"{p:<8}{h:>10.2f}{h - onceki_h:>23.3f}"
          f"{(h - onceki_h) / (p - onceki_p):>22.5f}{1 / S - h:>16.2f}")
    onceki_p, onceki_h = p, h

import math
SABIT = sum(d for _, d, b in ADIM if b == "hayir")


def buyuyen(n):
    """03'un esgudum kalemi ve 04'un baslik degeri is boyuyla buyur."""
    bolum = max(8, math.ceil(n / 400000))
    return 4 * bolum * 3 + 4 + 3 * math.ceil(n / 64) + 60


print(f"{'is boyu':<12}{'bolunebilir':>13}{'sabit seri':>11}{'sabit tavan':>13}"
      f"{'buyuyen seri':>14}{'buyuyen tavan':>15}{'p90':>9}")
for n in (3199, 31990, 319900, 3199000, 31990000):
    bol = 2 * n + 45
    s1, s2 = SABIT / (bol + SABIT), buyuyen(n) / (bol + buyuyen(n))
    print(f"{n:<12}{bol:>13}{SABIT:>11}{1 / s1:>13.1f}{buyuyen(n):>14}"
          f"{1 / s2:>15.2f}{yuzde90(s2):>9}")
EN_BUYUK = 31990000
doyum = (2 * EN_BUYUK + 45 + buyuyen(EN_BUYUK)) / buyuyen(EN_BUYUK)
print(f"sabit seri varsayimi tavani is boyuyla dogru orantili buyutur; buyuyen "
      f"seri varsayiminda tavan {doyum:.2f} katta doyar ve yuzde 90'ina 512 "
      f"birimde varilir")
```

```
bolunemeyen pay 4.59 yuzde; tavan 21.78 kat
birim     hizlanma  onceki katindan artis  eklenen birim basina  tavana uzaklik
2             1.91                  0.912               0.91222           19.87
4             3.52                  1.604               0.80180           18.27
8             6.05                  2.539               0.63466           15.73
16            9.48                  3.421               0.42762           12.31
64           16.44                  6.968               0.14517            5.34
256          20.15                  3.704               0.01929            1.64
1024         21.35                  1.202               0.00157            0.43
4096         21.67                  0.323               0.00011            0.11
is boyu       bolunebilir sabit seri  sabit tavan  buyuyen seri  buyuyen tavan      p90
3199                 6443        310         21.8           310          21.78      256
31990               64025        310        207.5          1660          39.57      512
319900             639845        310       2065.0         15157          43.21      512
3199000           6398045        310      20639.9        150115          43.62      512
31990000         63980045        310     206388.2       1500556          43.64      512
sabit seri varsayimi tavani is boyuyla dogru orantili buyutur; buyuyen seri varsayiminda tavan 43.64 katta doyar ve yuzde 90'ina 512 birimde varilir
```

İlk tablo eklenen birimin ne kazandırdığını veriyor. İkinci birim 0,91 kat ekliyor; 4.096'ncı birim
grubundaki her birim 0,00011 kat. **256 birimden sonra eklenen her birim binde ikiden az
kazandırıyor** ve tavana uzaklık 1,64 kata inmiş oluyor. Aradan sonrası ölçülebilir bir kazanç
değil, yalnızca tavana asimptotik yaklaşmadır.

İkinci tablo dersin gizlenen kararıdır. "Küme büyüdükçe dağıtım daha çok kazandırır" cümlesi
doğrudur, ama ne kadar kazandırdığı bir varsayıma bağlıdır. Bölünemeyen kalemler sabit sayılırsa
tavan iş boyuyla doğru orantılı büyür ve 31.990.000 satırda 206.388 kata çıkar. Oysa üçüncü ders
eşgüdüm kaleminin bölüm sayısıyla, dördüncü ders satır kümesi başlığının satır kümesi sayısıyla
büyüdüğünü ölçtü. Bu iki formül hesaba katıldığında tavan **43,64 katta doyuyor** ve iş boyunu
büyütmek onu bir daha oynatmıyor.

İki sayı arasındaki oran 4.729 kattır ve ikisi de aynı modelden, aynı kümeden, aynı zincirden
çıkıyor. Aradaki tek fark bölünemeyen kalemlerin iş boyuyla büyüyüp büyümediği varsayımıdır ve o
varsayım hiçbir hızlanma raporunda kendiliğinden yazılmaz.

Dağıtımın ne zaman gereksiz olduğu da buradan okunur. Kurgu kümenin 3.199 satırında tavan 21,78 kat
görünüyor, ama üçüncü ders bu iş boyunda bir eşgüdüm kalemine yalnız 61,5 satır düştüğünü ve kalem
başına 10.000 satır hedefinin 520.000 satırda karşılandığını saymıştı; birinci ders de kümenin
seçilen kararla 22.525.682 satırda tek makineye sığmaz hale geldiğini yazmıştı. Üç sayı aynı yöne
işaret ediyor: bu küme dağıtılmaz. Tavanın yüksek olması dağıtmayı gerektirmez; dağıtmayı
gerektiren şey işin kendisinin tek makineyi aşmasıdır.

#### Özet

- Bölünemeyen pay bir kod özelliği değil bir adım listesidir: şema okuma, iş planı ve tek rapor
  tablosunun yazımı 310 dokunuş tutar ve 6.753 dokunuşluk işin yüzde 4,59'unu eder.
- Yüzde 4,59'luk pay hızlanma tavanını 21,78 kata çiviler; pay yüzde 2 iken tavan 50, yüzde 30 iken
  3,33 kattır ve 4.096 birim bile tavanı geçemez.
- Tavana varmak da bedavaya gelmez: tavanın yüzde 90'ına yüzde 30'luk payda 32, yüzde 2'lik payda
  512 birimde varılır.
- Eklenen birimin kazancı erir: ikinci birim 0,91 kat ekler, 256 birimden sonra her birim binde
  ikiden az ekler ve tavana uzaklık 1,64 kata iner.
- Tavanın iş boyuyla davranışı bir varsayım kararıdır: bölünemeyen kalemler sabit sayılırsa tavan
  206.388 kata çıkar, üçüncü ve dördüncü dersin formülleriyle büyütülürse 43,64 katta doyar; iki
  sayı arasında 4.729 kat vardır.
- Yüksek tavan dağıtmayı gerektirmez: 3.199 satırlık kümede tavan 21,78 kat olduğu halde kalem
  eşiği 520.000, hacim eşiği 22.525.682 satırdır.

#### Kurs Kapanışı

On sekiz dersin her birinde iki sayı yan yana durdu: veride ne olduğu ve okuyana ne ulaştığı. Her
sayı kendi dersinden okundu.

| Ders | Kaynaktaki sayı | Geri okunan sayı | Fark |
|---|---|---|---|
| Grafik 01 grafik türü seçimi | doğu - kuzey 4,463 m³ | alan kanalından 2,500 m³ | -1,963 m³ |
| Grafik 02 dağılım görselleştirme | 2 tepe, uç değer 45,47 m³ | kutu özetinde 0 tepe | 2 tepe kayıp |
| Grafik 03 kategorik görselleştirme | ayrılması gereken 39 çift | yığılmışta 35 çift | 4 çift, hata yüzde 47,2 |
| Grafik 04 ilişki görselleştirme | 1.101 nokta | 309 dolu hücre | 792 nokta, yüzde 71,9 |
| Grafik 05 zaman serisi görselleştirme | eğilim -0,140 m³/dönem | -0,1347 m³/dönem | 0,0053, bedeli 12 dönem |
| Grafik 06 coğrafi görselleştirme | ortalama 19,713 m³ | alanla ağırlıklı 18,484 m³ | -1,228 m³ |
| Grafik 07 görsel yanıltma | kuzey 20,210 m³ | kırpılmış eksende 8,019 m³ | -12,191 m³ |
| Grafik 08 erişilebilir grafik | 8 seri | ton okunmayınca 4 seri | 4 seri, yedeklemeyle 0 |
| Rapor 01 gösterge tablosu tasarımı | 12 ölçüt | karara göre düzende 9 ölçüt | 3 ölçüt dışarıda |
| Rapor 02 anahtar başarım göstergeleri | kapsama 60,948 | altı yolla 93,766'ya kadar | 32,818 puan |
| Rapor 03 iş zekâsı araçları | nihai değer 63,055 | önceden toplanmışta 36,042 | 27,013 puan |
| Rapor 04 veriyle anlatı kurma | 16 önerme | bir adım eksikken 2-4 çıkarım | 11 yanlış varsayılan, 9'u lehte |
| Rapor 05 rapor yeniden üretilebilirliği | 59 sabitlenecek kalem | 11 kalemin 10'u oynatır | 36,042 ↔ 100,000 |
| Ölçek 01 büyük veri kavramı | satır başına 286,0 bayt | eşik 22.525.682 satır | 7.508.560 ↔ 240.273.951 |
| Ölçek 02 eşle-indirge modeli | 3.199 kayıt | karışımda 40 kayıt | 79,97 kat |
| Ölçek 03 dağıtık analiz motorları | 8 işlem | 4 aşama, 100 kalem | 61,5 satır/kalem |
| Ölçek 04 sütunlu depolama biçimleri | 22.393 değer | en iyi düzende 1.174 değer | 19,07 kat |
| Ölçek 05 paralel işleme sınırları | 6.753 dokunuş | tavan 21,78 kat | 43,64 ↔ 206.388 |

Üçüncü sütun kursun kuralıdır: **bir gösterim, okuyucunun geri okuyabildiği sayı kadar doğrudur;
geri okunamayan gösterim raporlanmamış sayılır.** İlk sekiz satırda geri okunan şey bir m³ değeri,
bir tepe sayısı ya da bir seri sayısıdır ve fark sütunu kanalın kaybettiğini verir. Ortadaki beş
satırda okuyucu bir karar vericidir ve geri okunan şey tek bir sayı değil karardır: kaç ölçüt
görülüyor, ölçüt ölçtüğünü oynatmadan nereye taşınabiliyor, aynı rapor ikinci kez üretilebiliyor mu.

Son beş satırda geri okunan sayı bir gösterimden değil **ölçekten** okunuyor: taşınan kayıt,
eşgüdüm kalemi, okunan değer, hızlanma katı. Kural değişmiyor. Bir hesabın bölünebilirliği de
ancak sayıldığı kadar bilinir ve sayılmayan kalem hesabın içinde değil, faturanın içinde çıkar.

M26'nın altı kursu aynı ekseni altı ayrı yerde okudu.

| Kurs | Ders | Ölçü ekseni | Birim |
|---|---|---|---|
| K01 Veri Analitiğine Giriş | 9 | sayı tanım anında oynar | tanım |
| K02 Elektronik Tablolarla Analiz | 11 | kararın yeri | kararın kurtarılabilirliği |
| K03 Veri Toplama ve Hazırlama | 17 | satır sayısı yolda değişir | giren ve çıkan satır |
| K04 Python ile Veri Analizi | 22 | soyutlamanın kazandırdığı ve gizlediği | kazanç ve gizlenen karar |
| K05 İstatistiksel Analiz ve Keşifsel Veri Analizi | 13 | sayının oynama payı | pay |
| K06 Görselleştirme, Raporlama ve Ölçekli Analiz | 18 | kanalın geri okuttuğu sayı | geri okuma hatası |
| Toplam | 90 | | |

Doksan ders tek bir kuralı altı kez kurdu: **bir sayının yanında hangi kararla üretildiği
yazılmıyorsa o sayı bir bulgu değil, bir seçimdir.** Kural her kursta başka bir yerde sınandı —
tanımda, elektronik tablonun hücresinde, hazırlama adımında, çağrının varsayılanında, oynama
payında ve kodlama kanalında — ve her seferinde aynı biçimde ödendi: aynı veriden, aynı soruya,
iki savunulabilir kararla iki farklı sayı çıktı ve ikisi de yan yana basıldı. Doğu ile kuzey
arasındaki 4,463 m³'lük fark bu doksan dersin hepsinde durdu ve hiçbir yerde kendiliğinden bir
şey söylemedi.

Doksan derste veriden karara giden yolun her adımı ölçüldü ve her adımda kararın sayıyı oynattığı
gösterildi. Ama bütün bu kararları **insan** verdi: hangi satır sayılacak, hangi eşik seçilecek,
hangi kanal kullanılacak, iş hangi anahtara göre bölünecek. Kararı veriden **öğrenen** bir yordamın
hangi sayıyı oynattığı ve o yordamın kendi kararlarının nerede durduğu hiç sorulmadı. Sonraki
müfredat, Makine Öğrenmesi ve Veri Bilimi, bu soruyla açılır.

