İçeriğe geç
academia.sh

Ders 09 / 22

Filtreleme ve Sorgulama

Endeksi 120.000'in üstündeki okumalar üç ayrı yazımla süzülüyor ve üç farklı satır sayısı çıkıyor: sayısal karşılaştırma 1.835 satır ve 36.435,83 m³, aynı koşulun değillemesi 2.026 satır ve 40.356,71 m³, sütun metin okunmuşken aynı karşılaştırma 1.915 satır ve 38.035,93 m³. Fark eksiklerdedir: değilleme 191 eksik satırın tamamını içeri alır, metin karşılaştırması 80 'yok' hücreyi alır çünkü harf rakamdan büyük sayılır. İki koşul birleşince fark büyür ve mantıkta denk yazılan iki ifade 446 ile 502 satır döner. Elle süzgeç zinciri 5.034 geçişte 16.184 baytlık ara tablo kurarken maske 6.398 geçiş yapar ve 3.399 bayt tutar.

İçindekiler

Dilimleme bir aralık verir, ama analizde asıl istenen aralık değil koşuldur: “endeksi 120.000’in üstündeki okumalar”. Koşullu seçim bir karşılaştırmadan doğar ve karşılaştırma sessiz bir varsayım taşır — kümedeki her değerin karşılaştırılabilir olduğu varsayımı. Bir önceki derste endeks sütununda 111 boş ve 80 “yok” yazılı hücre görülmüştü. Bir eşiğin altında mı üstünde mi olduğu bilinmeyen bir değer, süzgeçten hangi tarafa düşer.

Bu dersin cevabı tek bir cümle değildir, çünkü yazıma göre değişir. Aynı soru üç ayrı biçimde yazılabilir, üçü de doğru görünür, üçü de hata vermez ve üçü farklı satır sayısı döndürür. Ölçü budur: aynı koşul, üç yazım, üç sayı. Fark hep aynı yerden gelir, eksik değerlerden, ama her yazımda başka bir tarafa düşer.

  • VC14. Eksik bir değerle yapılan karşılaştırma yanlış döner. Bu, eksik değerin küçük olduğu anlamına gelmez; karşılaştırma bir cevap üretemediği için kendi başına bir cevap seçmiştir.
  • VC15. Karşılaştırma “bilinmiyor”u “yanlış”a eşlediği için bu eşleme değilleme altında korunmaz: x > e yanlış olduğu satırda x <= e de yanlıştır, dolayısıyla değil(x <= e) doğrudur. Aynı satır bir yazımda dışarıda, ötekinde içeridedir.
  • VC16. Süzgeç maskesi, satır başına bir doğruluk değeri taşıyan bir dizidir. Maske satırı kopyalamaz; koşullar maske üzerinde birleştirilir ve sonuç bir kez üretilir.
  • VC17. Sorgulama, süzgeç koşulunun bir ifade olarak yazılmasıdır. Yazımın biçimi sonucu değiştirmez; koşulun içindeki karşılaştırmaların eksik değerde ne yaptığı değiştirir.

Eksik Değer Karşılaştırılamaz

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

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

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

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

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

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

# endeks sutunu bir onceki dersteki gibi saha formundan gelir: bir kismi bos,
# bir kismina elle "yok" yazilmistir.
import sys

kumul: dict[int, float] = {}
kayit = []
for o in UZUN:
    no, t = o["abone_no"], o["tuketim_m3"]
    kumul[no] = kumul.get(no, 0.0) + t
    u = uretec(TOHUM + 9100 + no * 4 + DONEM.index(o["donem"]))()
    ham = "" if u < 0.033 else "yok" if u < 0.055 else str(
        100000 + (no - 10000) * 37 + int(kumul[no]))
    kayit.append({"abone_no": no, "donem": o["donem"], "tuketim_m3": t, "endeks": ham})

EKSIK = None

def sayiya(h: str):
    """Metni sayiya cevirir; cevrilemeyen her hucre EKSIK olur."""
    try:
        return int(h)
    except ValueError:
        return EKSIK

sayisal = [sayiya(k["endeks"]) for k in kayit]
metin = [k["endeks"] for k in kayit]
print(f"{len(kayit)} satir: {sum(1 for v in sayisal if v is not EKSIK)} sayisal endeks, "
      f"{sum(1 for k in kayit if k['endeks'] == '')} bos, "
      f"{sum(1 for k in kayit if k['endeks'] == 'yok')} 'yok'")
