İç içe kayıtlar ve küçük veri işleme

Bir ürünün ad, fiyat, stok bilgilerini sözlükte tutabildiğimizi gördük. Birden fazla ürün veya öğrenci kaydı olduğunda bu sözlükleri bir liste içinde saklayabiliriz.

Bu hafta ana hedef yeni bir Python yapısı öğrenmek değil; bildiğimiz liste, sözlük, koşul, döngü ve fonksiyonları birlikte kullanarak küçük veri kümelerini işlemek olacak. Filtreleme, özetleme, maksimum arama ve basit gruplama kalıplarına odaklanacağız.

Bu bölümün kapsamı

NoteSınavda sorulur
  • liste[indeks]["alan"] erişiminin iki adımı
  • filtreleme kalıbı ve append()in kaydı kopyalamaması
  • kayıtlar üzerinde sayma ve biriktirme ile özet çıkarma
  • maksimum arama kalıbında ilişkili kaydı tutma
  • sözlük anahtarlarıyla gruplama
  • metin alanlarında normalleştirmenin gruplamayı belirlemesi
  • eksik alan varsayılanının problem kuralına göre seçilmesi
  • veri çerçevesi kullanan kütüphaneler
  • ikiden çok seviyeli iç içe yapıların derin kopyalanması

Bu başlıklar konunun devamıdır; ileride karşınıza çıkar ama bu derste ezberlemeniz beklenmiyor.

Filtrelediğim kaydı değiştirince asıl liste neden bozuldu?

Kayıtları yeni bir listeye taşımak onları kopyalamaz. Sözlükle listeyi birlikte kullanmanın iki yüzü var: erişim kalıpları ve paylaşılan nesnenin yan etkisi.

Liste içinde sözlükler

Bir öğrenci kaydı:

ogrenci = {
    "ad": "Deniz",
    "bolum": "Bilgisayar",
    "puan": 78
}

Birden fazla öğrenci:

ogrenciler = [
    {"ad": "Deniz", "bolum": "Bilgisayar", "puan": 78},
    {"ad": "Ayşe", "bolum": "Siber", "puan": 92},
    {"ad": "Mert", "bolum": "Bilgisayar", "puan": 55}
]

Burada dış yapı bir liste, her eleman bir sözlük, sözlük içindeki alanlar ise kaydın özellikleridir.

İlk öğrencinin puanı:

print(ogrenciler[0]["puan"])

Önce listenin 0. elemanına, sonra o sözlüğün "puan" alanına erişilir.

Liste içindeki sözlüğe ve sözlük alanına aşamalı erişim.

İç içe kayıtlara erişim

Kayıtlar üzerinde gezinmek

for ogrenci in ogrenciler:
    print(ogrenci["ad"], ogrenci["puan"])

Her yinelemede ogrenci değişkeni bir sözlüğü gösterir.

kayıt listesi
    ↓
her kaydı sırayla al
    ↓
alanlarını oku
    ↓
koşula göre işle

Tahmin et — hangi kayıtlar yazılır?

Bu kod stoku tükenen ürünleri filtreleyerek yazdırır.

Filtreleme: koşulu sağlayan kayıtları seçmek

ogrenciler = [
    {"ad": "Deniz", "puan": 78},
    {"ad": "Ayşe", "puan": 92},
    {"ad": "Mert", "puan": 55}
]

gecenler = []

for ogrenci in ogrenciler:
    if ogrenci["puan"] >= 60:
        gecenler.append(ogrenci)

print(gecenler)

Yeni liste yalnızca koşulu sağlayan kayıtları içerir.

Veri işleme sürecinde filtreleme, dönüştürme ve özetleme adımlarının ayrımı.

Filtrele, dönüştür ve özetle

Önemli: filtrelenen sözlükler kopyalanmış değildir

  1. haftadaki aliasing davranışı burada yeniden karşımıza çıkar. gecenler.append(ogrenci) yeni bir sözlük üretmez; mevcut sözlük nesnesine bir referans ekler.
ogrenciler = [
    {"ad": "Deniz", "puan": 78},
    {"ad": "Ayşe", "puan": 92}
]

