Pandas'ta en çok yanlış bilinen 7 şey
Yıllarca gördüğüm yaygın ama yanlış pattern'ler · 15 dakika okuma
Pandas öğrenmek kolay. Pandas'ı doğrukullanmayı öğrenmek zor. Çünkü yanlış kullanan kod çalışır — sadece yavaş, güvensiz veya bakımı zor olur. Yıllarca kod review'larında gördüğüm 7 yaygın hatayı derledim.
1. inplace=True aslında ne işe yarar?
Pek çok kişi inplace=True'nun belleği daha verimli kullandığını zanneder. Yanılgı. Pandas arka planda yine de yeni bir kopya oluşturur, sadece döndürmez. Üstelik zincirleme işlemleri engeller ve hata ayıklamayı zorlaştırır.
# Kötü
df.drop(columns=['gereksiz'], inplace=True)
df.rename(columns={'eski': 'yeni'}, inplace=True)
# İyi — zincirleme, okunabilir, güvenli
df = (df
.drop(columns=['gereksiz'])
.rename(columns={'eski': 'yeni'})
)
# inplace=True'nun tek avantajı yok gibi.
# Pandas geliştiricileri de kaldırmayı tartışıyor.2. SettingWithCopyWarning seni uyarıyor, dinle
Bu uyarıyı görünce çoğu kişi ya görmezden gelir ya da.copy()ekleyerek susturur. Ama asıl mesaj şu: orijinal DataFrame'i mi değiştiriyorsun, yoksa geçici bir kopyayı mı? Cevabı bilmiyorsan bug üretiyorsun demektir.
# Bu bir uyarı üretir ve belirsizdir: filtre = df[df['sehir'] == 'İzmir'] filtre['yeni_sutun'] = 1 # SettingWithCopyWarning! # Çözüm 1: Kopyayı açıkça al filtre = df[df['sehir'] == 'İzmir'].copy() filtre['yeni_sutun'] = 1 # Güvenli # Çözüm 2: Orijinali değiştirmek istiyorsan loc kullan df.loc[df['sehir'] == 'İzmir', 'yeni_sutun'] = 1 # Doğrudan
3. apply() çoğu zaman yanlış araç
apply()güçlü ama yavaş. Python döngüsü kadar yavaş, çünkü zaten bir döngü. Pandas'ın vektörize operasyonları 10x-100x daha hızlı çalışır.
import pandas as pd
import numpy as np
df = pd.DataFrame({'fiyat': [100, 200, 300, 400, 500]})
# Kötü — apply ile satır satır
df['kdv'] = df['fiyat'].apply(lambda x: x * 0.18)
# İyi — vektörize, çok daha hızlı
df['kdv'] = df['fiyat'] * 0.18
# Kötü — string işleminde apply
df['isim'] = df['isim'].apply(lambda x: x.upper())
# İyi — str accessor kullan
df['isim'] = df['isim'].str.upper()
# apply() ne zaman kullanılır?
# Sadece vektörize edilemeyen karmaşık iş mantığında.
# Örnek: birden fazla sütuna bakan koşullu hesaplamalar.Kural: önce vektörize yöntemi ara. Yoksa numpy kullan. O da yoksa apply(). iterrows() ise neredeyse hiç kullanma.
4. merge, join ve concat karıştırılıyor
Üçü de birleştirme yapar ama farklı durumlarda kullanılır. Yanlış seçim hem yavaşlatır hem de beklenmedik sonuçlar üretir.
# merge — SQL JOIN gibi, ortak sütun üzerinden sonuc = pd.merge(df1, df2, on='musteri_id', how='left') # join — index üzerinden birleştirme sonuc = df1.join(df2, on='musteri_id') # concat — satır veya sütun ekleme (yığma) sonuc = pd.concat([df1, df2], axis=0) # satır ekle sonuc = pd.concat([df1, df2], axis=1) # sütun ekle # Yaygın hata: concat ile merge karıştırmak # concat index'e bakar, merge sütuna bakar # İki DataFrame'in sütunları aynıysa concat doğru # Farklı anahtarlar üzerinden birleştiriyorsan merge doğru
5. Tarih sütunlarını string bırakmak
Tarih sütununu string olarak bırakırsan filtreleme yavaş, aritmetik imkansız, gruplama anlamsız olur. Her zaman datetime'a dönüştür.
# Kötü — string olarak kaldı
df = pd.read_csv('veri.csv')
df['tarih'] # object dtype — yavaş ve kısıtlı
# İyi — okurken dönüştür
df = pd.read_csv('veri.csv', parse_dates=['tarih'])
# Ya da sonradan dönüştür
df['tarih'] = pd.to_datetime(df['tarih'])
# Artık bunları yapabilirsin:
df['ay'] = df['tarih'].dt.month
df['gun_adi'] = df['tarih'].dt.day_name()
df['hafta'] = df['tarih'].dt.isocalendar().week
# Tarihler arası fark
df['gun_fark'] = (df['bitis'] - df['baslangic']).dt.days
# Belirli dönem filtresi
filtre = df[df['tarih'].between('2024-01-01', '2024-06-30')]6. Bellek optimizasyonu: category dtype
Az sayıda tekrarlayan string değeri olan sütunlar (şehir, kategori, cinsiyet gibi) object olarak tutulursa bellekte çok yer kaplar.category dtype kullanmak bazen %90 bellek tasarrufu sağlar.
import sys
df['sehir'] = pd.Series(['İzmir', 'İstanbul', 'Ankara'] * 100_000)
# object dtype bellek kullanımı
print(df['sehir'].nbytes) # ~24 MB
# category dtype'a dönüştür
df['sehir'] = df['sehir'].astype('category')
print(df['sehir'].nbytes) # ~100 KB — 240x tasarruf!
# Tüm DataFrame'i optimize et
def optimize_df(df):
for col in df.select_dtypes(include='object').columns:
if df[col].nunique() / len(df) < 0.05: # %5'ten az unique
df[col] = df[col].astype('category')
for col in df.select_dtypes(include='int64').columns:
df[col] = pd.to_numeric(df[col], downcast='integer')
for col in df.select_dtypes(include='float64').columns:
df[col] = pd.to_numeric(df[col], downcast='float')
return df7. groupby sonrası reset_index unutulmak
groupby().agg()sonucunda index'ler gruplama sütunlarına dönüşür. Bunu fark etmeden devam edersen sonraki merge veya filtrelemede beklenmedik hatalar alırsın.
# groupby sonucu — sehir index'e düştü
ozet = df.groupby('sehir')['fiyat'].mean()
print(ozet.index) # Index(['Ankara', 'İstanbul', 'İzmir'])
# Merge yapmak istersen sorun çıkar
# Çözüm 1: reset_index
ozet = df.groupby('sehir')['fiyat'].mean().reset_index()
# Çözüm 2: as_index=False (daha temiz)
ozet = df.groupby('sehir', as_index=False)['fiyat'].mean()
# Çözüm 3: named aggregation ile sütun adı ver
ozet = df.groupby('sehir', as_index=False).agg(
ortalama_fiyat=('fiyat', 'mean'),
ilan_sayisi=('fiyat', 'count'),
medyan_fiyat=('fiyat', 'median')
)
# Temiz, okunabilir, merge'e hazırBonus: iterrows() kullanma
Listeye eklemek istedim ama 7'de bırakmak daha iyi görünüyordu. Yine de söylemeden geçemem: iterrows()pandas'ın en yavaş yöntemi. 1 milyon satırlık DataFrame'de dakikalarca sürebilir. Vektörize işlem, apply() veya en kötü ihtimalle itertuples() kullan.
# Asla bu şekilde kullanma:
for idx, row in df.iterrows():
df.at[idx, 'yeni'] = row['a'] + row['b']
# Vektörize — binlerce kat hızlı:
df['yeni'] = df['a'] + df['b']Sıradaki yazıda zaman serisi analizi: TÜİK enflasyon verisiyle trend, mevsimsellik ve basit tahmin modeli.