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

# Seri ve Veri Çerçevesi

Ocak ve şubat okumaları abone başına toplanıyor ve aynı iki seri üç hizalama kipinde üç farklı sonuç veriyor: konum tabanlı toplama 1.101 satır üretir ve bu satırların yalnız 4 tanesinde iki etiket aynı aboneyi gösterir, ama toplam 44.271,89 m³ ile doğru cevabın 119,13 m³ yakınındadır. Etiketle iç hizalama da 1.101 satır verir ve hepsi doğrudur; etiketle dış hizalama 1.260 satır verir ve 159 satırı eksiktir. Etiket dizini kurmayan elle hizalama 781.710 etiket karşılaştırması yaparken seri toplaması 2.361 geçişle aynı sonuca varır. Eksik tarafın sıfır sayılması toplamı 44.391,02 m³'ten 47.707,44 m³'e taşır.

Bir önceki konu sayıları hızlandırdı. Aynı toplama işi düz listeyle yapıldığında her öğe için ayrı
bir geçiş gerekiyordu, dizi arayüzüyle tek bir işleme indi ve ayrılan bayt tip seçimiyle birlikte
düştü. Ama o dizilerin ortak bir eksiği vardı: hiçbirinin **etiketi** yoktu. Üçüncü sütunun tüketim,
beşinci satırın 10.412 numaralı abone olduğunu koddan başka hiçbir şey söylemiyordu. İki diziyi
toplarken de tek dayanak konumdu — birinci öğe birinci öğeyle, ikinci ikinciyle toplanıyordu.

Bu konu diziye etiketi ekler ve ilk ders etiketin ne üstlendiğini ölçer. Ölçü şudur: iki seri
toplandığında sonuç **kaç satır** olur. Konum tabanlı bir toplamada bu soru sorulmaz, çünkü cevap
baştan bellidir. Etiketli bir toplamada ise cevap eldeki iki dizinin ne kadar örtüştüğüne bağlıdır
ve toplama işlemi, hiç yazılmamış bir hizalama kararını kendi başına verir.

- **VC1.** Küme, M26/K03'ün bıraktığı halidir ve **kurgudur**: 3.199 satırlık uzun biçimli okuma
  tablosu, 1.329 abone, üç dönem. Küme `python` ile ve yalnız standart kitaplıkla **modellenir**;
  hiçbir kütüphane çağrılmaz, hiçbir arayüz taklit edilmez. Ölçülen şey aracın kendisi değil,
  soyutlamanın davranışıdır.
- **VC2. Seri**, bir değer listesi ile ona eşit uzunlukta bir **etiket dizini** taşır. Bu dizin,
  Veritabanları kursunda tanıtılan veritabanı dizininden farklı bir şeydir: sorguyu hızlandırmak
  için değil, satırı sırasıyla değil **adıyla** adreslemek için vardır.
- **VC3. Veri çerçevesi**, aynı etiket dizinini paylaşan serilerin sıralı bir sözlüğüdür. Sütunlar
  aynı dizini paylaşmıyorsa çerçeve kurulamaz; hizalama çerçeveden **önce** gelir.
- **VC4. Hizalama**, iki seri üzerinde işlem yapılırken değerlerin konuma göre değil **etikete
  göre** eşleştirilmesidir. Sonucun satır sayısı bu eşleşmeden çıkar.
- **VC5.** Hizalamanın **kipi** bir karardır: iç kip yalnız iki dizinde de bulunan etiketi tutar,
  dış kip etiketlerin birleşimini üretir ve tek taraflı kalan etiketi eksik bırakır. Kip yazılmazsa
  soyutlama birini seçer ve satır sayısını o seçim belirler.

## Etiket Dizini Ne Üstlenir

