İç 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ı
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.
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.
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
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.
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.