Takip et

itertools.groupby ile Python’da Veri Gruplama Sanatı

Büyük veri setleri içerisinde benzer niteliklere sahip öğeleri hızla tanımlamak ve bunları anlamlı gruplar halinde düzenlemek, çoğu yazılım projesinin temel taşlarından biridir. Python’ın güçlü ve esnek yapısı, bu tür görevler için birçok araç sunsa da, performans ve bellek verimliliği açısından öne çıkan bir yıldız var: itertools.groupby. Bu makale, veri analizinden raporlamaya kadar geniş bir yelpazede kullanılan bu fonksiyonun derinliklerine inerek, onu nasıl ustaca kullanacağınızı adım adım açıklayacak ve gerçek dünya senaryolarıyla pekiştirecektir.

Modern yazılım uygulamaları, sürekli artan hacimlerde veriyle etkileşim halindedir. Finansal işlemlerden sensör verilerine, kullanıcı davranışlarından lojistik kayıtlarına kadar her alanda, genellikle dağınık ve ham formda gelen verileri anlamlı bilgilere dönüştürme ihtiyacı doğar. İşte bu noktada “veri gruplama” devreye girer. Veri gruplama, belirli bir kritere göre benzer öğeleri bir araya getirerek, genel eğilimleri görmemizi, anormal durumları tespit etmemizi ve daha bilinçli kararlar almamızı sağlar.

Örneğin, bir e-ticaret platformunda günlük binlerce sipariş kaydının olduğunu düşünün. Bu siparişleri bölgesine, ürüne veya müşteriye göre gruplayarak satış trendlerini analiz edebilir, en çok satan ürünleri belirleyebilir veya belirli bir kampanya dönemindeki performansı ölçebilirsiniz. Ya da bir sistemin log kayıtlarını incelerken, aynı türdeki hata mesajlarını gruplayarak hangi hataların daha sık tekrar ettiğini ve dolayısıyla acil müdahale gerektirdiğini kolayca anlayabilirsiniz. Bu tür senaryolar, veri gruplamanın sadece bir “kolaylık” değil, aynı zamanda veriye dayalı stratejiler için bir “zorunluluk” olduğunu açıkça ortaya koyar. Geleneksel yöntemlerle (döngüler, manuel listeler vb.) bu işlemleri yapmak hem kod karmaşıklığını artırır hem de performans açısından ciddi darboğazlara yol açabilir. İşte tam da bu nedenle, Python’ın standart kütüphanesinde yer alan ve özellikle büyük veri setleriyle çalışırken imdadımıza yetişen itertools.groupby gibi araçlara ihtiyaç duyarız. Bu fonksiyon, iterasyon protokolünü kullanarak bellek verimli bir şekilde gruplama yapmamızı sağlayarak, daha temiz, daha hızlı ve daha okunabilir kodlar yazmamızın kapısını aralar. Şimdi, bu güçlü aracın temel prensiplerini ve nasıl çalıştığını daha yakından inceleyelim.

itertools.groupby Nedir ve Nasıl Çalışır?

itertools.groupby, Python’ın itertools modülünde bulunan ve adından da anlaşılacağı gibi, bir iterable içindeki ardışık öğeleri belirli bir kritere göre gruplamak için tasarlanmış bir fonksiyondur. Bu fonksiyonun en önemli özelliği, “lazy evaluation” (tembel değerlendirme) prensibiyle çalışmasıdır. Yani, tüm iterable’ı belleğe yüklemek yerine, grupları birer birer üretir, bu da özellikle çok büyük veri setleriyle çalışırken bellek tüketimini minimumda tutar. groupby, çıktı olarak bir iterator döndürür ve bu iterator her bir grup için iki öğeli bir tuple üretir: grubun anahtarı ve o anahtara ait öğeleri içeren başka bir iterator.

Peki, groupby tam olarak nasıl gruplama yapar? Temel çalışma mantığı oldukça basittir ancak bir kritik noktası vardır: groupby, sadece ardışık (yan yana) olan ve aynı anahtara sahip öğeleri gruplar. Bu, verinin groupby‘a verilmeden önce belirli bir kritere göre sıralanmış olmasının hayati önem taşıdığı anlamına gelir. Eğer veri sıralanmamışsa, aynı anahtara sahip olsalar bile yan yana olmayan öğeler farklı gruplara ayrılacaktır. İşte bu nedenle, groupby‘ı genellikle sorted() fonksiyonu ile birlikte kullanırız. groupby fonksiyonu iki ana parametre alır:

  • iterable: Gruplanacak olan veri kaynağı (liste, tuple, string vb.).
  • key (isteğe bağlı): Gruplama işlemini hangi kritere göre yapacağımızı belirleyen bir fonksiyon. Eğer key belirtilmezse, öğelerin kendisi anahtar olarak kullanılır. Genellikle bir lambda fonksiyonu veya mevcut bir fonksiyon kullanılır.