gecenler = []
for ogrenci in ogrenciler:
    if ogrenci["puan"] >= 60:
        gecenler.append(ogrenci)

gecenler[0]["puan"] = 0

print(ogrenciler[0]["puan"])  # 0

gecenler[0] ile ogrenciler[0] aynı sözlük nesnesini gösterdiği için değişiklik iki listeden de görünür.

Orijinal kaydı koruyup bağımsız bir sığ kayıt kopyası istiyorsanız:

gecenler.append(ogrenci.copy())

kullanabilirsiniz.

Warning

.copy() burada yalnızca sözlüğün dış katmanını kopyalar. Sözlüğün içinde liste gibi değiştirilebilir başka nesneler varsa onlar yine paylaşılabilir. Bu derste tek seviyeli kayıtlar için sığ kopya sezgisi yeterlidir.

Alıştırma — düşük stok kayıtlarını seçin

Beklenen listede Defter ve Silgi kayıtları bulunmalıdır.

if urun["stok"] <= 5: koşulu içinde dusuk_stok.append(urun) kullanabilirsiniz.

urunler = [
    {"ad": "Kalem", "stok": 12},
    {"ad": "Defter", "stok": 3},
    {"ad": "Silgi", "stok": 5},
    {"ad": "Cetvel", "stok": 8}
]

dusuk_stok = []

for urun in urunler:
    if urun["stok"] <= 5:
        dusuk_stok.append(urun)

print(dusuk_stok)

Özetleme: toplam ve ortalama

Kayıtların belirli alanını biriktirebiliriz:

satislar = [
    {"urun": "Kalem", "tutar": 120},
    {"urun": "Defter", "tutar": 250},
    {"urun": "Silgi", "tutar": 80}
]

toplam = 0
for satis in satislar:
    toplam = toplam + satis["tutar"]

print(toplam)  # 450

Ortalama için toplam / len(satislar) kullanılabilir; fakat boş listeyi ayrıca düşünmek gerekir.

“Veri yok” ile 0 aynı şey değildir

Önceki sürümde boş öğrenci listesinde ortalama 0 döndürmek mümkün görünüyordu. Fakat bunun bir anlam bedeli vardır:

  • [] → hiç puan yok,
  • [0] → bir öğrenci var ve puanı gerçekten 0.

İki durumu aynı sonuçla göstermek bilgiyi kaybettirir. Bu nedenle burada sözleşmeyi açıkça değiştiriyoruz:

def puan_ortalamasi(ogrenciler):
    if len(ogrenciler) == 0:
        return None

    toplam = 0
    for ogrenci in ogrenciler:
        toplam = toplam + ogrenci["puan"]

    return toplam / len(ogrenciler)

Sözleşme:

Çıktı:
- liste boşsa None: hesaplanabilecek ortalama yok
- değilse puanların sayısal ortalaması

Kullanırken bu durumu kontrol ederiz:

ortalama = puan_ortalamasi([])

if ortalama is None:
    print("Ortalama hesaplanamadı: veri yok")
else:
    print("Ortalama:", ortalama)
Note

None ile is None kontrolünü 6. haftada tanıdık; 13. haftada hata sinyali tasarımının avantaj ve bedellerini daha ayrıntılı ele alacağız. Buradaki ana sözleşme kararı şudur: “veri yok” bir sayı değildir.

Maksimum arama: yalnızca değeri değil, kaydı bulmak

  1. haftada sayılar üzerinde “şimdiye kadarki en yüksek” kalıbını gördük. Kayıtlarda aynı fikir, en yüksek değerle ilişkili sözlüğü bulmak için kullanılır:
def en_yuksek_puanli(ogrenciler):
    if len(ogrenciler) == 0:
        return None

    en_yuksek = ogrenciler[0]

    for ogrenci in ogrenciler[1:]:
        if ogrenci["puan"] > en_yuksek["puan"]:
            en_yuksek = ogrenci

    return en_yuksek

Örnek:

ogrenciler = [
    {"ad": "Deniz", "puan": 78},
    {"ad": "Ayşe", "puan": 92},
    {"ad": "Mert", "puan": 55}
]

print(en_yuksek_puanli(ogrenciler))

