---
title: 'Veri Çerçevesi Dilbilgisi'
source: 'https://academia.sh/tr/kurslar/python-ile-analiz/veri-cercevesi-dilbilgisi'
course: 'Python ile Veri Analizi'
language: tr
updated: '2026-08-17T18:08:43+00:00'
license: 'CC BY-SA 4.0'
---

# 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.

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

```python
# 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.

```python
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.

```python
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.