Şimdi basit bir örnekle bu çalışma prensibini pekiştirelim. Bir sayı listemiz olsun ve bu sayıları tek veya çift olmalarına göre gruplamak isteyelim:


from itertools import groupby

sayilar = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Sayıları çift/tek olmalarına göre gruplayalım
# Önce sıralama yapmalıyız, ancak burada gruplama anahtarına göre sıralamadığımız için
# çıktının nasıl olacağını görmek için sıralı olmayan bir örnekle başlayalım.
print("Sıralı olmayan liste ile groupby:")
for k, g in groupby(sayilar, key=lambda x: "Çift" if x % 2 == 0 else "Tek"):
    print(f"Anahtar: {k}, Grup: {list(g)}")

# Çıktı şöyle olacaktır:
# Anahtar: Tek, Grup: [1]
# Anahtar: Çift, Grup: [2]
# Anahtar: Tek, Grup: [3]
# Anahtar: Çift, Grup: [4]
# Anahtar: Tek, Grup: [5]
# Anahtar: Çift, Grup: [6]
# Anahtar: Tek, Grup: [7]
# Anahtar: Çift, Grup: [8]
# Anahtar: Tek, Grup: [9]
# Anahtar: Çift, Grup: [10]

# Gördüğünüz gibi, her sayı ayrı bir grup oluşturdu çünkü tek ve çift sayılar ardışık değildi.
# Doğru gruplama için önce sıralama yapmalıyız:
print("\nSıralı liste ile groupby (ancak anahtara göre sıralı değil):")
sirali_sayilar = sorted(sayilar) # sayıları sıralar, ama tek/çift olarak değil
for k, g in groupby(sirali_sayilar, key=lambda x: "Çift" if x % 2 == 0 else "Tek"):
    print(f"Anahtar: {k}, Grup: {list(g)}")

# Hala aynı sonuç. Çünkü sorted(sayilar) işlemi sayıları küçükten büyüğe sıralar: [1, 2, 3, ...].
# Bizim istediğimiz, gruplama anahtarına göre sıralama.

print("\nGruplama anahtarına göre sıralı liste ile groupby:")
sirali_sayilar_anahtara_gore = sorted(sayilar, key=lambda x: "Çift" if x % 2 == 0 else "Tek")
for k, g in groupby(sirali_sayilar_anahtara_gore, key=lambda x: "Çift" if x % 2 == 0 else "Tek"):
    print(f"Anahtar: {k}, Grup: {list(g)}")

# Şimdi çıktı doğru:
# Anahtar: Tek, Grup: [1, 3, 5, 7, 9]
# Anahtar: Çift, Grup: [2, 4, 6, 8, 10]
    

Bu örnekte görüldüğü gibi, groupby'ın doğru ve beklenen çıktıyı vermesi için, verinin gruplama anahtarına göre önceden sıralanmış olması kritik bir öneme sahiptir. İlk örnekte sıralama olmadığı için, groupby her tek ve çift sayıyı ayrı bir grup olarak değerlendirdi. İkinci örnekte sayıları sıralasak bile, bu sıralama tek/çift kriterine göre olmadığı için yine istenen sonucu alamadık. Üçüncü örnekte ise, sorted() fonksiyonuna aynı key fonksiyonunu vererek, listeyi "tek" ve "çift" olarak ardışık hale getirdik ve bu sayede groupby beklendiği gibi çalıştı. Bu temel prensibi anlamak, itertools.groupby'ı etkili bir şekilde kullanmanın ilk adımıdır.

Uzman İpucu: itertools.groupby, grupları birer birer işlediği için, büyük veri setlerinde belleği verimli kullanır. Ancak her bir grup iterator'ı tüketildikten sonra yeniden kullanılamaz. Bu yüzden, bir grubu işledikten sonra tekrar kullanmanız gerekiyorsa, onu bir listeye dönüştürmek (list(g) gibi) iyi bir pratiktir.

groupby'ı Etkili Kullanmak İçin Veriler Nasıl Hazırlanır?

itertools.groupby'ın gücünü tam anlamıyla kullanabilmek için, gruplama işleminden önce verilerin doğru bir şekilde hazırlanması şarttır. Az önce de belirtildiği gibi, bu hazırlığın en kritik adımı, veriyi gruplama anahtarına göre sıralamaktır. Python'da sıralama işlemini gerçekleştirmek için genellikle sorted() global fonksiyonunu veya liste nesnelerinin sort() metodunu kullanırız. İki yöntem de benzer işlevselliği sunsa da, sorted() yeni bir sıralı liste döndürürken, sort() orijinal listeyi yerinde değiştirir.

sorted() fonksiyonu, iterable'ın yanı sıra bir key parametresi alır. Bu key parametresi, her bir öğe için sıralama kriterini belirleyen bir fonksiyon bekler. İşte bu noktada, groupby için kullandığımız key fonksiyonunu, sorted() için de kullanarak tutarlılık sağlamamız büyük önem taşır. Böylece, groupby'ın beklentisi olan "ardışık aynı anahtar" koşulunu garanti altına almış oluruz.