Sonuç Ayşe kaydıdır. Bu kalıp 14. haftadaki “en fazla ciro yapan ürün” gibi problemlerin doğrudan temelidir.

Important

max() sayısal listede en büyük değeri kolayca bulur. Fakat veri sözlüklerden oluştuğunda başlangıçta döngü kalıbını açıkça görmek, hangi alanın karşılaştırıldığını ve hangi kaydın tutulduğunu anlamayı kolaylaştırır.

Alıştırma — toplam stok fonksiyonu

Beklenen sonuç 20dir.

Döngü içinde toplam = toplam + urun["stok"] yazabilirsiniz.

def toplam_stok(urunler):
    toplam = 0

    for urun in urunler:
        toplam = toplam + urun["stok"]

    return toplam

Basit gruplama

Verileri bir alana göre gruplamak için sözlük kullanabiliriz:

ogrenciler = [
    {"ad": "Deniz", "bolum": "Bilgisayar"},
    {"ad": "Ayşe", "bolum": "Siber"},
    {"ad": "Mert", "bolum": "Bilgisayar"},
    {"ad": "Ece", "bolum": "Siber"},
    {"ad": "Can", "bolum": "Bilgisayar"}
]

bolum_sayilari = {}

for ogrenci in ogrenciler:
    bolum = ogrenci["bolum"]
    bolum_sayilari[bolum] = bolum_sayilari.get(bolum, 0) + 1

print(bolum_sayilari)

Beklenen:

{'Bilgisayar': 3, 'Siber': 2}

Bu, geçen haftaki frekans sayımının kayıt verisine uygulanmış hâlidir.

Gruplama anahtarı gerçekten aynı mı?

Metin alanlarını anahtar olarak kullanırken "Siber", "siber" ve " SİBER " sözlük açısından farklı string’lerdir. Aynı gruba ait olduklarını düşünüyorsanız önce açık bir normalleştirme kuralı gerekir.

bolum = ogrenci["bolum"].strip().lower()

ASCII ağırlıklı veride bu yeterli olabilir; fakat 8. haftada gördüğümüz gibi Türkçe I / İ dönüşümleri lower() ile yerel ayara göre yapılmaz. "İdari", "IDARI" gibi gerçek Türkçe verilerde hangi eşdeğerlik kuralının kullanılacağını problem alanı belirlemelidir.

Warning

Gruplamada “biraz temizleyelim” diye körlemesine lower() uygulamak veri kalitesi kararının yerine geçmez. Özellikle Türkçe ad, bölüm, ürün ve şehir değerlerinde 8. haftadaki normalleştirme uyarısını hatırlayın.

Gruplara toplam değer eklemek

satislar = [
    {"kategori": "Kırtasiye", "tutar": 120},
    {"kategori": "İçecek", "tutar": 50},
    {"kategori": "Kırtasiye", "tutar": 80}
]

kategori_toplamlari = {}

for satis in satislar:
    kategori = satis["kategori"]
    tutar = satis["tutar"]
    kategori_toplamlari[kategori] = kategori_toplamlari.get(kategori, 0) + tutar

print(kategori_toplamlari)

Sonuç:

{'Kırtasiye': 200, 'İçecek': 50}

get() ile eksik alan düşüncesi

Bir veri kaydında alanın bulunmaması iki farklı anlam taşıyabilir:

urun = {"ad": "Kalem"}

"stok" alanı yoksa urun.get("stok", 0) teknik olarak hata vermeyi engeller. Fakat bu ancak eksik stok bilgisini sıfır kabul etmek problem açısından doğruysa uygundur.

Başka bir problemde eksik alan veri kalitesi hatası olabilir ve sessizce 0 vermek yanlış sonucu gizleyebilir.

Important

get() teknik olarak hatayı engelleyebilir; fakat doğru varsayılan değeri problem kuralı belirler. “Program hata vermesin” tek başına doğru veri işleme değildir.

İç içe yapıyı izlemek

İç içe veri yapılarında bir ifadeyi soldan sağa çözmek faydalıdır:

ogrenciler[1]["puan"]

