Feature engineering: modelden önce gelen sanat
2026 · 15 dakika okuma · Python + pandas + sklearn
İki veri bilimci aynı algoritmayı kullanıyor. Biri %72, diğeri %89 doğruluk alıyor. Fark nerede? Büyük ihtimalle feature engineering'de.
Daha iyi algoritma aramadan önce elindeki veriyi daha iyi temsil etmeyi öğren. Çoğu zaman bu, algoritmayı değiştirmekten çok daha etkili.
1. Sayısal özellikler: dönüşümler
Ham sayılar her zaman modele hazır değildir. Çarpık dağılımlar, farklı ölçekler, aykırı değerler — bunlar modelin işini zorlaştırır.
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Log dönüşümü — çarpık dağılımları normalize eder
# Gelir, fiyat, nüfus gibi sağa çarpık veriler için
df['log_gelir'] = np.log1p(df['gelir']) # log1p: 0 için güvenli
# Kare kök — daha yumuşak dönüşüm
df['sqrt_alan'] = np.sqrt(df['alan'])
# Standardizasyon — ortalama 0, std 1
# SVM, KNN, logistic regression için şart
scaler = StandardScaler()
df['gelir_scaled'] = scaler.fit_transform(df[['gelir']])
# Min-Max normalizasyon — [0,1] aralığına çek
# Sinir ağları ve görüntü işleme için yaygın
minmax = MinMaxScaler()
df['fiyat_norm'] = minmax.fit_transform(df[['fiyat']])
# Binning — sayıyı kategoriye çevir
df['yas_grubu'] = pd.cut(df['yas'],
bins=[0, 18, 35, 55, 100],
labels=['genc', 'yetiskin', 'orta_yas', 'yasli']
)Tree-based modeller (Random Forest, XGBoost) ölçeklemeye ihtiyaç duymaz. Ama doğrusal modeller ve uzaklık bazlı modeller (KNN, SVM) için standardizasyon neredeyse zorunlu.
2. Kategorik özellikler: encoding
Modeller sayı anlar, metin anlamaz. Kategorik değişkenleri sayıya çevirmek için birden fazla yol var — hangisini seçeceğin veriye ve modele bağlı.
from sklearn.preprocessing import LabelEncoder, OrdinalEncoder
import pandas as pd
df = pd.DataFrame({'sehir': ['İzmir', 'İstanbul', 'Ankara', 'İzmir'],
'egitim': ['lise', 'lisans', 'yuksek_lisans', 'doktora']})
# One-Hot Encoding — sırasız kategoriler için
# Dezavantaj: çok kategori varsa boyut patlar
sehir_dummies = pd.get_dummies(df['sehir'], prefix='sehir')
# sehir_Ankara, sehir_İstanbul, sehir_İzmir
# Ordinal Encoding — sıralı kategoriler için
oe = OrdinalEncoder(categories=[['lise', 'lisans', 'yuksek_lisans', 'doktora']])
df['egitim_encoded'] = oe.fit_transform(df[['egitim']])
# lise=0, lisans=1, yuksek_lisans=2, doktora=3
# Target Encoding — yüksek kardinalite için (şehir, posta kodu)
# Her kategoriyi hedef değişkenin ortalamasıyla değiştir
target_mean = df.groupby('sehir')['fiyat'].mean()
df['sehir_target'] = df['sehir'].map(target_mean)
# Dikkat: data leakage riskine karşı cross-val içinde kullan3. Tarih/zaman özellikleri
Tarih sütunu tek başına anlamsız. Ama ondan çıkarılacak özellikler altın değerinde: hafta sonu mu, tatil mi, ayın kaçıncı günü, geçen olaydan kaç gün geçmiş?
df['tarih'] = pd.to_datetime(df['tarih'])
# Temel zaman özellikleri
df['yil'] = df['tarih'].dt.year
df['ay'] = df['tarih'].dt.month
df['gun'] = df['tarih'].dt.day
df['haftanin_gunu'] = df['tarih'].dt.dayofweek # 0=Pzt, 6=Paz
df['hafta_ici'] = df['haftanin_gunu'].lt(5).astype(int)
df['ceyrek'] = df['tarih'].dt.quarter
# Zaman farklılıkları
bugun = pd.Timestamp('today')
df['kac_gun_once'] = (bugun - df['tarih']).dt.days
# Döngüsel encoding — ay ve gün için
# Aralık (12) ile Ocak (1) aslında birbirine yakın
# Ama sayısal olarak 11 birim uzak — yanlış!
import numpy as np
df['ay_sin'] = np.sin(2 * np.pi * df['ay'] / 12)
df['ay_cos'] = np.cos(2 * np.pi * df['ay'] / 12)
# Artık Aralık ve Ocak geometrik olarak yakın4. Etkileşim özellikleri (interaction features)
İki özelliğin kombinasyonu bazen tek başlarından çok daha güçlü bir sinyal taşır. Özellikle doğrusal modellerde bu özellikler büyük fark yaratır.
# Basit çarpım etkileşimi
df['alan_x_oda'] = df['alan'] * df['oda_sayisi']
df['fiyat_per_m2'] = df['fiyat'] / df['alan']
# Oran özellikleri
df['bos_oda_orani'] = df['bos_oda'] / df['toplam_oda']
df['gelir_borclanma_orani'] = df['borc'] / df['gelir']
# Polinom özellikler — sklearn ile otomatik
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X[['alan', 'oda_sayisi', 'yas']])
# alan², oda², alan×oda, alan×yas, oda×yas, yas²
print(f"Orijinal: 3 özellik → Polinom: {X_poly.shape[1]} özellik")5. Eksik veri: silmek değil, öğrenmek
Eksik veri bazen bir sinyal taşır. "Bu alan neden boş?" sorusu bazen en değerli özelliği yaratır.
# Eksik veriyi göster
print(df.isnull().sum())
print(df.isnull().mean().sort_values(ascending=False))
# Strateji 1: Eksikliği özellik olarak kullan
df['gelir_eksik'] = df['gelir'].isnull().astype(int)
# Strateji 2: Basit doldurma
df['yas'].fillna(df['yas'].median(), inplace=False)
# Strateji 3: Grup ortalamasıyla doldur
df['fiyat'] = df.groupby('sehir')['fiyat'].transform(
lambda x: x.fillna(x.median())
)
# Strateji 4: Model ile doldur (KNN Imputer)
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=5)
df_imputed = pd.DataFrame(
imputer.fit_transform(df),
columns=df.columns
)6. Özellik seçimi: az ama öz
100 özellik ürettin, hepsi işe yaramıyor. Gereksiz özellikler modeli yavaşlatır, overfitting'e iter. Seçim şart.
from sklearn.feature_selection import SelectKBest, f_classif, mutual_info_regression from sklearn.ensemble import RandomForestClassifier import pandas as pd # Korelasyon bazlı — basit ama hızlı corr = df.corr()['hedef'].abs().sort_values(ascending=False) print(corr.head(10)) # İstatistiksel test (ANOVA F-test) selector = SelectKBest(f_classif, k=10) X_selected = selector.fit_transform(X, y) # Model bazlı önem skoru (en güvenilir) rf = RandomForestClassifier(n_estimators=100, random_state=42) rf.fit(X_train, y_train) importance = pd.Series(rf.feature_importances_, index=X.columns) print(importance.sort_values(ascending=False).head(10)) # Recursive Feature Elimination from sklearn.feature_selection import RFE rfe = RFE(estimator=rf, n_features_to_select=15) X_rfe = rfe.fit_transform(X, y)
Feature engineering bir sanat. Veri setini ve iş problemini gerçekten anlamadan iyi özellik üretemezsin. En değerli özellikler genellikle domain knowledge'den gelir, otomatik araçlardan değil.
Hepsini bir pipeline'a topla
Feature engineering adımlarını pipeline'a koymak data leakage'i önler, kodu temiz tutar ve production'a taşımayı kolaylaştırır.
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import GradientBoostingClassifier
# Sayısal ve kategorik sütunları ayır
numeric_cols = ['alan', 'yas', 'gelir']
categoric_cols = ['sehir', 'tip']
# Her tip için işlem zinciri
numeric_pipeline = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
categoric_pipeline = Pipeline([
('imputer', SimpleImputer(strategy='most_frequent')),
('encoder', OneHotEncoder(handle_unknown='ignore'))
])
# Birleştir
preprocessor = ColumnTransformer([
('num', numeric_pipeline, numeric_cols),
('cat', categoric_pipeline, categoric_cols)
])
# Modelle birleştir
full_pipeline = Pipeline([
('preprocessor', preprocessor),
('model', GradientBoostingClassifier())
])
full_pipeline.fit(X_train, y_train)
print(f"Test skoru: {full_pipeline.score(X_test, y_test):.3f}")Sıradaki yazıda veri görselleştirme rehberi: hangi grafik ne zaman kullanılır, hangi araç hangi durumda?