Sz
Sıfır Gecikme
veri bilimi · türkçe
Tümüİnteraktif📖Rehber🛠Araç📊Vaka💼Kariyer🐍Playground📚Öğren
Hakkımda
Ana sayfarehber

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 kullan

3. 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ın

4. 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?

💬 Yorumlar