Şimdi farklı veri tipleri üzerinde groupby'ı nasıl kullanabileceğimizi inceleyelim:

Stringleri Gruplama

Stringleri belirli bir özelliğine göre gruplayabiliriz. Örneğin, bir kelime listesini başlangıç harflerine göre gruplamak isteyebiliriz:


from itertools import groupby

kelimeler = ["elma", "armut", "kiraz", "üzüm", "erik", "çilek", "vişne", "ayva", "muz"]

# Kelimeleri ilk harflerine göre gruplayalım
# Önce sıralama yapmalıyız
sirali_kelimeler = sorted(kelimeler, key=lambda x: x[0])

print("Kelimelerin ilk harflerine göre gruplanması:")
for bas_harf, grup in groupby(sirali_kelimeler, key=lambda x: x[0]):
    print(f"Baş Harf: {bas_harf}, Kelimeler: {', '.join(grup)}")

# Çıktı:
# Baş Harf: a, Kelimeler: armut, ayva
# Baş Harf: ç, Kelimeler: çilek
# Baş Harf: e, Kelimeler: elma, erik
# Baş Harf: k, Kelimeler: kiraz
# Baş Harf: m, Kelimeler: muz
# Baş Harf: ü, Kelimeler: üzüm
# Baş Harf: v, Kelimeler: vişne
    

Bu örnekte, sorted() fonksiyonuna key=lambda x: x[0] vererek kelimeleri ilk harflerine göre sıraladık. Daha sonra aynı key fonksiyonunu groupby'a geçirerek, ilk harfleri aynı olan kelimeleri başarıyla grupladık.

Nesneleri veya Sözlükleri Gruplama

Gerçek dünya senaryolarında sıklıkla karşımıza çıkan bir durum, karmaşık nesne listelerini veya sözlük listelerini gruplamaktır. Örneğin, bir ürün listesini fiyat aralığına veya kategoriye göre gruplayabiliriz:


from itertools import groupby

urunler = [
    {"id": 1, "ad": "Klavye", "kategori": "Elektronik", "fiyat": 250},
    {"id": 2, "ad": "Mouse", "kategori": "Elektronik", "fiyat": 100},
    {"id": 3, "ad": "Defter", "kategori": "Kırtasiye", "fiyat": 20},
    {"id": 4, "ad": "Kalem", "kategori": "Kırtasiye", "fiyat": 10},
    {"id": 5, "ad": "Monitör", "kategori": "Elektronik", "fiyat": 1500},
    {"id": 6, "ad": "Silgi", "kategori": "Kırtasiye", "fiyat": 5},
    {"id": 7, "ad": "USB Bellek", "kategori": "Elektronik", "fiyat": 80},
]

# Ürünleri kategorilerine göre gruplayalım
sirali_urunler = sorted(urunler, key=lambda urun: urun["kategori"])

print("\nÜrünlerin kategorilerine göre gruplanması:")
for kategori, grup in groupby(sirali_urunler, key=lambda urun: urun["kategori"]):
    print(f"Kategori: {kategori}")
    for urun in grup:
        print(f"  - {urun['ad']} ({urun['fiyat']} TL)")

# Çıktı:
# Kategori: Elektronik
#   - Klavye (250 TL)
#   - Mouse (100 TL)
#   - Monitör (1500 TL)
#   - USB Bellek (80 TL)
# Kategori: Kırtasiye
#   - Defter (20 TL)
#   - Kalem (10 TL)
#   - Silgi (5 TL)
    

Bu örnekte, her bir sözlüğün "kategori" anahtarını kullanarak hem sıralama hem de gruplama yaptık. Bu yaklaşım, gerçek dünya uygulamalarında veri yapılarını daha anlamlı hale getirmek için oldukça yaygın ve etkilidir. key fonksiyonu sayesinde, veri yapısının içindeki herhangi bir özelliğe göre gruplama yapma esnekliğine sahip oluruz. Bu sayede, karmaşık veri setlerini analiz etmek ve raporlamak çok daha kolay ve verimli hale gelir. Unutulmamalıdır ki, groupby'dan dönen her grup da bir iterator olduğundan, gruplanmış öğelere erişmek için genellikle bir döngü veya list() gibi bir dönüştürücü kullanmamız gerekir.

Gerçek Dünya Senaryolarında groupby: Vaka Analizleri

itertools.groupby, sadece teorik örneklerle değil, günlük yazılım geliştirme pratiklerinde de kendini kanıtlamış güçlü bir araçtır. Özellikle büyük veri setlerini işlerken ve belirli kriterlere göre raporlama yaparken sunduğu verimlilik ve kod okunabilirliği paha biçilmezdir. İşte iki farklı gerçek dünya senaryosu üzerinden groupby'ın nasıl kullanılabileceğine dair detaylı vaka analizleri:

Vaka 1: Müşteri Siparişlerini Kategoriye Göre Gruplama ve Özetleme