Adımlar:

  1. ogrenciler[1] → ikinci öğrenci sözlüğü
  2. ["puan"] → o sözlükteki puan değeri

Bu yaklaşım karmaşık ifadeleri “tek seferde anlamaya” çalışmaktan daha güvenlidir.

Değiştir — rapora yeni alan ekleyin

Bu örnek orijinal listedeki sözlükleri yerinde değiştirir. Eğer önce gecenler.append(ogrenci) ile bu kayıtları başka bir listeye taşımış olsaydınız, yeni gecti alanı o listeden de görünürdü. Orijinal veri korunmalıysa ogrenci.copy() ile yeni kayıt üretmek veya dönüşümü tamamen yeni sözlüklerle yapmak gerekir.

Üret — bölüm bazında puan özeti

Beklenen:

{'Bilgisayar': 140, 'Siber': 160}

Döngü içinde bolum, puan değerlerini alın; sonra toplamlar[bolum] = toplamlar.get(bolum, 0) + puan kullanın.

def bolum_puan_ozeti(ogrenciler):
    toplamlar = {}

    for ogrenci in ogrenciler:
        bolum = ogrenci["bolum"]
        puan = ogrenci["puan"]
        toplamlar[bolum] = toplamlar.get(bolum, 0) + puan

    return toplamlar

Veri işleme akışını parçalamak

Küçük veri problemlerini şu adımlarla düşünmek yararlıdır:

veriyi tanı
    ↓
hangi kayıtlar gerekli?
    ↓
hangi alanlar kullanılacak?
    ↓
filtrele / say / biriktir / maksimumu bul / grupla
    ↓
sonucu açık bir yapıda döndür

Bir fonksiyon her şeyi aynı anda yapmak zorunda değildir. gecen_ogrenciler, puan_ortalamasi ve en_yuksek_puanli ayrı sorumluluklara sahip olabilir.

Sınav provası

Önce kendi cevabını seç; sonra cevap anahtarında her şıkkın neden doğru veya yanlış olduğunu oku. Çeldiriciler uydurma değil, bu konuda gerçekten yapılan hatalardır. Sınav maddeleri de bu mantıkla yazılır.

Madde 1. ogrenciler bir sözlük listesiyken ogrenciler[2]["puan"] ifadesi ne yapar?

  • A. puan adlı listenin 2 numaralı elemanını verir
  • B. Önce listenin 2 numaralı kaydını seçer, sonra o kaydın puan alanını okur
  • C. Üçüncü kaydı siler
  • D. Listenin 2 numaralı elemanını puan değeriyle değiştirir
  • E. Bütün kayıtların puan alanlarını toplar

Madde 2. gecenler.append(ogrenci) satırı neden yeni bir sözlük kopyası oluşturmaz?

  • A. append() yalnızca sayıları kopyalar
  • B. Kopya oluşur ama bellekte aynı yerde tutulur
  • C. append() kaydın kendisini değil, o kayda giden başvuruyu ekler; iki liste aynı sözlüğü paylaşır
  • D. Kopya oluşur; iki liste bağımsızdır
  • E. Sözlükler kopyalanamaz

Madde 3. 50 ve üzeri puan alan öğrencileri yeni bir listede toplamak için boşluğa ne gelmelidir?

gecenler = []

for ogrenci in ogrenciler:
    if ogrenci["puan"] >= 50:
        ______
  • A. gecenler = ogrenci
  • B. ogrenciler.remove(ogrenci)
  • C. print(ogrenci)
  • D. gecenler.append(ogrenci)
  • E. gecenler.append(ogrenci["puan"])

Madde 4. Aşağıdaki program ne yazdırır?

kayitlar = [
    {"ad": "Ali", "puan": 70},
    {"ad": "Ayşe", "puan": 40},
    {"ad": "Can", "puan": 90},
]

toplam = 0
for kayit in kayitlar:
    toplam = toplam + kayit["puan"]

print(toplam / len(kayitlar))
  • A. Hata verir; sözlük alanları toplanamaz
  • B. 200
  • C. 66
  • D. 3
  • E. 66.66666666666667

