Python ile Veri Temizleme Nedir ve Neden Bu Kadar Önemli?
Veri bilimi projelerinde harcanan zamanın yaklaşık %80'i veri temizleme ve ön işleme adımlarına gidiyor. Evet, doğru okudunuz — işin büyük kısmı model kurmak değil, veriyi düzeltmek. Eksik değerler, yinelenen kayıtlar, tutarsız tipler ve hatalı girişler analiz sonuçlarınızı doğrudan etkiler. Temizlenmemiş veriyle eğitilen bir makine öğrenmesi modeli? Yanıltıcı tahminler üretir ve iş kararlarını olumsuz yönde etkiler.
Bu rehberde, Ocak 2026'da yayınlanan Pandas 3.0'ın getirdiği yeni özelliklerle birlikte Python'da veri temizleme sürecini adım adım ele alacağız. Copy-on-Write davranışı, pd.col() ifade API'si ve PyArrow destekli yeni str veri tipi gibi yenilikler, temizleme iş akışlarını hem daha güvenli hem de daha performanslı hale getiriyor.
Hadi başlayalım.
Pandas 3.0'ın Veri Temizleme İçin Getirdiği Yenilikler
Copy-on-Write (CoW) ile Güvenli Veri Manipülasyonu
Pandas 3.0'da Copy-on-Write artık varsayılan davranış. Bir DataFrame'den türetilen her alt küme veya dilim, kullanıcı açısından bir kopya gibi davranıyor. Bu ne anlama geliyor? O sinir bozucu SettingWithCopyWarning uyarısı tamamen ortadan kalktı ve zincirleme atama hataları artık geçmişte kaldı.
# Pandas 3.0 öncesi — belirsiz davranış
df["fiyat"][df["kategori"] == "Elektronik"] = 0 # df değişebilir veya değişmeyebilir
# Pandas 3.0 — güvenli ve öngörülebilir
df.loc[df["kategori"] == "Elektronik", "fiyat"] = 0 # Orijinal df güvenle güncellenir
Copy-on-Write sayesinde artık her yere savunmacı .copy() çağrısı serpmenize gerek yok:
# Eski yöntem (savunmacı kopya)
alt_kume = df[maske].copy()
alt_kume["yeni_sutun"] = degerler
# Pandas 3.0 (kopya gerekmiyor)
alt_kume = df[maske]
alt_kume["yeni_sutun"] = degerler # Orijinal df etkilenmez
Açıkçası bu değişiklik tek başına Pandas 3.0'a geçmeye değer.
pd.col() İfade API'si ile Temiz Kod Yazımı
Pandas 3.0'ın en dikkat çekici yeniliklerinden biri pd.col() ifade API'si. Lambda fonksiyonlarına olan bağımlılığı azaltarak daha okunabilir kod yazmanıza olanak tanıyor. Bence özellikle karmaşık assign zincirleri yazanlar bu özelliğe bayılacak.
import pandas as pd
# Eski yöntem — lambda kullanımı
df = df.assign(toplam=lambda x: x["fiyat"] * x["miktar"])
# Pandas 3.0 — pd.col() kullanımı
df = df.assign(toplam=pd.col("fiyat") * pd.col("miktar"))
pd.col() ifadesi .assign(), .loc[] ve getitem/setitem işlemlerinde kullanılabilir. Standart operatörlerin yanı sıra .str, .dt gibi erişimciler de destekleniyor.
PyArrow Destekli Yeni str Veri Tipi
Pandas 3.0 ile birlikte metin sütunları artık varsayılan olarak NumPy object yerine str dtype olarak çıkarılıyor. PyArrow kurulu olduğunda metin işlemleri 5-10 kat daha hızlı çalışıyor — bu ciddi bir fark.
# Pandas 3.0 — otomatik str dtype
seri = pd.Series(["İstanbul", "Ankara", "İzmir"])
print(seri.dtype) # str (PyArrow destekli)
# Veri tipi kontrolü güncellenmeli
# Eski: df["sutun"].dtype == "object"
# Yeni:
pd.api.types.is_string_dtype(df["sutun"])
Eğer eski kodlarınızda dtype == "object" kontrolü varsa, bunu güncellemeyi unutmayın.
Veri Setini Keşfetme ve İlk İnceleme
Temizleme sürecine atlamadan önce veri setinin genel yapısını anlamak şart. Bunu atlayanlar (hepimiz en az bir kere yaptık) sonradan pişman olur. Pandas'ın sağladığı keşif fonksiyonlarıyla hızlıca ön analiz yapabilirsiniz.
import pandas as pd
import numpy as np
# Örnek veri seti oluşturma
veri = {
"isim": ["Ahmet", "Ayşe", None, "Mehmet", "Ayşe", "fatma"],
"yas": [28, np.nan, 35, 42, 31, 29],
"maas": [5500.0, 7200.0, np.nan, 8100.0, 7200.0, np.nan],
"departman": ["IT", "Pazarlama", "IT", "Finans", "Pazarlama", "it"],
"baslangic_tarihi": ["2023-01-15", "2022-06-01", "2024-03-10", "abc", "2022-06-01", "2025-11-20"]
}
df = pd.DataFrame(veri)
# Genel bilgi
print(df.info())
print(df.describe())
print(df.shape)
# Eksik değer özeti
print(df.isna().sum())
print(f"Toplam eksik değer: {df.isna().sum().sum()}")
print(f"Eksik değer oranı:\n{df.isna().mean() * 100}")
Dikkat ettiyseniz örnek verimizde bilinçli olarak sorunlar var: eksik değerler, büyük/küçük harf tutarsızlıkları, geçersiz tarih ve yinelenen kayıtlar. Gerçek dünya verisi tam da böyle gelir.
Eksik Verileri Tespit Etme ve Yönetme
Eksik Veri Türlerini Anlamak
Eksik veriler üç ana kategoride incelenir ve doğru stratejiyi seçmek için bunları bilmek önemli:
- MCAR (Missing Completely at Random): Eksiklik tamamen rastgele. Diğer değişkenlerle hiçbir ilişkisi yok.
- MAR (Missing at Random): Eksiklik, gözlemlenen diğer değişkenlerle ilişkili ama kendi değeriyle değil.
- MNAR (Missing Not at Random): Eksiklik, eksik değerin kendisiyle ilişkili. En karmaşık ve en sinsi durum.
Çoğu gerçek dünya veri setinde MAR ile karşılaşırsınız. MNAR ise özellikle anket verilerinde sık görülür (mesela gelir sorusunu yüksek gelirliler atlar).
dropna() ile Eksik Satırları Kaldırma
dropna() metodu eksik değer içeren satır veya sütunları kaldırır. Ama dikkatli olun — büyük oranda eksik veri varsa ciddi veri kaybına yol açabilir.
# Herhangi bir sütunda NaN olan satırları kaldır
df_temiz = df.dropna()
# Belirli sütunlardaki eksik değerlere göre kaldır
df_temiz = df.dropna(subset=["isim", "yas"])
# Tüm değerleri NaN olan satırları kaldır
df_temiz = df.dropna(how="all")
# En az 3 dolu değere sahip satırları tut
df_temiz = df.dropna(thresh=3)
fillna() ile Eksik Değerleri Doldurma
fillna() yöntemi eksik değerleri sabit bir değer, istatistiksel ölçü veya yayma teknikleriyle dolduruyor. Veri temizlemenin en sık kullandığınız araçlarından biri olacak.
# Sabit değerle doldurma
df["isim"] = df["isim"].fillna("Bilinmiyor")
# Ortalama ile doldurma (aykırı değer yoksa)
df["yas"] = df["yas"].fillna(df["yas"].mean())
# Medyan ile doldurma (aykırı değer varsa tercih edilir)
df["maas"] = df["maas"].fillna(df["maas"].median())
# İleri doldurma (forward fill) — zaman serilerinde etkili
df["maas"] = df["maas"].ffill()
# Geri doldurma (backward fill)
df["maas"] = df["maas"].bfill()
# Sütuna özel farklı stratejiler
df = df.fillna({"isim": "Bilinmiyor", "yas": df["yas"].median(), "maas": 0})
interpolate() ile Akıllı Doldurma
interpolate() metodu çevredeki veri noktalarını kullanarak eksik değerleri tahmin eder. Özellikle zaman serileri ve sürekli sayısal veriler için fillna()'dan çok daha iyi sonuçlar verir.
# Doğrusal interpolasyon
df["maas"] = df["maas"].interpolate(method="linear")
# Spline interpolasyonu (daha yumuşak geçişler)
df["maas"] = df["maas"].interpolate(method="spline", order=2)
# Zaman bazlı interpolasyon
df["deger"] = df["deger"].interpolate(method="time")
Scikit-learn ile Gelişmiş Eksik Veri Doldurma
Pandas'ın yerleşik yöntemleri çoğu zaman yeterli olsa da, bazen daha sofistike yaklaşımlar gerekir. Scikit-learn 1.8 bu konuda üç güçlü imputer sunuyor. Bu imputer'lar ML pipeline'larında kullanılabilir ve veri sızıntısını önlemek için fit/transform API'sini takip ediyor.
SimpleImputer — Tek Değişkenli Doldurma
from sklearn.impute import SimpleImputer
import numpy as np
# Sayısal sütunlar için ortalama stratejisi
imp_sayisal = SimpleImputer(strategy="median")
# Kategorik sütunlar için en sık değer
imp_kategorik = SimpleImputer(strategy="most_frequent")
# Kullanım
df[["yas", "maas"]] = imp_sayisal.fit_transform(df[["yas", "maas"]])
df[["departman"]] = imp_kategorik.fit_transform(df[["departman"]])
KNNImputer — Komşuluk Tabanlı Doldurma
KNNImputer, her eksik değeri en yakın k komşunun değerlerinin ağırlıklı ortalamasıyla dolduruyor. Değişkenler arasında benzerlik ilişkisi olan veri setlerinde oldukça etkili bir yöntem.
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=5, weights="distance")
df_sayisal = df.select_dtypes(include=[np.number])
df[df_sayisal.columns] = imputer.fit_transform(df_sayisal)
IterativeImputer — Çok Değişkenli Yinelemeli Doldurma
IterativeImputer, her özelliği diğer tüm özelliklerden yola çıkarak modelliyor ve bu şekilde dolduruyor. R dilindeki missForest yaklaşımının Python karşılığı diyebiliriz. Uygulaması biraz daha karmaşık ama sonuçları gerçekten etkileyici olabiliyor.
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
from sklearn.ensemble import RandomForestRegressor
# RandomForest tabanlı yinelemeli doldurma
imp_iteratif = IterativeImputer(
estimator=RandomForestRegressor(n_estimators=100, random_state=42),
max_iter=10,
random_state=42
)
df_sayisal = df.select_dtypes(include=[np.number])
df[df_sayisal.columns] = imp_iteratif.fit_transform(df_sayisal)
Yinelenen (Duplicate) Verileri Temizleme
Yinelenen kayıtlar analiz sonuçlarını fena halde çarpıtır. Özellikle birden fazla kaynaktan veri birleştiriyorsanız bu sorunla mutlaka karşılaşırsınız. Neyse ki Pandas bu konuda güçlü araçlar sunuyor.
# Yinelenen satırları tespit et
print(f"Yinelenen satır sayısı: {df.duplicated().sum()}")
# Tüm sütunlara göre yinelenenleri kaldır (ilkini tut)
df = df.drop_duplicates()
# Belirli sütunlara göre yinelenenleri kaldır
df = df.drop_duplicates(subset=["isim", "departman"], keep="last")
# Yinelenen satırları inceleme
yinelenenler = df[df.duplicated(keep=False)]
print(yinelenenler)
Veri Tipi Dönüşümleri ve Düzeltmeler
Yanlış veri tipleri hesaplamalarda beklenmedik hatalara yol açar. Bir sütunun object olarak kalması gerektiğinde float olması veya tam tersi, saatlerce hata ayıklamanıza neden olabilir. Pandas 3.0'da veri tipi yönetimi daha sağlam hale geldi.
Sayısal Dönüşümler
# Güvenli sayısal dönüşüm — hatalar NaN olur
df["yas"] = pd.to_numeric(df["yas"], errors="coerce")
# Tarih dönüşümü — geçersiz tarihler NaN olur
df["baslangic_tarihi"] = pd.to_datetime(df["baslangic_tarihi"], errors="coerce")
# Kategorik dönüşüm — bellek optimizasyonu
df["departman"] = df["departman"].astype("category")
errors="coerce" parametresi burada hayat kurtarıcı — dönüştürülemeyen değerleri sessizce NaN'a çeviriyor, böylece programınız çökmüyor.
Metin Verilerini Standartlaştırma
Pandas 3.0'ın PyArrow destekli str dtype'ı ile metin işlemleri çok daha hızlı. Özellikle büyük veri setlerinde farkı hissedeceksiniz.
# Büyük/küçük harf tutarsızlıklarını giderme
df["departman"] = df["departman"].str.lower().str.strip()
# Boşlukları temizleme
df["isim"] = df["isim"].str.strip()
# İlk harfi büyük yapma
df["isim"] = df["isim"].str.title()
# Regex ile gereksiz karakterleri temizleme
df["telefon"] = df["telefon"].str.replace(r"[^\d]", "", regex=True)
Aykırı Değerleri (Outlier) Tespit Etme ve İşleme
Aykırı değerler, veri setinin genel dağılımından belirgin şekilde sapan gözlemler. Bunları görmezden gelirseniz hem istatistiksel analizleriniz hem de ML modelleriniz bundan olumsuz etkilenir.
IQR Yöntemi ile Aykırı Değer Tespiti
IQR (Interquartile Range) yöntemi en yaygın kullanılan yaklaşım. Basit, etkili ve çoğu durumda iş görür.
def aykiri_deger_tespit(df, sutun):
Q1 = df[sutun].quantile(0.25)
Q3 = df[sutun].quantile(0.75)
IQR = Q3 - Q1
alt_sinir = Q1 - 1.5 * IQR
ust_sinir = Q3 + 1.5 * IQR
aykirilar = df[(df[sutun] < alt_sinir) | (df[sutun] > ust_sinir)]
print(f"{sutun} sütununda {len(aykirilar)} aykırı değer bulundu")
print(f"Alt sınır: {alt_sinir:.2f}, Üst sınır: {ust_sinir:.2f}")
return alt_sinir, ust_sinir
alt, ust = aykiri_deger_tespit(df, "maas")
# Aykırı değerleri sınır değerlerle değiştirme (capping/winsorizing)
df["maas"] = df["maas"].clip(lower=alt, upper=ust)
Z-Score Yöntemi
Normal dağılıma yakın veriler için Z-Score yöntemi de iyi bir alternatif. Genel kural olarak Z-Score'u 3'ün üzerinde olan değerler aykırı kabul edilir.
from scipy import stats
z_skorlari = np.abs(stats.zscore(df["maas"].dropna()))
aykiri_maske = z_skorlari > 3
print(f"Z-Score ile tespit edilen aykırı değer: {aykiri_maske.sum()}")
pipe() ile Veri Temizleme Pipeline'ı Oluşturma
Buraya kadar her adımı ayrı ayrı gördük. Şimdi asıl güzel kısma geçelim: tüm bu adımları tek bir pipeline'da birleştirmek. Pandas'ın pipe() metodu tam da bunu yapıyor — temizleme adımlarını zincirleme şeklinde uygulamanıza olanak tanıyor.
def eksik_deger_doldur(df):
"""Eksik değerleri stratejiye göre doldurur."""
sayisal_sutunlar = df.select_dtypes(include=[np.number]).columns
kategorik_sutunlar = df.select_dtypes(include=["str", "category"]).columns
for sutun in sayisal_sutunlar:
df[sutun] = df[sutun].fillna(df[sutun].median())
for sutun in kategorik_sutunlar:
df[sutun] = df[sutun].fillna(df[sutun].mode()[0])
return df
def yinelenenleri_kaldir(df):
"""Yinelenen satırları kaldırır."""
return df.drop_duplicates()
def metin_standartlastir(df, sutunlar):
"""Metin sütunlarını standartlaştırır."""
for sutun in sutunlar:
if sutun in df.columns:
df[sutun] = df[sutun].str.lower().str.strip()
return df
def veri_tipi_duzelt(df, tarih_sutunlari=None):
"""Veri tiplerini düzeltir."""
if tarih_sutunlari:
for sutun in tarih_sutunlari:
df[sutun] = pd.to_datetime(df[sutun], errors="coerce")
return df
# Pipeline: tüm adımları zincirle
df_temiz = (
df.pipe(eksik_deger_doldur)
.pipe(yinelenenleri_kaldir)
.pipe(metin_standartlastir, sutunlar=["isim", "departman"])
.pipe(veri_tipi_duzelt, tarih_sutunlari=["baslangic_tarihi"])
)
print(f"Temizleme öncesi: {df.shape}")
print(f"Temizleme sonrası: {df_temiz.shape}")
print(df_temiz.info())
Bu yaklaşımın güzelliği, her fonksiyonu bağımsız olarak test edebilmeniz ve ihtiyaç halinde sıralamayı kolayca değiştirebilmeniz.
Scikit-learn Pipeline ile Üretime Hazır Temizleme
Makine öğrenmesi projelerinde veri temizleme adımlarını Scikit-learn Pipeline ve ColumnTransformer ile birleştirmek en doğru yaklaşım. Bu sayede veri sızıntısını (data leakage) önlersiniz ve modeli üretime taşımak çok daha kolay olur.
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer, KNNImputer
# Sütun grupları
sayisal_sutunlar = ["yas", "maas"]
kategorik_sutunlar = ["departman"]
# Sayısal transformer
sayisal_pipeline = Pipeline(steps=[
("imputer", KNNImputer(n_neighbors=5)),
("scaler", StandardScaler())
])
# Kategorik transformer
kategorik_pipeline = Pipeline(steps=[
("imputer", SimpleImputer(strategy="most_frequent")),
("encoder", OneHotEncoder(handle_unknown="ignore"))
])
# Birleştirme
on_isleme = ColumnTransformer(transformers=[
("sayisal", sayisal_pipeline, sayisal_sutunlar),
("kategorik", kategorik_pipeline, kategorik_sutunlar)
])
# Modelle birlikte kullanım
from sklearn.ensemble import RandomForestClassifier
tam_pipeline = Pipeline(steps=[
("on_isleme", on_isleme),
("model", RandomForestClassifier(random_state=42))
])
# tam_pipeline.fit(X_train, y_train)
# tahminler = tam_pipeline.predict(X_test)
Bu pipeline'ı bir kere kurup joblib ile kaydederseniz, aynı ön işleme mantığını üretimde de birebir uygulayabilirsiniz. Eğitim ve tahmin arasındaki tutarsızlık sorunlarına elveda.
Veri Temizleme Kontrol Listesi
Her veri temizleme projesinde bu adımları sırasıyla kontrol edin. Bu listeyi bir yere kaydetmenizi tavsiye ederim:
- Veri keşfi:
info(),describe(),shapeile genel yapıyı anlayın - Eksik değer analizi:
isna().sum()ile eksik değer dağılımını belirleyin - Yinelenenleri kaldırın:
duplicated()ile kontrol edin ve temizleyin - Veri tiplerini düzeltin:
to_numeric(),to_datetime(),astype()kullanın - Metin verilerini standartlaştırın: Büyük/küçük harf, boşluklar, özel karakterler
- Aykırı değerleri işleyin: IQR veya Z-Score ile tespit ve düzeltme
- Eksik değerleri doldurun: Uygun strateji seçin (medyan, KNN, IterativeImputer)
- Doğrulama: Temizleme sonrası
info()vedescribe()ile son kontrolü yapın
Sıkça Sorulan Sorular
Pandas'ta eksik verileri doldurmak için en iyi yöntem hangisidir?
Aslında "en iyi" tek bir yöntem yok — tamamen verinizin yapısına bağlı. Sayısal ve aykırı değer içermeyen veriler için ortalama, aykırı değer varsa medyan tercih edilir. Zaman serilerinde interpolate() daha isabetli sonuçlar verir. Değişkenler arası ilişki güçlüyse KNNImputer veya IterativeImputer denemeye değer. Kategorik veriler içinse mod (en sık değer) stratejisi genellikle en mantıklı seçenek.
Pandas 3.0'da Copy-on-Write nedir ve veri temizlemeyi nasıl etkiler?
Copy-on-Write (CoW), Pandas 3.0'da varsayılan davranış olarak geldi. Bir DataFrame'den türetilen her alt küme artık kopya gibi davranıyor. Bu sayede zincirleme atama hataları ortadan kalkıyor ve SettingWithCopyWarning tarih oluyor. Veri temizleme sürecinde .loc[] kullanarak güncelleme yapmanız gerekiyor ama artık her yere .copy() eklemenize gerek kalmıyor.
fillna() ile interpolate() arasındaki fark nedir?
fillna() eksik değerleri sabit bir değer, ortalama/medyan gibi istatistiklerle ya da ileri/geri yayma ile dolduruyor. interpolate() ise çevredeki veri noktalarına dayanarak matematiksel tahmin yapıyor. Kısa cevap: sürekli sayısal verilerde ve zaman serilerinde interpolate(), genel amaçlı doldurmada fillna() kullanın.
Veri temizlemede aykırı değerleri silmeli miyim yoksa dönüştürmeli miyim?
Eğer aykırı değerler veri giriş hatasından kaynaklanıyorsa silebilirsiniz. Ama gerçek gözlemlerden geliyorsa silmek yerine capping (kırpma) veya winsorizing yöntemiyle sınır değerlerine çekmek daha akıllıca. IQR yöntemiyle sınırları belirleyip clip() ile kırpabilirsiniz. Bu şekilde hem veri kaybını önlersiniz hem de dağılımı korursunuz.
Scikit-learn IterativeImputer ne zaman kullanılmalıdır?
IterativeImputer, değişkenler arasında güçlü ilişkiler olduğunda ve basit ortalama/medyan doldurma yeterli kalmadığında devreye girer. Her özelliği diğer tüm özelliklerden yola çıkarak modelleyerek dolduruyor. Özellikle çok değişkenli veri setlerinde ve ML pipeline'larında etkili. Bir not: hâlâ deneysel statüde, bu yüzden from sklearn.experimental import enable_iterative_imputer satırını eklemeyi unutmayın.