Bir e-ticaret uygulamasında çalıştığınızı ve belirli bir zaman dilimindeki tüm sipariş verilerine sahip olduğunuzu varsayalım. Her sipariş, ürünler, fiyatlar, tarihler gibi bilgiler içeriyor. Amacımız, bu siparişleri satın alınan ürünlerin kategorilerine göre gruplamak ve her kategori için toplam satış miktarını ve satılan ürün sayısını özetlemektir. Bu analiz, hangi kategorilerin daha popüler olduğunu veya hangi kategorilerin satış performansının düşük olduğunu anlamak için kritik öneme sahiptir.

Öncelikle, örnek bir sipariş veri seti oluşturalım. Her bir sipariş öğesi, bir sözlük olarak temsil edilecek ve kategori, urun_adi ve fiyat gibi alanları içerecektir.


from itertools import groupby

# Örnek müşteri sipariş verisi
siparis_detaylari = [
    {"siparis_id": "ORD001", "kategori": "Elektronik", "urun_adi": "Akıllı Telefon", "fiyat": 5000},
    {"siparis_id": "ORD002", "kategori": "Giyim", "urun_adi": "T-shirt", "fiyat": 150},
    {"siparis_id": "ORD001", "kategori": "Elektronik", "urun_adi": "Kulaklık", "fiyat": 750},
    {"siparis_id": "ORD003", "kategori": "Ev Aletleri", "urun_adi": "Kahve Makinesi", "fiyat": 1200},
    {"siparis_id": "ORD002", "kategori": "Giyim", "urun_adi": "Kot Pantolon", "fiyat": 400},
    {"siparis_id": "ORD004", "kategori": "Elektronik", "urun_adi": "Akıllı Saat", "fiyat": 1500},
    {"siparis_id": "ORD003", "kategori": "Ev Aletleri", "urun_adi": "Tost Makinesi", "fiyat": 300},
    {"siparis_id": "ORD005", "kategori": "Kitap", "urun_adi": "Bilim Kurgu Romanı", "fiyat": 80},
    {"siparis_id": "ORD001", "kategori": "Elektronik", "urun_adi": "Ekran Koruyucu", "fiyat": 100},
    {"siparis_id": "ORD005", "kategori": "Kitap", "urun_adi": "Tarih Kitabı", "fiyat": 120},
]

# Adım 1: Veriyi "kategori" anahtarına göre sırala
sirali_siparisler = sorted(siparis_detaylari, key=lambda x: x["kategori"])

# Adım 2: Sıralanmış veriyi "kategori"ye göre grupla
print("Siparişlerin kategori bazında gruplanmış özeti:\n")
for kategori, grup_iterator in groupby(sirali_siparisler, key=lambda x: x["kategori"]):
    # Her grup iterator'ını bir listeye dönüştürerek işleyelim
    grup_listesi = list(grup_iterator)
    
    toplam_fiyat = sum(item["fiyat"] for item in grup_listesi)
    urun_sayisi = len(grup_listesi)
    
    print(f"Kategori: {kategori}")
    print(f"  Toplam Satış Fiyatı: {toplam_fiyat} TL")
    print(f"  Satılan Ürün Sayısı: {urun_sayisi}\n")

# Çıktı:
# Siparişlerin kategori bazında gruplanmış özeti:
#
# Kategori: Elektronik
#   Toplam Satış Fiyatı: 7350 TL
#   Satılan Ürün Sayısı: 4
#
# Kategori: Ev Aletleri
#   Toplam Satış Fiyatı: 1500 TL
#   Satılan Ürün Sayısı: 2
#
# Kategori: Giyim
#   Toplam Satış Fiyatı: 550 TL
#   Satılan Ürün Sayısı: 2
#
# Kategori: Kitap
#   Toplam Satış Fiyatı: 200 TL
#   Satılan Ürün Sayısı: 2
    

Bu vaka analizinde, groupby sayesinde karmaşık bir sipariş listesini sadece birkaç satır kodla kategorilere ayırıp, her kategori için anlamlı özet bilgiler elde ettik. Bu yaklaşım, büyük veri setlerinde raporlama ve iş zekası uygulamaları için oldukça verimlidir.

Vaka 2: Log Dosyalarını Hata Tipine Göre Analiz Etme

Bir sunucu veya uygulamanın ürettiği log dosyaları, sistemin durumu hakkında hayati bilgiler içerir. Ancak genellikle bu dosyalar yüzbinlerce, hatta milyonlarca satır içerebilir. Belirli bir zaman dilimindeki log girdilerini hata tiplerine (örneğin, ERROR, WARNING, INFO) göre gruplayarak, sistemdeki sorunların yoğunluğunu ve türünü hızlıca anlayabiliriz. Bu, hata ayıklama ve sistem sağlığı izleme süreçlerinde büyük kolaylık sağlar.

Örnek log girdilerini simüle edelim. Her bir log satırı, bir zaman damgası, hata seviyesi ve mesajdan oluşsun.


