İçeriğe geç
academia.sh

Ders 20 / 22

Veri Çerçevesi Dilbilgisi

Aynı yedi adımlık dönüştürme üç yazımla ifade edilir ve üçü de aynı sonucu verir: ara değişkenli yazım 7 deyim ve 6 ara değişken tutar, iç içe çağrı ve fiil zinciri tek deyim ve 0 ara değişken. Ayrım okuma sırasında ortaya çıkar — okuma sırası yürütme sırasıyla ara değişkenli yazımda 7/7, fiil zincirinde 7/7, iç içe çağrıda 1/7 adımda örtüşür. Zincirin bedeli ara sonuçların adsız kalmasıdır: 6 ara sonucun 0'ının satır sayısı görünür ve iz alan bir fiil eklendiğinde çağrı sayısı 7'den 15'e çıkarak 3.199 → 3.080 → 3.080 → 750 → 750 → 5 → 5 → 3 dizisi okunur hale gelir. Aynı altı fiilden birinin yeri değiştiğinde rapor edilen bölge sayısı 5'ten 1'e, doğu ortalaması 7,301 m³'ten 0,593 m³'e iner.

İçindekiler

Bir önceki ders bildirimsel ifadeyi tek deyime indirdi ve bedelini saydı: dört anahtarlık bir sözlük dört kararı yazıyor, beş kararı varsayılana bırakıyordu. O sözlüğün ikinci bir eksiği daha vardı. Bir analiz yalnız bir tanım değildir, bir sıradır: önce şu satırlar atılır, sonra şu sütun türetilir, sonra gruplanır. Anahtar-değer sözlüğü bu sırayı hiç yazmaz.

Bu ders aradaki biçimi ölçer. Veri çerçevesi dilbilgisi (grammar of data), dönüştürmeyi tek tek fiillerden kurulu bir zincir olarak yazar: süz, ekle, grupla, özetle, sırala, ilk. Ölçü iki sayıdır: aynı dönüştürme kaç deyimde ve kaç ara değişkenle yazılıyor, ve zincir kısaldığında hangi ara sonuç görünmez oluyor.

  • AO5. Küme K03’ten devralınır ve kurgudur: 3.199 satır, 1.329 abone, 10/25/40 m³ tarife basamakları. Tohum koda yazılıdır.
  • AO6. Fiil zinciri standart kitaplıkla modellenir. Cerceve küçük bir modeldir: her fiil yeni bir çerçeve döndürür, hiçbir fiil kendi girdisini değiştirmez. Hiçbir araç taklit edilmez.
  • AO7. Ölçülen dönüştürme yedi fiilden oluşur: okunmuş satırları süz, 25 m³ üzerindeki aşımı ekle, aşımı pozitif olanları süz, bölgeye göre grupla, ortalama aşımı özetle, azalan sırala, ilk üçü al.
  • AO8. İki sayı yan yana durur. Kazanç ara değişken ve deyim sayısıdır. Gizlenen karar iki başlıktır: zincirde ara sonuçların satır sayısı görünmez, ve fiil sırası koda yazılı olsa da sırayı seçen kararın gerekçesi hiçbir yerde yazılı değildir.

Yedi Fiil, Üç Yazım

# dilbilgisi.py — MODELDIR. Once K03'un 3.199 satirlik kurgu kumesi ayni tohumla
# yeniden uretilir, sonra fiil temelli zincir standart kitaplikla modellenir.
import ast
import inspect
import math

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
DONEM, TARIFE = ["2026-01", "2026-02", "2026-03"], [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: int):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

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


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


_kalan, _elenen = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    kayit = {"abone_no": 10001 + i, "bolge": b[0], "taban": b[2],
             "hane_kisi": ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05]),
             "memnuniyet": ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17]) + 1}
    (_elenen if r() < 0.046 else _kalan).append(kayit)
_gecerli = {k["abone_no"] for k in _kalan}
OKUMA = []
for k in _kalan + _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"] not in _gecerli:
            r()
            continue
        OKUMA.append({"abone_no": k["abone_no"], "bolge": k["bolge"], "donem": DONEM[d],
                      "tuketim_m3": t, "memnuniyet": k["memnuniyet"],
                      "basamak": 1 if t < TARIFE[0] else 2 if t < TARIFE[1]
                      else 3 if t < TARIFE[2] else 4})
        r()


