İç 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 hafta neleri yapabilmelisiniz?
Bölümün sonunda:
- Liste içinde sözlüklerden oluşan küçük kayıt yapısını okuyabilmeli,
- Bir kaydın alanlarına anlamlı anahtarlarla erişebilmeli,
- Belirli koşulu sağlayan kayıtları filtreleyebilmeli,
- Filtrelenen listede aynı sözlük nesnelerinin paylaşılabileceğini açıklayabilmeli,
- Kayıtlardan toplam, ortalama, sayı ve maksimum gibi özetler çıkarabilmeli,
- Boş veri kümesinde “veri yok” ile sayısal
0sonucunu ayırt edebilmeli, - Belirli alana göre basit gruplama yapabilmeli,
- Metin alanlarını gruplarken normalleştirme kuralının sonucu etkileyebileceğini açıklayabilmeli,
- Eksik veya beklenmeyen alanlarda
get()kullanmanın ne zaman uygun olduğunu açıklayabilmeli, - Veri işleme kodunu küçük fonksiyonlara ayırabilmeli,
- Verilen küçük veri kümesinde beklenen sonucu elle hesaplayıp kodla karşılaştırabilmelisiniz.
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.
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.
Önemli: filtrelenen sözlükler kopyalanmış değildir
- 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"]) # 0gecenler[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.
.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) # 450Ortalama 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çekten0.
İ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)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
- 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.
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 toplamBasit 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.
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.
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:
ogrenciler[1]→ ikinci öğrenci sözlüğü["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 toplamlarVeri 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.
Bölüm özeti
- 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
Nonedöndürmek, “veri yok” durumunu gerçek0sonucundan 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.
Kendinizi kontrol edin
ogrenciler[2]["puan"]ifadesini iki adımda açıklayın.gecenler.append(ogrenci)neden yeni bir sözlük kopyası oluşturmaz?- Filtrelenmiş kaydı bağımsız hâle getirmek için hangi basit yöntem kullanılabilir?
- Boş listede
puan_ortalamasineden0yerineNonedöndürüyor? - En yüksek puanlı öğrenciyi bulurken neden yalnızca en yüksek sayıyı değil sözlüğü tutmak yararlıdır?
- Bölüm başına öğrenci sayısı için sözlük neden uygundur?
- Türkçe metin alanlarında
lower()ile körlemesine gruplama neden sorun çıkarabilir? - Eksik alanı
0kabul etmek hangi durumda yanlış olabilir? - Mevcut kaydı değiştirerek yeni alan eklemek ile yeni kayıt üretmek arasında ne fark vardır?
- Veri işleme fonksiyonlarını küçük parçalara ayırmanın yararı nedir?
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.