from itertools import groupby
import datetime

# Örnek log girdileri (gerçekte bir dosyadan okunurdu)
log_kayitlari = [
    "2023-10-26 10:01:05 INFO User 'admin' logged in.",
    "2023-10-26 10:01:10 WARNING Disk space low on /dev/sda1.",
    "2023-10-26 10:01:15 INFO Data processed successfully.",
    "2023-10-26 10:01:20 ERROR Database connection failed.",
    "2023-10-26 10:01:25 INFO Report generated.",
    "2023-10-26 10:01:30 WARNING API rate limit exceeded.",
    "2023-10-26 10:01:35 ERROR File not found: config.ini.",
    "2023-10-26 10:01:40 INFO Backup completed.",
    "2023-10-26 10:01:45 ERROR Invalid input parameter.",
    "2023-10-26 10:01:50 WARNING Memory usage high.",
]

# Log satırlarından hata seviyesini ayrıştırmak için bir anahtar fonksiyon
def get_log_level(log_entry):
    # Log girdisi "ZAMAN SEVIYE MESAJ" formatında varsayılıyor
    parts = log_entry.split(' ', 2) # Sadece ilk iki boşluğa kadar böl
    if len(parts) > 1:
        return parts[2].split(' ')[0] # İkinci kelime (INFO, WARNING, ERROR)
    return "UNKNOWN"

# Adım 1: Log kayıtlarını seviyelerine göre sırala
# Sorted fonksiyonuna, log seviyesini çıkaran key fonksiyonunu veriyoruz.
# Hata seviyelerini belirli bir öncelik sırasına göre sıralamak isteyebiliriz (ERROR > WARNING > INFO)
# Bu durumda özel bir sıralama anahtarı oluşturulmalı. Şimdilik alfabetik sıralayalım.
sirali_log_kayitlari = sorted(log_kayitlari, key=get_log_level)

# Adım 2: Sıralanmış log kayıtlarını seviyelerine göre grupla
print("Log kayıtlarının seviye bazında analizi:\n")
for seviye, grup_iterator in groupby(sirali_log_kayitlari, key=get_log_level):
    grup_listesi = list(grup_iterator)
    print(f"Seviye: {seviye} (Toplam {len(grup_listesi)} kayıt)")
    for log_entry in grup_listesi:
        print(f"  - {log_entry}")
    print()

# Çıktı:
# Log kayıtlarının seviye bazında analizi:
#
# Seviye: ERROR (Toplam 3 kayıt)
#   - 2023-10-26 10:01:20 ERROR Database connection failed.
#   - 2023-10-26 10:01:35 ERROR File not found: config.ini.
#   - 2023-10-26 10:01:45 ERROR Invalid input parameter.
#
# Seviye: INFO (Toplam 4 kayıt)
#   - 2023-10-26 10:01:05 INFO User 'admin' logged in.
#   - 2023-10-26 10:01:15 INFO Data processed successfully.
#   - 2023-10-26 10:01:25 INFO Report generated.
#   - 2023-10-26 10:01:40 INFO Backup completed.
#
# Seviye: WARNING (Toplam 3 kayıt)
#   - 2023-10-26 10:01:10 WARNING Disk space low on /dev/sda1.
#   - 2023-10-26 10:01:30 WARNING API rate limit exceeded.
#   - 2023-10-26 10:01:50 WARNING Memory usage high.
    

Bu örnekte, log kayıtları bir dosyadan okunur gibi string formatında geldi. get_log_level adında küçük bir yardımcı fonksiyon yazarak her bir log girdisinden hata seviyesini (ERROR, WARNING, INFO) ayıklamayı başardık. Ardından bu fonksiyonu hem sorted() hem de groupby() için key olarak kullanarak, logları seviyelerine göre grupladık ve her seviye için kaç kayıt olduğunu ve bu kayıtlardan bazılarını listeledik. Bu, hata ayıklama süreçlerinde zaman kazandıran ve genel sistem sağlığı hakkında hızlı bir bakış sağlayan çok etkili bir yöntemdir.

Bu vaka analizleri, itertools.groupby'ın sadece liste işlemekten çok daha fazlasını yapabileceğini ve karmaşık veri setlerini anlamlı şekillerde düzenlemek için ne kadar güçlü bir araç olduğunu göstermektedir. Verileri doğru bir şekilde hazırlayarak ve uygun anahtar fonksiyonları kullanarak, groupby'ın potansiyelini tam olarak ortaya çıkarabilirsiniz.

groupby ile İleri Düzey Teknikler ve Optimizasyonlar

itertools.groupby'ın temel prensiplerini ve pratik kullanımlarını anladıktan sonra, daha karmaşık senaryolarla başa çıkmak ve performansı optimize etmek için bazı ileri düzey tekniklere ve ipuçlarına geçebiliriz. Bu bölüm, özellikle büyük veri setleriyle çalışan veya çoklu kriterlere göre gruplama ihtiyacı duyan geliştiriciler için faydalı olacaktır.