class Cerceve:
    """Veri cercevesi dilbilgisinin MODELI: her fiil yeni bir cerceve dondurur."""

    def __init__(self, satirlar, anahtar=None, iz=None):
        self.satirlar, self.anahtar = satirlar, anahtar
        self.iz = [] if iz is None else iz

    def suz(self, kosul):
        return Cerceve([s for s in self.satirlar if kosul(s)], self.anahtar, self.iz)

    def ekle(self, ad, f):
        return Cerceve([{**s, ad: f(s)} for s in self.satirlar], self.anahtar, self.iz)

    def grupla(self, ad):
        return Cerceve(self.satirlar, ad, self.iz)

    def ozetle(self, ad, f):
        kova = {}
        for s in self.satirlar:
            kova.setdefault(s[self.anahtar], []).append(s)
        return Cerceve([{self.anahtar: g, ad: f(v)} for g, v in kova.items()], None, self.iz)

    def sirala(self, ad, azalan=True):
        return Cerceve(sorted(self.satirlar, key=lambda s: s[ad], reverse=azalan),
                       self.anahtar, self.iz)

    def ilk(self, n):
        return Cerceve(self.satirlar[:n], self.anahtar, self.iz)

    def say(self, etiket):
        self.iz.append((etiket, len(self.satirlar)))
        return Cerceve(self.satirlar, self.anahtar, self.iz)


FIIL = ("suz", "ekle", "grupla", "ozetle", "sirala", "ilk")
# Ic ice yazim icin ayni fiillerin islev bicimi.
def suz(c, k): return c.suz(k)
def ekle(c, a, f): return c.ekle(a, f)
def grupla(c, a): return c.grupla(a)
def ozetle(c, a, f): return c.ozetle(a, f)
def sirala(c, a): return c.sirala(a)
def ilk(c, n): return c.ilk(n)
# Donusturmenin kosullari ve olcusu.
def okundu(s): return s["tuketim_m3"] > 0
def asim(s): return s["tuketim_m3"] - 25
def pozitif(s): return s["asim_m3"] > 0
def ortalama_asim(v): return sum(s["asim_m3"] for s in v) / len(v)


KAYNAK = Cerceve(OKUMA)


def ara_degiskenli(c):
    okunan = c.suz(okundu)
    asimli = okunan.ekle("asim_m3", asim)
    asan = asimli.suz(pozitif)
    gruplu = asan.grupla("bolge")
    ozet = gruplu.ozetle("ortalama_asim", ortalama_asim)
    dizili = ozet.sirala("ortalama_asim")
    return dizili.ilk(3)


def ic_ice(c):
    return ilk(sirala(ozetle(grupla(suz(ekle(
        suz(c, okundu), "asim_m3", asim), pozitif), "bolge"),
        "ortalama_asim", ortalama_asim), "ortalama_asim"), 3)


def zincirli(c):
    return (c
            .suz(okundu)
            .ekle("asim_m3", asim)
            .suz(pozitif)
            .grupla("bolge")
            .ozetle("ortalama_asim", ortalama_asim)
            .sirala("ortalama_asim")
            .ilk(3))


def agac(f):
    return ast.parse(inspect.getsource(f)).body[0]


def fiil_adi(n):
    if not isinstance(n, ast.Call):
        return None
    ad = n.func.attr if isinstance(n.func, ast.Attribute) else getattr(n.func, "id", None)
    return ad if ad in FIIL else None


# Her fiil cagrisi icin: hangi deyimde, kac fiil cagrisini iceriyor, kodda nerede.
def cagrilar(f):
    g, out = agac(f), []
    for i, deyim in enumerate(g.body):
        for n in ast.walk(deyim):
            if fiil_adi(n):
                ic = sum(1 for m in ast.walk(n) if m is not n and fiil_adi(m))
                out.append({"deyim": i, "ic": ic,
                            "yer": (n.func.end_lineno, n.func.end_col_offset)})
    return out


def ad_sayisi(f):
    return len({n.id for n in ast.walk(agac(f))
                if isinstance(n, ast.Name) and isinstance(n.ctx, ast.Store)})