print(f"sayisal endeks araligi {min(v for v in sayisal if v is not EKSIK)}.."
      f"{max(v for v in sayisal if v is not EKSIK)}")
3199 satir: 3008 sayisal endeks, 111 bos, 80 'yok'
sayisal endeks araligi 100067..151812

Üç bin yüz doksan dokuz satırın 3.008’i sayısal bir endeks taşıyor, 191’i taşımıyor. Bu 191 satırın kaybı kayıt aşamasındandır: sayaç okunamamıştır ya da form eksik doldurulmuştur. İkisi de aynı sonuca çıkar — o satır için endeks bilinmiyordur.

“Bilinmiyor” bir değer değildir, bir bilgi durumudur, ve bir karşılaştırma işleci bunu ifade edemez. Karşılaştırma iki seçenek arasından birini vermek zorundadır ve seçtiği şey “yanlış” olur. Bu seçim savunulabilir bir varsayılandır, çünkü bir süzgecin bilmediği satırı seçmemesi makuldür. Sorun varsayılanın kendisinde değil, yazılmamış olmasındadır.

Ayrım burada bir kez daha çizilmelidir: eksik değer ile sıfır aynı şey değildir. Kümede sıfır tüketimli okumalar vardır ve bunlar ölçülmüş kayıtlardır; sayaç okunmuş, tüketim çıkmamıştır. Karşılaştırmaya girerler, bir tarafa düşerler ve hiçbir belirsizlik taşımazlar. Eksik endeksli 191 satır ise ölçülmemiştir. İki durumu aynı sütunda tutan bir küme, süzgeç yazılırken hangisinin karşılaştırıldığını da yazmak zorundadır.

Aynı Soru, Üç Yazım

# uc_yazim.py — ayni soru uc bicimde: endeksi 120000'in ustundeki okumalar.
ESIK = 120000

def maske_sayisal(v):
    """Eksik deger karsilastirmaya giremez, sonuc yanlis doner."""
    return v is not EKSIK and v > ESIK

def maske_degilleme(v):
    """Ayni kosulun degillemesi: eksik satirda ic karsilastirma da yanlistir."""
    return not (v is not EKSIK and v <= ESIK)

s1 = [maske_sayisal(v) for v in sayisal]
s2 = [maske_degilleme(v) for v in sayisal]
s3 = [h > str(ESIK) for h in metin]

EN = [42, 8, 12]
y = lambda h: "".join(str(v).rjust(EN[k]) if k else str(v).ljust(EN[0])
                      for k, v in enumerate(h))
T = lambda m: round(sum(k["tuketim_m3"] for k, b in zip(kayit, m) if b), 2)
print(y(["yazim", "satir", "toplam m3"]))
print(y([f"sayisal   endeks > {ESIK}", sum(s1), T(s1)]))
print(y([f"degilleme not(endeks <= {ESIK})", sum(s2), T(s2)]))
print(y([f"metin     endeks > '{ESIK}'", sum(s3), T(s3)]))
print(f"\nikinci yazim birinciden {sum(s2) - sum(s1)} satir fazla: eksiklerin tamami")
print(f"ucuncu yazim birinciden {sum(s3) - sum(s1)} satir fazla: 'yok' yazili hucreler, "
      f"cunku metin karsilastirmasi harfi rakamdan buyuk sayar")
yazim                                        satir   toplam m3
sayisal   endeks > 120000                     1835    36435.83
degilleme not(endeks <= 120000)               2026    40356.71
metin     endeks > '120000'                   1915    38035.93

ikinci yazim birinciden 191 satir fazla: eksiklerin tamami
ucuncu yazim birinciden 80 satir fazla: 'yok' yazili hucreler, cunku metin karsilastirmasi harfi rakamdan buyuk sayar

Üç satır aynı soruyu soruyor ve üç ayrı sayı veriyor: 1.835, 2.026 ve 1.915 satır. Rapora giren tüketim toplamı da üç ayrı değerdir ve en düşükle en yüksek arasında 3.920,88 m³ vardır.

İkinci yazım en sinsi olanıdır, çünkü değilleme okurken doğal görünür: “eşiğin altında olmayanlar”, “eşiği aşanlar” ile aynı şey sanılır. Değildir. Eksik endeksli bir satırda endeks <= 120000 karşılaştırması yanlış döner, çünkü karşılaştırılamayan değer için seçilen cevap budur; o yanlışın değillemesi ise doğrudur. Böylece 191 satır, hakkında hiçbir bilgi olmadığı halde “eşiği aşanlar” kümesine girer ve 3.920,88 m³ tüketim raporlanan toplama eklenir.