Birden Fazla Kriterle Gruplama Yapılabilir mi?

Tek bir kritere göre gruplama yapmak oldukça kolaydır, peki ya verileri aynı anda birden fazla özelliğine göre gruplamak istersek? Örneğin, siparişleri önce kategoriye, sonra da sipariş tarihine göre gruplamak isteyebiliriz. Bu durumda sorted() fonksiyonunun key parametresine bir tuple döndüren bir fonksiyon verebiliriz.

Python'da tuple'lar, soldan sağa doğru eleman eleman sıralanır. Bu özellik sayesinde, sorted() fonksiyonu tuple'ın ilk elemanına göre sıralar; eğer ilk elemanlar eşitse, ikinci elemana göre sıralar ve bu şekilde devam eder. groupby da bu sıralanmış veriyi alarak, tuple anahtarına göre gruplamayı başarıyla yapar.


from itertools import groupby
from datetime import datetime

# Örnek müşteri sipariş verisi (tarih eklendi)
siparisler = [
    {"siparis_id": "ORD001", "kategori": "Elektronik", "urun_adi": "Telefon", "fiyat": 5000, "tarih": "2023-10-26"},
    {"siparis_id": "ORD002", "kategori": "Giyim", "urun_adi": "T-shirt", "fiyat": 150, "tarih": "2023-10-26"},
    {"siparis_id": "ORD003", "kategori": "Elektronik", "urun_adi": "Kulaklık", "fiyat": 750, "tarih": "2023-10-27"},
    {"siparis_id": "ORD004", "kategori": "Giyim", "urun_adi": "Pantolon", "fiyat": 400, "tarih": "2023-10-26"},
    {"siparis_id": "ORD005", "kategori": "Ev Aletleri", "urun_adi": "Fırın", "fiyat": 2000, "tarih": "2023-10-27"},
    {"siparis_id": "ORD006", "kategori": "Elektronik", "urun_adi": "Tablet", "fiyat": 3000, "tarih": "2023-10-26"},
    {"siparis_id": "ORD007", "kategori": "Giyim", "urun_adi": "Ayakkabı", "fiyat": 600, "tarih": "2023-10-27"},
]

# Önce kategoriye, sonra tarihe göre sıralama
# Tarihleri karşılaştırılabilir hale getirmek için datetime objelerine dönüştürebiliriz
sirali_siparisler_coklu_kriter = sorted(
    siparisler, 
    key=lambda x: (x["kategori"], datetime.strptime(x["tarih"], "%Y-%m-%d"))
)

print("Siparişlerin kategori ve tarihe göre gruplanması:\n")
for (kategori, tarih), grup_iterator in groupby(
    sirali_siparisler_coklu_kriter, 
    key=lambda x: (x["kategori"], datetime.strptime(x["tarih"], "%Y-%m-%d"))
):
    print(f"Kategori: {kategori}, Tarih: {tarih.strftime('%Y-%m-%d')}")
    for siparis_detay in grup_iterator:
        print(f"  - {siparis_detay['urun_adi']} ({siparis_detay['fiyat']} TL)")
    print()

# Çıktı:
# Siparişlerin kategori ve tarihe göre gruplanması:
#
# Kategori: Elektronik, Tarih: 2023-10-26
#   - Telefon (5000 TL)
#   - Tablet (3000 TL)
#
# Kategori: Elektronik, Tarih: 2023-10-27
#   - Kulaklık (750 TL)
#
# Kategori: Ev Aletleri, Tarih: 2023-10-27
#   - Fırın (2000 TL)
#
# Kategori: Giyim, Tarih: 2023-10-26
#   - T-shirt (150 TL)
#   - Pantolon (400 TL)
#
# Kategori: Giyim, Tarih: 2023-10-27
#   - Ayakkabı (600 TL)
    

Bu teknik, veri setinizdeki farklı boyutlara göre detaylı analizler yapmak için inanılmaz derecede esneklik sunar. lambda fonksiyonu içinde birden fazla anahtar döndüren bir tuple oluşturarak, groupby'ın gücünü katlayabilirsiniz.

Performans İpuçları ve Büyük Veri Setlerinde Kullanım