# Okuma sirasi kodun konumundan, yurutme sirasi ic ice gecmislikten okunur.
def ortusme(f):
    c = cagrilar(f)
    oku = sorted(range(len(c)), key=lambda j: c[j]["yer"])
    yur = sorted(range(len(c)), key=lambda j: (c[j]["deyim"], c[j]["ic"]))
    return sum(1 for a, b in zip(oku, yur) if a == b), len(c)


def hiza(w):
    return lambda h: "".join(str(v).ljust(w[0]) if j == 0 else str(v).rjust(w[j])
                             for j, v in enumerate(h))


YAZIM = [("ara degiskenli", ara_degiskenli), ("ic ice cagri", ic_ice),
         ("fiil zinciri", zincirli)]
y = hiza([18, 8, 8, 15, 18, 16])
print(y(["yazim", "deyim", "fiil", "ara degisken", "okuma=yurutme", "adli ara sonuc"]))
for ad, f in YAZIM:
    n, t = ortusme(f)
    print(y([ad, len(agac(f).body), t, ad_sayisi(f), f"{n}/{t}", f"{ad_sayisi(f)}/{t - 1}"]))
sonuc = [[(s["bolge"], round(s["ortalama_asim"], 3)) for s in f(KAYNAK).satirlar]
         for _, f in YAZIM]
print(f"\nkurgu kume {len(OKUMA)} satir; uc yazim ayni sonucu veriyor: "
      f"{'evet' if all(s == sonuc[0] for s in sonuc) else 'hayir'}")
print("  " + ", ".join(f"{b} {v}" for b, v in sonuc[0]))
yazim                deyim    fiil   ara degisken     okuma=yurutme  adli ara sonuc
ara degiskenli           7       7              6               7/7             6/6
ic ice cagri             1       7              0               1/7             0/6
fiil zinciri             1       7              0               7/7             0/6

kurgu kume 3199 satir; uc yazim ayni sonucu veriyor: evet
  dogu 7.301, merkez 6.532, kuzey 4.986

Üç yazım aynı yedi fiili çağırır ve aynı üç satırı döndürür. Ayrıldıkları yer ilk üç sütunda görünür. Ara değişkenli yazım her adımı bir ada bağlar: yedi deyim, altı ara değişken. İç içe çağrı ile fiil zinciri ikisi de tek deyimdir ve hiçbir ad bağlamaz.

Üç yazımın da aynı sonucu vermesi modelin bir özelliğinden gelir: hiçbir fiil kendi girdisini değiştirmez, her biri yeni bir çerçeve döndürür. Bu, zincirin kurulabilmesinin ön koşuludur. Girdiyi yerinde değiştiren bir fiil zincire takıldığında, aynı çerçeveye bakan ikinci bir ifade sessizce başka bir sonuç okur; ara değişkenli yazımda bu en azından bir adın üzerine yazılması olarak görünür, zincirde hiç görünmez.

Dördüncü sütun bu ikisini birbirinden ayırır. Okuma sırası kodun konumundan okunur — hangi fiil adı sayfada daha önce yazılmıştır. Yürütme sırası iç içe geçmişlikten çıkar — en içteki çağrı önce koşar. Ara değişkenli yazımda ikisi yedi adımda da örtüşür, çünkü her deyim bir öncekinin sonucunu alır. Fiil zincirinde de yedi adımda örtüşür: zincir soldan sağa ve yukarıdan aşağıya yürüdüğü için okunan sıra koşan sıradır. İç içe çağrıda örtüşme yedide bire iner. Sayfada ilk okunan ilk en son koşar, en sonda okunan suz en önce koşar; yalnız ortadaki grupla iki sırada da dördüncüdür. Zincirin kazancı budur: aynı tek deyimlik yazım, tersine okunmayı ortadan kaldırır.

İz Bırakan Zincir

Son sütun bedeli veriyor. Ara değişkenli yazımda altı ara sonucun altısı bir ada bağlıdır; herhangi birinin satır sayısı istendiği anda okunabilir. Zincirde bu sayı sıfırdır. Zincir bir bütün olarak koşar ve arasında hiçbir şey durmaz; süzgecin 3.000 satır mı yoksa 3 satır mı bıraktığı, sonuca bakılmadan bilinemez. Bu, dilbilgisinin gizlediği karardır: hangi adımda kaç satırın düştüğü. Modelde say fiili bunu geri kazandırır.