Madde 5. En yüksek puanlı öğrenciyi bulurken yalnızca en yüksek sayıyı değil, kaydın kendisini tutmak neden gerekir?

  • A. Sayı tek başına kimin aldığını söylemez; adı da isteniyorsa ilişkili kaydın saklanması gerekir
  • B. Gerekmez; sonradan puana göre arama yapılabilir
  • C. max() fonksiyonu sözlüklerle çalışmadığı için
  • D. Böylece döngü daha hızlı biter
  • E. Sayılar listede tutulamadığı için

Madde 6. Bölüm başına öğrenci sayısını tutmak için hangi yapı uygundur?

  • A. Tek bir sayaç değişkeni
  • B. Sözlük; bölüm adı anahtar, sayı değer olur
  • C. Demet; değerler değişmeyeceği için
  • D. Küme; her bölüm bir kez tutulur
  • E. Liste; her bölüm için bir eleman eklenir

Madde 7. Türkçe metin alanlarında lower() ile körlemesine gruplama yapmak neden sorun çıkarabilir?

  • A. Gruplama yalnızca sayısal alanlarda yapılabilir
  • B. Sorun çıkarmaz; lower() bütün dillerde aynı çalışır
  • C. Türkçe’ye özgü I / İ dönüşümü genel harf dönüşümüyle aynı sonucu vermez; aynı olması gereken iki değer farklı gruplara düşebilir
  • D. lower() Türkçe karakterleri tümüyle siler
  • E. lower() sözlüklerde kullanılamaz

Madde 8. Eksik bir alanı 0 kabul etmek hangi durumda yanlış olur?

  • A. Alan sayısal olduğunda her zaman yanlıştır
  • B. Yalnızca metin alanlarında yanlış olur
  • C. Yanlış olmaz; 0 güvenli bir varsayılandır
  • D. Alan gerçekten ölçülmemişse; 0 bu durumu geçerli bir ölçüm gibi gösterip ortalamayı aşağı çeker
  • E. 0 hiçbir zaman geçerli bir değer olamaz

Madde 1 — Doğru: B. İki farklı erişim art arda uygulanır: köşeli parantezin ilki indeks, ikincisi anahtardır.

  • A yanlış: İlk köşeli parantez ogrenciler listesine aittir.
  • C yanlış: Silme işlemi yapılmıyor.
  • D yanlış: Atama yok; yalnızca okuma yapılıyor.
  • E yanlış: Yalnızca tek bir kaydın alanı okunur.

Madde 2 — Doğru: C. Filtrelenmiş listedeki bir kaydı değiştirmek özgün listedeki kaydı da değiştirir. Bağımsız kayıt gerekiyorsa ogrenci.copy() kullanılır.

  • A yanlış: append() hiçbir türü kopyalamaz; her zaman başvuru ekler.
  • B yanlış: Kopya hiç oluşmaz; tek bir sözlük vardır.
  • D yanlış: Listeler bağımsızdır ama içindeki kayıtlar paylaşılır.
  • E yanlış: copy() ile kopyalanabilirler.

Madde 3 — Doğru: D. Filtreleme kalıbı budur: koşulu sağlayan kayıt yeni listeye eklenir.

  • A yanlış: Bu, listeyi tek bir kayıtla değiştirir; birikim olmaz.
  • B yanlış: Üzerinde dolaşılan listeden silmek eleman atlamalarına yol açar.
  • C yanlış: Yazdırmak listeyi doldurmaz.
  • E yanlış: Bu yalnızca puanları toplar; öğrenci kaydının kalan alanları kaybolur.

Madde 4 — Doğru: E. Toplam 200, kayıt sayısı 3tür. / işlemi float sonuç üretir.

  • A yanlış: Alanlar sayı olduğu için toplanabilir.
  • B yanlış: Bu toplamdır; bölme sonucu değil.
  • C yanlış: / tam bölme yapmaz; tam sayı sonucu için // gerekirdi.
  • D yanlış: Bu kayıt sayısıdır.

Madde 5 — Doğru: A. En büyüğü ararken hem değeri hem de o değere ait kaydı güncellemek kalıbın çekirdeğidir.

  • B yanlış: Aynı puanı birden fazla öğrenci almış olabilir; sonradan arama belirsizlik üretir.
  • C yanlış: max() uygun bir ölçütle sözlük listelerinde de kullanılabilir; gerekçe bu değildir.
  • D yanlış: Tur sayısı değişmez.
  • E yanlış: Sayılar listede tutulabilir.