itertools.groupby, tasarımsal olarak bellek dostu bir araçtır çünkü lazy evaluation prensibiyle çalışır. Ancak, performans üzerindeki en büyük etken, gruplama öncesi uygulanan sıralama işlemidir.

  • Sıralamanın Maliyeti: Büyük veri setleri için sorted() fonksiyonunu kullanmak önemli bir zaman ve CPU maliyeti oluşturabilir. Veri boyutu arttıkça, bu maliyet daha da belirginleşir (genellikle O(N log N) karmaşıklığı). Eğer veriniz zaten kısmen veya tamamen sıralıysa, bu maliyetten tasarruf edebilirsiniz.
  • Lazy Evaluation'ın Faydaları: groupby'ın kendisi, döndürdüğü iteratorlar sayesinde, tüm grupları belleğe aynı anda yüklemez. Her bir grubu sadece ihtiyaç duyulduğunda işler. Bu, özellikle gigabaytlarca veri içeren log dosyaları veya büyük veritabanı sorgu sonuçları gibi senaryolarda bellek yetersizliği yaşamanızı engeller. Her bir grup iterator'ını list(g) gibi bir listeye dönüştürmek, o grubun tüm öğelerini belleğe yükleyecektir. Bu nedenle, mümkünse her grubu işlem bitmeden tüketmek, bellek verimliliği açısından daha iyidir.
  • Veritabanı Seviyesinde Sıralama: Eğer veriniz bir veritabanında saklanıyorsa, veriyi Python'a çekmeden önce SQL sorgunuzda ORDER BY ifadesini kullanarak sıralama işlemini veritabanı motoruna yaptırmak genellikle daha verimlidir. Veritabanları, büyük veri setlerini sıralamak için optimize edilmiş algoritmalar ve indeksler kullanır.

groupby'ın Sınırlamaları Nelerdir?

groupby'ın en önemli sınırlaması, yalnızca ardışık olan aynı anahtara sahip öğeleri gruplamasıdır. Eğer veri sıralanmamışsa veya aynı anahtara sahip öğeler arasında farklı anahtarlara sahip öğeler varsa, groupby onları ayrı gruplar olarak ele alacaktır. Bu, yukarıdaki örneklerde sorted() kullanımıyla açıkça vurgulandı.

Peki ya sıralama yapmak istemiyorsak veya verimiz rastgele dağılmışsa? Bu durumda groupby uygun bir seçenek olmayabilir. Alternatif olarak şunlar kullanılabilir:

  • collections.defaultdict veya Dictionary: Eğer verinin tümünü belleğe alabilirseniz ve sıralama önceliğiniz yoksa, bir sözlük veya defaultdict kullanmak daha uygun olabilir. Bu yapılar, tüm öğeleri anahtara göre gruplayarak saklayabilir, ardışık olma zorunluluğu yoktur.

from collections import defaultdict

# Sıralanmamış bir liste
veriler = [
    {"kategori": "A", "deger": 10},
    {"kategori": "B", "deger": 20},
    {"kategori": "A", "deger": 15},
    {"kategori": "C", "deger": 30},
    {"kategori": "B", "deger": 25},
]

gruplanmis_veriler = defaultdict(list)
for item in veriler:
    gruplanmis_veriler[item["kategori"]].append(item)

for k, v in gruplanmis_veriler.items():
    print(f"Kategori: {k}, Öğeler: {v}")

# Çıktı:
# Kategori: A, Öğeler: [{'kategori': 'A', 'deger': 10}, {'kategori': 'A', 'deger': 15}]
# Kategori: B, Öğeler: [{'kategori': 'B', 'deger': 20}, {'kategori': 'B', 'deger': 25}]
# Kategori: C, Öğeler: [{'kategori': 'C', 'deger': 30}]
    

defaultdict, verileri ardışık olma şartı aramadan gruplar ancak tüm veriyi bellekte tutar. Bu nedenle, bellek kısıtlamaları olan büyük veri setleri için groupby'ın performans avantajı daha belirgin hale gelir.

Mobil Uyumluluk ve Modern Web İçin HTML Yapısı

Bu makalede kullandığımız HTML yapısı, temel SEO ilkelerine ve okunabilirliğe odaklanmıştır. Ancak günümüz dünyasında içeriğin farklı ekran boyutlarında sorunsuz görünmesi, yani mobil uyumluluk, vazgeçilmez bir standarttır. Makaledeki kod blokları, listeler veya tablolar gibi öğelerin mobil cihazlarda taşma yapmadan, düzenli bir şekilde görüntülenmesi için CSS media query'lerden faydalanabiliriz.

Yukarıdaki CSS kodları, makalenin görsel sunumunu iyileştirmek ve farklı ekran boyutlarına uyum sağlamak için tasarlanmıştır. Özellikle pre (kod blokları) ve table (tablolar) etiketleri için overflow-x: auto; kullanımı, küçük ekranlarda yatay kaydırma çubuklarının otomatik olarak çıkmasını sağlayarak içeriğin taşmasını engeller. @media screen and (max-width: 768px) kuralı ise, ekran genişliği 768 pikselin altına düştüğünde belirli stil değişiklikleri uygulayarak daha iyi bir mobil deneyim sunar. Bu yaklaşımlar, modern web geliştirmenin temelini oluşturur.

Sonuç: Neden itertools.groupby Bilgisayarınızda Olmalı?

Bu makale boyunca itertools.groupby'ın ne olduğunu, nasıl çalıştığını, neden öncelikle veriyi sıralamamız gerektiğini ve farklı gerçek dünya senaryolarında nasıl kullanılabileceğini detaylı bir şekilde inceledik. Gördüğümüz gibi, bu küçük ama güçlü fonksiyon, Python'da veri gruplama işlemlerini gerçekleştirmek için son derece etkili ve zarif bir yol sunar. Özellikle büyük veri setleriyle çalışırken, bellek verimliliği ve performans açısından sunduğu avantajlar onu vazgeçilmez kılar.