izli = (Cerceve(OKUMA, None, []).say("giris")
        .suz(okundu).say("suz okunmus")
        .ekle("asim_m3", asim).say("ekle asim_m3")
        .suz(pozitif).say("suz asim>0")
        .grupla("bolge").say("grupla bolge")
        .ozetle("ortalama_asim", ortalama_asim).say("ozetle ortalama")
        .sirala("ortalama_asim").say("sirala azalan")
        .ilk(3).say("ilk 3"))
y2 = hiza([20, 10, 10])
print(y2(["asama", "satir", "degisim"]))
onceki = None
for etiket, n in izli.iz:
    print(y2([etiket, n, "-" if onceki is None else n - onceki]))
    onceki = n
print(f"\niz {len(izli.iz)} say cagrisiyla alindi: fiil 7 -> cagri {7 + len(izli.iz)}, "
      f"gorunur ara sonuc 0/6 -> 6/6")
asama                    satir   degisim
giris                     3199         -
suz okunmus               3080      -119
ekle asim_m3              3080         0
suz asim>0                 750     -2330
grupla bolge               750         0
ozetle ortalama              5      -745
sirala azalan                5         0
ilk 3                        3        -2

iz 8 say cagrisiyla alindi: fiil 7 -> cagri 15, gorunur ara sonuc 0/6 -> 6/6

Dizi zincirin ne yaptığını satır satır anlatıyor. İlk süzgeç 119 satır düşürür — okunmamış kayıtların sayısı, önceki dersle aynı. ekle hiçbir satıra dokunmaz, yalnız sütun ekler. İkinci süzgeç 2.330 satır düşürür ve asıl daralma buradadır: 3.080 okumanın yalnız 750’si 25 m³’ün üzerindedir. Özetleme 745 satırı eritip beş bölgeye indirir, son fiil ikisini keser.

Bedel de görünüyor: 7 fiil 15 çağrıya çıkar. Ara sonuç görünürlüğü 0/6’dan 6/6’ya gider ve karşılığı sekiz fazladan çağrıdır. Zincirin okunma kolaylığı ile ara sonucun görünürlüğü aynı anda elde edilmez; ikisinden biri sekiz çağrı ile satın alınır. Sayı olarak söylenirse: görünürlüğün fiil başına bedeli bir çağrıdan biraz fazladır ve iz alınmayan bir zincirde 2.330 satırlık düşüş hiçbir uyarı üretmeden geçer. İzin bir yan kazancı daha var: grupla ve sirala aşamalarındaki sıfır değişimler, o iki fiilin satır sayısına hiç dokunmadığını sayıyla gösterir. Bir zincirin hangi fiillerinin satır ürettiğini ya da düşürdüğünü bilmek, hata arandığında bakılacak yeri yediden ikiye indirir.

Fiilin Sırası Bir Karardır

Zincirin fiilleri koda yazılıdır ve sırası görünür. Görünmeyen şey, o sıranın neden o sıra olduğudur. Aşağıda aynı altı fiil iki farklı sırayla yazılır: süzgeç bir kez özetlemeden önce, bir kez özetlemeden sonra.

def once_suz(c):
    return (c.suz(okundu).ekle("asim_m3", asim).suz(pozitif)
            .grupla("bolge").ozetle("ortalama_asim", ortalama_asim)
            .sirala("ortalama_asim"))


def sonra_suz(c):
    return (c.suz(okundu).ekle("asim_m3", asim)
            .grupla("bolge").ozetle("ortalama_asim", ortalama_asim)
            .suz(lambda s: s["ortalama_asim"] > 0).sirala("ortalama_asim"))