Madde 6 — Doğru: B. Grup adının kendisi anahtar olarak kullanılabildiği için gruplama doğrudan kurulur.

  • A yanlış: Tek sayaç toplam öğrenci sayısını verir; bölüm ayrımı yapmaz.
  • C yanlış: Sayaçlar güncellenir; demet değiştirilemez.
  • D yanlış: Küme hangi bölümlerin olduğunu söyler ama sayıları tutmaz.
  • E yanlış: Listede hangi sayının hangi bölüme ait olduğu kaybolur.

Madde 7 — Doğru: C. "İĞNE" ile "iğne" normalleştirmeden sonra bile eşleşmeyebilir. casefold() de bunu tek başına çözmez.

  • A yanlış: Metin alanlarında da gruplama yapılır.
  • B yanlış: Türkçe’deki noktalı/noktasız i ayrımı bu varsayımı bozar.
  • D yanlış: Karakterler silinmez; yalnızca belirli bir harf çiftinde beklenen dönüşüm olmaz.
  • E yanlış: Metin alanlarına uygulanabilir; sorun dönüşümün kendisinde.

Madde 8 — Doğru: D. Girilmemiş bir puanla sıfır alınmış bir puan farklı şeylerdir. Varsayılan seçimi doğrudan sonucu değiştirir.

  • A yanlış: Bazı problemlerde 0 doğru varsayılandır; örneğin hiç satış yapılmamış bir gün.
  • B yanlış: Sorun sayısal alanlarda daha da görünürdür.
  • C yanlış: Güvenli görünür ama eksik veriyi sessizce hesaba katar.
  • E yanlış: Çoğu alanda geçerli bir değerdir; sorun eksik veriyle karışması.

Tek sayfa özet

  • Bir kayıt sözlükle, kayıt kümesi listeyle temsil edilebilir.
  • liste[indeks]["alan"] önce kaydı, sonra alanı seçer.
  • Filtreleme, koşulu sağlayan kayıtları yeni listeye ekleme kalıbıdır; append(ogrenci) sözlüğü kopyalamaz, aynı nesneyi paylaşır.
  • Bağımsız tek seviyeli kayıt gerekiyorsa ogrenci.copy() kullanılabilir.
  • Özetleme alan değerlerini sayma veya biriktirme işlemidir.
  • Boş veri için None döndürmek, “veri yok” durumunu gerçek 0 sonucundan ayırabilir.
  • Maksimum arama kalıbı, yalnızca en büyük sayıyı değil o sayıyla ilişkili kaydı tutabilir.
  • Gruplama için sözlük anahtarları grup adlarını temsil edebilir.
  • Metin alanlarında gruplama sonucu seçilen normalleştirme kuralına bağlıdır; Türkçe I / İ konusu özellikle dikkate alınmalıdır.
  • get(grup, 0) grup toplamı veya sayacı oluştururken kullanışlıdır; eksik alan varsayılanı ise problem kuralına göre seçilmelidir.
  • Veri işleme kodu küçük ve tek amaçlı fonksiyonlara ayrıldığında daha kolay doğrulanır.

Bir sonraki bölümde bu tür kayıtları program dışından almak ve dışarıya yazmak için dosyalar ve CSV biçimiyle çalışacağız.

Bu bölümün kazanımları

Bu bölümü bitiren öğrenci:

  • liste[indeks]["alan"] erişimini iki adımda çözümler.
  • Filtreleme kalıbını kurar ve append()in kaydı kopyalamadığını bilir.
  • Kayıt kümesi üzerinde sayma ve biriktirme ile özet çıkarır.
  • Maksimum arama kalıbında sayıyla birlikte ilişkili kaydı tutar.
  • Sözlük anahtarlarıyla gruplama yapar.
  • Metin alanlarında normalleştirme kuralının gruplama sonucunu belirlediğini açıklar.
  • Eksik alan varsayılanını problem kuralına göre seçer.
Back to top