groupby'ı kullanmanın temel faydaları şunlardır:

  • Bellek Verimliliği: Iterator tabanlı çalışması sayesinde tüm veriyi belleğe yüklemeden işler, bu da büyük veri setlerinde bellek sorunlarının önüne geçer.
  • Kod Temizliği ve Okunabilirlik: Gruplama mantığını net ve öz bir şekilde ifade etmeyi sağlar, kodunuzu daha anlaşılır hale getirir.
  • Performans: Doğru kullanıldığında (özellikle önceden sıralanmış veya veritabanı seviyesinde sıralanmış verilerle), manuel döngülerden veya daha az optimize edilmiş yaklaşımlardan daha hızlı sonuçlar verebilir.
  • Esneklik: key fonksiyonu sayesinde, basit değerlerden karmaşık nesne özelliklerine kadar her türlü kritere göre gruplama yapma imkanı sunar.

İster bir veri analisti olun, ister bir web geliştiricisi, isterse sistem yöneticisi; verileri düzenleme ve analiz etme ihtiyacı her zaman karşınıza çıkacaktır. itertools.groupby, bu tür görevler için Python araç setinizde mutlaka bulunması gereken bir fonksiyondur. Onunla tanıştığınızda, veri gruplama işlerinizin ne kadar kolaylaştığına şaşıracaksınız. Şimdi, aklınızdaki bazı olası soruları yanıtlayarak makalemizi tamamlayalım.

Sıkça Sorulan Sorular

1. groupby ve collections.defaultdict arasındaki temel fark nedir?
groupby, bir iterable içindeki ardışık öğeleri gruplar ve bellek verimli bir şekilde iteratorlar döndürür. Verinin önceden sıralanması gerekir. collections.defaultdict (veya genel bir Python sözlüğü) ise, verileri sıralama gereksinimi olmaksızın anahtarlarına göre bir araya getirir ancak tüm veriyi belleğe yükler. Bellek kısıtlamaları veya iterator tabanlı işleme gerektiğinde groupby tercih edilirken, sıralama önceliği yoksa ve tüm veri belleğe sığabiliyorsa defaultdict daha basit bir çözüm olabilir.
2. groupby ile bir listeyi iki kez gruplayabilir miyim?
Evet, ancak dikkatli olmak gerekir. groupby'dan dönen her grup da bir iterator'dır ve bir kez tüketildikten sonra tekrar kullanılamaz. Yani, bir grubu işledikten sonra (örneğin list(grup) yaparak veya üzerinde döngü kurarak), o grubu ikinci kez işleyemezsiniz. Eğer aynı veriyi farklı kriterlere göre tekrar gruplamak isterseniz, orijinal veri kaynağına geri dönmeniz veya ilk gruplama sonrası oluşan her grubu ayrı bir listeye kaydederek o listeler üzerinde ikinci bir groupby işlemi uygulamanız gerekir.
3. groupby neden sıralanmış veri ister?
groupby'ın tasarımı gereği, sadece yan yana olan aynı anahtara sahip öğeleri tek bir grup olarak değerlendirir. Eğer veri sıralanmamışsa, aynı anahtara sahip öğeler iterable içinde ayrı noktalarda bulunabilir ve bu durumda groupby her ayrı noktayı farklı bir grup olarak algılar. Bu nedenle, doğru ve eksiksiz gruplama için verinin gruplama anahtarına göre önceden sıralanmış olması zorunludur.
4. groupby'ın output'u bir liste midir?
Hayır, groupby bir iterator döndürür. Bu iterator, her grup için iki öğeli tuple'lar üretir: grubun anahtarı ve o anahtara ait öğeleri içeren başka bir iterator. Bu iç iterator'lar da tembeldir ve öğeleri yalnızca ihtiyaç duyulduğunda sağlar. Bir grup içindeki tüm öğelere erişmek isterseniz, o grup iterator'ını list() fonksiyonu ile bir listeye dönüştürmeniz gerekir (örn: list(grup)).
5. Büyük bir dosyayı groupby ile işlerken bellek sorunları yaşar mıyım?
groupby'ın kendisi bellek açısından oldukça verimlidir çünkü veriyi parça parça işler (lazy evaluation). Ancak, dosyayı groupby'a vermeden önce dosyanın tümünü belleğe okursanız veya sorted() gibi bir fonksiyonla tüm veriyi sıralarsanız, bu sıralama işlemi büyük dosyalar için bellek sorunlarına yol açabilir. En iyi uygulama, dosyayı satır satır okumak, okurken sıralamak (örneğin, bir veritabanı sorgusu veya özel bir harici sıralama algoritmasıyla) ve sonra sıralanmış stream üzerinde groupby kullanmaktır.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.
Exit mobile version