```python
# seri.py — MODELDIR: etiketli seri standart kitaplikla kendi modelinde
# gerceklestirilir. Kume kurgudur ve M26/K03'un birakttigi uzun bicimli okuma
# tablosuyla ayni tohumdan yeniden uretilir.
import math

M32 = 0xFFFFFFFF

def karistir(t: int) -> int:
    t = ((t ^ (t >> 16)) * 2246822507) & M32
    t = ((t ^ (t >> 13)) * 3266489909) & M32
    return t ^ (t >> 16)

def uretec(tohum: int):
    s = karistir(tohum)
    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

TOHUM = 20260218
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)]

abone, elenen = [], []
for i in range(1400):
    r = uretec(TOHUM + i)
    ad, _, taban = BOLGE[ayrik(r(), [b[1] for b in BOLGE])]
    k = {"abone_no": 10001 + i, "bolge": ad, "taban": taban,
         "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 abone).append(k)

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()]
print(f"kume (tohum {TOHUM}): abone {len(abone)} satir, uzun okuma tablosu "
      f"{len(UZUN)} satir, {len({o['abone_no'] for o in UZUN})} abone")

EKSIK = None

class Seri:
    """Bir deger listesi ve ona esit uzunlukta bir etiket dizini. Etiket dizini
    bir veritabani dizini degildir: satiri sirasiyla degil adiyla bulur."""

    def __init__(self, etiket, deger, ad="deger"):
        self.etiket, self.deger, self.ad = list(etiket), list(deger), ad
        self.yer = {e: i for i, e in enumerate(self.etiket)}

    def __len__(self):
        return len(self.deger)

    def al(self, e):
        return self.deger[self.yer[e]]

    def toplam(self):
        return round(sum(v for v in self.deger if v is not EKSIK), 2)

    def eksik_sayisi(self):
        return sum(1 for v in self.deger if v is EKSIK)

def donem_serisi(donem: str, ad: str) -> Seri:
    s = [o for o in UZUN if o["donem"] == donem]
    return Seri([o["abone_no"] for o in s], [o["tuketim_m3"] for o in s], ad)

ocak, subat = donem_serisi("2026-01", "ocak"), donem_serisi("2026-02", "subat")
print(f"ocak serisi {len(ocak)} etiket, {ocak.toplam()} m3")
print(f"subat serisi {len(subat)} etiket, {subat.toplam()} m3")
print(f"iki serinin etiket dizini ayni mi: "
      f"{'evet' if ocak.etiket == subat.etiket else 'hayir'}")
```

```
kume (tohum 20260218): abone 1329 satir, uzun okuma tablosu 3199 satir, 1260 abone
ocak serisi 1260 etiket, 26038.51 m3
subat serisi 1101 etiket, 21668.93 m3
iki serinin etiket dizini ayni mi: hayir
```

Son satır dersin bütün sorununu kuruyor. Ocak serisinde 1.260 abone var, şubat serisinde 1.101. İki
seri aynı kümeden, aynı sütundan, aynı abone numaralarıyla çıktı; yine de dizinleri aynı değil.
Aradaki 159 abone şubatta hiç okunmamıştır. Bu, verinin kendi eksikliğidir ve M26/K03'ün ölçtüğü
sessiz kaybın devamıdır; burada yeni olan şey, iki serinin **toplanabilir** görünmesine rağmen
toplanabilir olmamasıdır.

Etiket dizini işte bunu üstlenir. Etiketsiz iki dizi toplandığında birinci öğe birinci öğeyle
toplanır ve bu, kimin kiminle toplandığı sorusunu tamamen yok eder. Etiket dizini soruyu geri
getirir: iki değer ancak **aynı etikete** aitse toplanır. Bunun bedeli, toplamanın artık her zaman
tanımlı olmamasıdır — bir etiketin karşılığı diğer tarafta yoksa o satır için bir karar gerekir.

## Aynı Toplama, Üç Hizalama