SIRA = [("suz ozetlemeden once", once_suz), ("suz ozetlemeden sonra", sonra_suz)]
y3 = hiza([26, 8, 14, 12, 12])
print(y3(["fiil sirasi", "fiil", "cikan satir", "dogu m3", "toplam m3"]))
okunan = []
for ad, f in SIRA:
    sonuc_c = f(Cerceve(OKUMA, None, []))
    d = {s["bolge"]: s["ortalama_asim"] for s in sonuc_c.satirlar}
    okunan.append((len(sonuc_c.satirlar), d.get("dogu", 0.0)))
    print(y3([ad, len(cagrilar(f)), len(sonuc_c.satirlar), f"{d.get('dogu', 0):.3f}",
              f"{sum(d.values()):.3f}"]))
print(f"\nayni {len(cagrilar(once_suz))} fiil, tek bir fiilin yeri degisti: "
      f"rapor edilen bolge sayisi {okunan[0][0]} -> {okunan[1][0]}, "
      f"dogu ortalamasi {okunan[0][1]:.3f} -> {okunan[1][1]:.3f} m3")
fiil sirasi                   fiil   cikan satir     dogu m3   toplam m3
suz ozetlemeden once             6             5       7.301      24.539
suz ozetlemeden sonra            6             1       0.593       0.593

ayni 6 fiil, tek bir fiilin yeri degisti: rapor edilen bolge sayisi 5 -> 1, dogu ortalamasi 7.301 -> 0.593 m3

Altı fiil aynı, biri yer değiştirdi ve rapor bambaşka bir şey söylüyor. İlk sırada süzgeç satırlara uygulanır: aşımı olan 750 okumanın bölge ortalamaları hesaplanır ve doğu için 7,301 m³ çıkar. İkinci sırada süzgeç özete uygulanır: bütün okumaların bölge ortalaması alınır — negatif aşımlar da sayılır — ve sonra ortalaması pozitif kalan bölgeler tutulur. Beş bölgeden yalnız doğu ayakta kalır ve sayısı 0,593 m³’e iner.

İki sayı da yanlış değildir; farklı iki soruya cevaptır. İlki “aşım yapan okumaların ortalama aşımı ne kadar” sorusunun cevabı, ikincisi “hangi bölgenin ortalama abonesi basamağı aşıyor” sorusunun. Dilbilgisinin verdiği şey her ikisini de iki satır kodla yazabilmektir; vermediği şey hangisinin sorulduğunun kayda geçmesidir. Zincir sırayı yazar, sıranın gerekçesini yazmaz; kursun kuralı burada da geçerlidir — sırası yazılmayan bir fiil değil, gerekçesi yazılmayan bir sıra ölçülmemiş sayılır.

Özet

  • Aynı yedi fiil üç yazımda aynı sonucu verir: doğu 7,301 — merkez 6,532 — kuzey 4,986 m³ ortalama aşım.
  • Ara değişkenli yazım 7 deyim ve 6 ara değişken, iç içe çağrı ve fiil zinciri 1 deyim ve 0 ara değişken tutar.
  • Okuma sırası yürütme sırasıyla ara değişkenli yazımda 7/7, zincirde 7/7, iç içe çağrıda 1/7 örtüşür; zincirin kazancı tersine okunmayı ortadan kaldırmasıdır.
  • Zincirde 6 ara sonucun 0’ının satır sayısı görünür; iz alan fiil eklendiğinde çağrı 7’den 15’e çıkar ve 3.199 → 3.080 → 750 → 5 → 3 dizisi okunur olur. İkinci süzgeç tek başına 2.330 satır düşürür.
  • Aynı altı fiilden birinin yeri değişince rapor edilen bölge sayısı 5’ten 1’e, doğu ortalaması 7,301 m³’ten 0,593 m³’e iner.

Sonraki Adım

Dönüştürme fiillerden kuruldu ve her fiil bir öncekinin çıktısını aldı. Aynı düşünce görselleştirmede de karşımıza çıkar: bir grafik tek bir çağrı değil, üst üste bindirilmiş katmanlardan oluşur — veri, eşleme, geometri, istatistik, ölçek. Sonraki ders bunu sayar: aynı grafik hazır bir çağrıyla kurulduğunda kaç bağımsız değişken alıyor, katmanlarla kurulduğunda kaç katman tutuyor, ve üzerine bir seri daha eklendiğinde kaç katman yeniden kullanılıyor. Yanında gizlenen karar durur: bir katmanın sessizce uyguladığı istatistik.

İ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