Üçüncü yazım bir önceki dersin doğrudan devamıdır. Endeks sütunu 80 “yok” yüzünden metin okunmuştu; metin okunmuş bir sütunda > işleci hâlâ çalışır, ama sözlük sırasına göre çalışır. Bütün sayısal endeksler altı haneli olduğu için metin karşılaştırması onlarda sayısal karşılaştırmayla aynı sonucu verir; ayrıldıkları tek yer 'yok' hücreleridir ve “y” harfi “1” rakamından büyüktür. Sonuç, tam olarak 80 satır fazladır ve bu satırların hiçbirinde bir endeks yoktur.

Birleşik Koşul ve Süzgecin Bedeli

# birlesik.py — iki kosul birlesince eksigin etkisi cogalir: mantikta denk
# yazilan iki ifade ayni satir sayisini vermez.
def ve(v, t):
    return (v is not EKSIK and v > ESIK) and t > 25.0

def degil_veya(v, t):
    return not ((v is not EKSIK and v <= ESIK) or t <= 25.0)

b1 = [ve(v, k["tuketim_m3"]) for v, k in zip(sayisal, kayit)]
b2 = [degil_veya(v, k["tuketim_m3"]) for v, k in zip(sayisal, kayit)]
print(f"(endeks > {ESIK}) ve (tuketim > 25)           : {sum(b1)} satir, {T(b1)} m3")
print(f"degil((endeks <= {ESIK}) veya (tuketim <= 25)) : {sum(b2)} satir, {T(b2)} m3")
print(f"iki ifade mantikta denktir, sonuc {sum(b2) - sum(b1)} satir farklidir")

def elle_zincir():
    """Her kosul icin kume bastan taranir ve ara tablo kopyalanir."""
    ara = [(k, v) for k, v in zip(kayit, sayisal) if v is not EKSIK and v > ESIK]
    son = [(k, v) for k, v in ara if k["tuketim_m3"] > 25.0]
    return ara, son, len(kayit) + len(ara)

ara, son, elle_gecis = elle_zincir()
maske = bytearray(b1)
print(f"\nelle zincir: {len(son)} satir, {elle_gecis} gecis, ara tablo "
      f"{sys.getsizeof([k for k, _ in ara])} bayt")
print(f"maske      : {sum(maske)} satir, {2 * len(kayit)} gecis, maske "
      f"{sys.getsizeof(maske)} bayt, 1 satir kod")
print(f"maske gecis sayisinda kazanmaz, ara tabloda kazanir: "
      f"{sys.getsizeof([k for k, _ in ara]) - sys.getsizeof(maske)} bayt")
(endeks > 120000) ve (tuketim > 25)           : 446 satir, 13906.21 m3
degil((endeks <= 120000) veya (tuketim <= 25)) : 502 satir, 15675.3 m3
iki ifade mantikta denktir, sonuc 56 satir farklidir

elle zincir: 446 satir, 5034 gecis, ara tablo 16184 bayt
maske      : 446 satir, 6398 gecis, maske 3399 bayt, 1 satir kod
maske gecis sayisinda kazanmaz, ara tabloda kazanir: 12785 bayt

İki koşul birleşince fark küçülmez, yön değiştirir. A ve B ile değil(değil A veya değil B) iki değerli mantıkta birbirinin aynısıdır ve bir ders kitabında yer değiştirilebilir. Burada değiştirilemiyor, çünkü atomik karşılaştırma iki değerli değildir: üç durumu, yani doğru, yanlış ve bilinmiyor durumlarını, iki değere sıkıştırır ve “bilinmiyor”u “yanlış”a eşler. Bu eşleme değilleme altında korunmaz. Sonuç 446 yerine 502 satır ve raporlanan toplam 13.906,21 yerine 15.675,30 m³ olur.

Süzgecin bedeli ikinci çıktıdadır ve burada soyutlamanın kazancı beklenen yerde değildir. Elle yazılan zincir her koşulu ayrı bir geçişte uygular ve arada bir tablo kopyalar: 5.034 geçiş, 16.184 baytlık ara tablo. Maske ise her koşulu bütün satırlarda değerlendirir, 6.398 geçiş yapar, yani daha fazla geçiş tutar; kazandığı yer bellektir. Maske 3.399 bayttır ve hiçbir satır kopyalanmaz; fark 12.785 bayttır ve küme büyüdükçe oransal olarak korunur.