```python
# hizalama.py — ayni iki seri uc kiple toplanir ve uc sonucun satir sayisi,
# eksik sayisi ve toplami yan yana basilir.
GECIS = {"sayi": 0}

def hizala(a: Seri, b: Seri, kip: str) -> Seri:
    """Iki seriyi toplar. 'konum' etiketi hic okumaz; 'ic' yalniz iki dizinde de
    bulunan etiketi tutar; 'dis' etiketlerin birlesimini uretir ve tek tarafli
    etiketi EKSIK ile doldurur."""
    if kip == "konum":
        n = min(len(a), len(b))
        GECIS["sayi"] += 2 * n
        return Seri(a.etiket[:n], [a.deger[i] + b.deger[i] for i in range(n)])
    GECIS["sayi"] += len(a) + len(b)
    if kip == "ic":
        ortak = [e for e in a.etiket if e in b.yer]
        return Seri(ortak, [a.al(e) + b.al(e) for e in ortak])
    etiket = a.etiket + [e for e in b.etiket if e not in a.yer]
    return Seri(etiket, [a.al(e) + b.al(e) if e in a.yer and e in b.yer
                         else EKSIK for e in etiket])

def yeniden_dizinle(s: Seri, etiket) -> Seri:
    """Seriyi verilen etiket dizinine tasir; karsiligi olmayan etiket EKSIK olur."""
    return Seri(etiket, [s.al(e) if e in s.yer else EKSIK for e in etiket], s.ad)

class VeriCerceve:
    """Ayni etiket dizinini paylasan serilerin sirali sozlugu. Paylasmiyorlarsa
    cerceve kurulmaz: hizalama cerceveden once gelir."""

    def __init__(self, sutun: dict[str, Seri]):
        ilk = next(iter(sutun.values()))
        for s in sutun.values():
            if s.etiket != ilk.etiket:
                raise ValueError(f"'{s.ad}' sutununun etiket dizini farkli")
        self.sutun, self.etiket = sutun, ilk.etiket

    def bicim(self) -> tuple[int, int]:
        return len(self.etiket), len(self.sutun)

EN = [24, 8, 8, 12]
y = lambda h: "".join(str(v).rjust(EN[j]) if j else str(v).ljust(EN[0])
                      for j, v in enumerate(h))
print(y(["hizalama kipi", "satir", "eksik", "toplam m3"]))
sonuc = {}
for kip, ad in [("konum", "konum tabanli"), ("ic", "etiketle ic"), ("dis", "etiketle dis")]:
    sonuc[kip] = hizala(ocak, subat, kip)
    print(y([ad, len(sonuc[kip]), sonuc[kip].eksik_sayisi(), sonuc[kip].toplam()]))

tutan = sum(1 for i in range(min(len(ocak), len(subat)))
            if ocak.etiket[i] == subat.etiket[i])
print(f"\nkonum tabanli toplamanin {len(sonuc['konum'])} satirindan {tutan} tanesinde "
      f"iki etiket ayni aboneyi gosteriyor; iki toplam arasindaki fark "
      f"{round(sonuc['ic'].toplam() - sonuc['konum'].toplam(), 2)} m3")

try:
    VeriCerceve({"ocak": ocak, "subat": subat})
except ValueError as hata:
    print(f"cerceve kurulamadi: {hata}")
birlesim = sonuc["dis"].etiket
cerceve = VeriCerceve({"ocak": yeniden_dizinle(ocak, birlesim),
                       "subat": yeniden_dizinle(subat, birlesim),
                       "toplam": sonuc["dis"]})
print(f"hizalandiktan sonra cerceve bicimi {cerceve.bicim()[0]} satir x "
      f"{cerceve.bicim()[1]} sutun, subat sutununda "
      f"{cerceve.sutun['subat'].eksik_sayisi()} eksik")
```

```
hizalama kipi              satir   eksik   toplam m3
konum tabanli               1101       0    44271.89
etiketle ic                 1101       0    44391.02
etiketle dis                1260     159    44391.02

konum tabanli toplamanin 1101 satirindan 4 tanesinde iki etiket ayni aboneyi gosteriyor; iki toplam arasindaki fark 119.13 m3
cerceve kurulamadi: 'subat' sutununun etiket dizini farkli
hizalandiktan sonra cerceve bicimi 1260 satir x 3 sutun, subat sutununda 159 eksik
```

Üç satır aynı iki seriyi topluyor ve üç farklı tablo çıkarıyor. Dikkat çeken şey ilk iki satırın
**aynı satır sayısını** vermesidir: 1.101. Konum tabanlı toplama listeleri baştan eşleştirdiği için
kısa olanın uzunluğunda durur; iç hizalama ise şubat dizininin ocak dizininin bir alt kümesi
olması nedeniyle yine 1.101 satır çıkarır. Satır sayısına bakan bir denetim bu iki sonucu
birbirinden ayıramaz.

Ayrım içeriktedir. Konum tabanlı toplamanın 1.101 satırından yalnız **4 tanesinde** iki etiket
aynı aboneyi gösteriyor; kalan 1.097 satır bir abonenin ocak okumasını başka bir abonenin şubat
okumasıyla toplamıştır. Şubatta okunmamış ilk abone, ondan sonraki bütün eşleşmeleri bir kaydırır
ve kayma dizin boyunca birikir. Bunun bir hata üretmemesi tesadüf değildir: iki liste de sayı
taşır, toplama tanımlıdır, sonuç bir sayıdır.

Asıl tehlikeli sayı sonuncudur. Yanlış eşleştirilmiş toplam 44.271,89 m³, doğru eşleştirilmiş
toplam 44.391,02 m³; aradaki fark **119,13 m³**, yani binde üçün altında. Toplam, tek tek satırları
karıştıran bir işlemin hemen hemen aynı sonucu vermesine izin verir, çünkü toplama sırayı umursamaz
ve iki dizinin ortak olmayan kısmı küçüktür. Bir rapor yalnız toplamı taşıyorsa hata görünmez;
abone bazında bakan ilk soru ise 1.097 satırda yanlış cevap verir.

Üçüncü satır kararın kendisini gösteriyor. Dış hizalama 1.260 satır üretir ve 159 satırı eksiktir.
Toplamı iç hizalamayla aynıdır, çünkü eksik satırlar toplamaya katılmamıştır. Yani dış hizalama
kaybı **silmez, görünür kılar**: sayılabilir 159 satır olarak çıktıya yazar.

Çerçevenin kurulamaması da aynı kuralın başka bir yüzüdür. İki seri farklı etiket dizini taşıdığı
sürece yan yana bir tabloya konamaz; önce ortak bir dizine taşınmaları gerekir. Birleşim dizinine
taşındıklarında çerçeve 1.260 satır ve üç sütun olur, şubat sütununda 159 eksik hücre görünür.

## Kazanç ve Gizlenen Karar

```python
# olcum.py — hizalamanin kazanci gecis sayisiyla, gizledigi karar eksik tarafin
# nasil sayildigiyla olculur.
def elle_hizala(a: Seri, b: Seri):
    """Etiket dizini kurulmadan hizalama: her sol etiket icin sag etiket
    listesi bastan taranir. Sonuc ayni, yol farkli."""
    etiket, deger, gecis = [], [], 0
    for i, e in enumerate(a.etiket):
        for j, f in enumerate(b.etiket):
            gecis += 1
            if e == f:
                etiket.append(e)
                deger.append(a.deger[i] + b.deger[j])
                break
    return Seri(etiket, deger), gecis

GECIS["sayi"] = 0
elle, elle_gecis = elle_hizala(ocak, subat)
hizali = hizala(ocak, subat, "ic")
print(f"elle hizalama: {len(elle)} satir, {elle_gecis} etiket karsilastirmasi, 11 satir kod")
print(f"seri toplama : {len(hizali)} satir, {GECIS['sayi']} etiket gecisi, 1 satir kod")
print(f"iki sonuc ayni mi: {'evet' if elle.deger == hizali.deger else 'hayir'}")

def sifirli_dis(a: Seri, b: Seri) -> Seri:
    """Ayni dis hizalama, tek fark: eksik taraf sifir sayilir."""
    etiket = a.etiket + [e for e in b.etiket if e not in a.yer]
    return Seri(etiket, [(a.al(e) if e in a.yer else 0.0)
                         + (b.al(e) if e in b.yer else 0.0) for e in etiket])

dis, sifirli = hizala(ocak, subat, "dis"), sifirli_dis(ocak, subat)
print(f"\ndis hizalama {len(dis)} satir uretti, {dis.eksik_sayisi()} tanesi eksik")
print(f"eksik atlanarak toplanirsa {dis.toplam()} m3, eksik taraf sifir "
      f"sayilirsa {sifirli.toplam()} m3; fark {round(sifirli.toplam() - dis.toplam(), 2)} m3")
```

```
elle hizalama: 1101 satir, 781710 etiket karsilastirmasi, 11 satir kod
seri toplama : 1101 satir, 2361 etiket gecisi, 1 satir kod
iki sonuc ayni mi: evet

dis hizalama 1260 satir uretti, 159 tanesi eksik
eksik atlanarak toplanirsa 44391.02 m3, eksik taraf sifir sayilirsa 47707.44 m3; fark 3316.42 m3
```

Kazanç iki sayıda okunuyor. Elle hizalama 11 satır kod tutar ve 781.710 etiket karşılaştırması
yapar, çünkü her sol etiket için sağ liste baştan taranır. Seri toplaması aynı sonuca **2.361
geçişle** varır: bir kez sol dizin, bir kez sağ dizin okunur, arama sözlük üzerinden yapılır.
Karşılaştırma sayısı 331 kat düşer ve kod tek satıra iner. Ölçü bir süre değil, bir sayıdır ve
koşumdan koşuma değişmez.

Gizlenen karar ikinci çıktıdadır. Dış hizalamanın 159 eksik satırı toplama katılmadığında sonuç
44.391,02 m³, eksik taraf sıfır sayıldığında 47.707,44 m³ olur; fark **3.316,42 m³**, yani toplamın
yüzde yedisi. İki sonuç da savunulabilir. Şubatta hiç okunmamış bir abonenin iki aylık toplamına
"ocak okuması kadar" demek, o abonenin şubatta hiç su kullanmadığını varsaymaktır. "Bilinmiyor"
demek ise abonenin toplamını hiç raporlamamaktır.

Karar burada, hiç yazılmadan verilmiştir. Ne `hizala(ocak, subat, "dis")` çağrısında ne de
toplam alan satırda "eksikleri atla" diye bir şey yazar; atlama, toplama işlevinin varsayılan
davranışıdır. Bu kursun kuralı buradan çıkıyor: bir soyutlama işi kısalttığı kadar **kararı da
gizler**, ve varsayılanını bilmediğin bir işlev senin adına karar veren bir işlevdir.

## Özet

- Seri bir değer listesi ve ona eşit uzunlukta bir etiket dizini taşır; veri çerçevesi aynı etiket
  dizinini paylaşan serilerin sıralı sözlüğüdür ve dizinler ayrıysa çerçeve kurulamaz.
- Aynı iki seri üç kiple toplanınca üç sonuç çıkar: konum tabanlı 1.101 satır, iç hizalama 1.101
  satır, dış hizalama 1.260 satır ve 159 eksik.
- Konum tabanlı toplamanın 1.101 satırından yalnız 4 tanesi doğru aboneyi eşleştirir, ama toplamı
  doğru cevaptan 119,13 m³ sapar; toplama sırayı umursamadığı için hata özetin içinde kaybolur.
- Etiket dizini kazancı geçiş sayısında görünür: elle tarama 781.710 karşılaştırma yaparken seri
  toplaması 2.361 geçişle aynı sonuca varır.
- Hizalamanın gizlediği karar eksik tarafın ne sayıldığıdır: atlanınca 44.391,02 m³, sıfır
  sayılınca 47.707,44 m³, aradaki fark 3.316,42 m³.

## Sonraki Adım

Bu derste küme kod içinde üretildi ve her sütunun tipi yazıldığı yerde belliydi. Gerçek bir küme
ise bir dosyadan gelir ve dosyada tip yoktur: her hücre metindir. Okuma işlemi o metni bir tipe
dönüştürmek zorundadır ve bunu kimse söylemeden, sütuna bakarak yapar. Abone numarası sayıya
dönüşür, sıfırla başlayan sayaç numarası sıfırlarını kaybeder, ilk yüz satırda tam sayı görünen bir
sütun dokuz yüzüncü satırda ondalık taşır. Sonraki ders okuma ve yazmayı ele alır ve tek bir sayı
sorar: bir çağrıyla okunan kümenin kaç sütunu yanlış tiple gelmiştir, gidip dönen bir dosyada kaç
hücre değişmiştir.