Sorgulamanın koşulu bir ifade olarak yazması da bu tabloyu değiştirmez. Koşul dizgiyle yazılsın, maskeler işleçle birleştirilsin, sonuç aynı üç kalemden çıkar: hangi karşılaştırmalar var, eksik değerde ne dönüyorlar ve nasıl birleştiriliyorlar. Yazım biçimi bir okunabilirlik kararıdır; satır sayısını belirleyen şey karşılaştırmanın eksik değerdeki davranışıdır.

Süzgecin Üçüncü Kümesi

# uc_kume.py — savunulabilir yazim: suzgec iki degil uc kume uretir ve ucunun
# toplami kumenin tamamini verir.
ust = sum(1 for v in sayisal if v is not EKSIK and v > ESIK)
alt = sum(1 for v in sayisal if v is not EKSIK and v <= ESIK)
bilinmiyor = sum(1 for v in sayisal if v is EKSIK)
print(f"esigin ustunde {ust}, altinda {alt}, bilinmiyor {bilinmiyor}, "
      f"toplam {ust + alt + bilinmiyor}")
print(f"bilinmeyen pay: {round(100 * bilinmiyor / len(kayit), 2)} yuzde; "
      f"tamami ustune yazilirsa {ust} yerine {ust + bilinmiyor}, "
      f"yani {round(100 * bilinmiyor / ust, 1)} yuzde oynama")
esigin ustunde 1835, altinda 1173, bilinmiyor 191, toplam 3199
bilinmeyen pay: 5.97 yuzde; tamami ustune yazilirsa 1835 yerine 2026, yani 10.4 yuzde oynama

Bir süzgeç yazımı bu üç sayıyı ayrı ayrı raporladığında dersin bütün belirsizliği ortadan kalkar. 1.835 satır eşiğin üstündedir, 1.173 satır altındadır, 191 satır hakkında karar verilememiştir ve üçünün toplamı kümenin tamamıdır. Bu toplama denetimi, tek bir sayı raporlayan yazımların hiçbirinde yapılamaz: 1.835 ile 2.026 arasında hangi sayının doğru olduğu, kümeye bakarak değil ancak eksiklerin nereye yazılacağına karar vererek belirlenir.

Belirsizliğin büyüklüğü de burada okunuyor. Bilinmeyen satırlar kümenin yüzde 5,97’sidir, ama raporlanan sayı üzerindeki etkisi yüzde 10,4’tür, çünkü eksikler kümenin tamamına değil süzgeçten geçen kısma oranlanır. Küçük görünen bir eksiklik oranının rapordaki etkisi eşiğin nereye konduğuna bağlıdır ve eşik daraldıkça büyür. Bu yüzden eksiklik oranı tek başına yeterli bir denetim değildir; ölçülmesi gereken şey eksiklerin süzgeç sonucuna oranıdır.

Özet

  • Eksik bir değerle yapılan karşılaştırma yanlış döner; bu, eksik değerin küçük olduğu anlamına gelmez, karşılaştırmanın cevap üretemediği yerde bir cevap seçtiği anlamına gelir.
  • Aynı koşul üç yazımda üç satır sayısı verir: sayısal 1.835, değilleme 2.026, metin karşılaştırması 1.915; raporlanan toplam 36.435,83 ile 40.356,71 m³ arasında oynar.
  • Değilleme 191 eksik satırın tamamını içeri alır; metin karşılaştırması 80 “yok” hücreyi alır, çünkü sözlük sırasında harf rakamdan büyüktür.
  • Birleşik koşulda mantıkta denk yazılan iki ifade 446 ile 502 satır döner, çünkü karşılaştırma “bilinmiyor”u “yanlış”a eşler ve bu eşleme değilleme altında korunmaz.
  • Maskenin kazancı geçişte değil bellektedir: elle zincir 5.034 geçişte 16.184 baytlık ara tablo kurarken maske 6.398 geçiş yapar ve 3.399 bayt tutar.

Sonraki Adım

Süzgeç satırları seçer, ama rapor tek tek satır istemez; kırılıma göre özet ister. “Bölge başına ortalama tüketim” sorusu kümeyi önce böler, her parçaya bir hesap uygular ve sonuçları tek tabloda birleştirir. Bu üç adımlık iş tek bir çağrıya sığar ve o çağrı iki ayrı karar taşır: grup anahtarında eksik değer taşıyan satırlar hangi gruba girer ve toplulaştırma hesabı eksik değerleri paydaya katar mı. Sonraki ders bu iki kararı ayrı ayrı ölçer ve kaç satırın hiçbir grupta görünmeden kaybolduğunu sayar.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat