Er is een gezegde in machine learning: garbage in, garbage out. Je kunt het meest geavanceerde algoritme ter wereld hebben, maar als je het slecht voorbereide gegevens invoert, zullen de resultaten slecht zijn. Omgekeerd kan een eenvoudig algoritme op basis van uitstekende, zorgvuldig voorbereide gegevens beter presteren dan een complex algoritme op basis van ruwe, rommelige gegevens.
Feature engineering is het vak waarbij ruwe data worden omgezet in representaties waar machine learning-algoritmen effectief van kunnen leren. Het is misschien wel de meest impactvolle vaardigheid in toegepast machinaal leren – en het is waar diepgaande domeinkennis datawetenschap ontmoet.
Bij machinaal leren is een kenmerk elke meetbare eigenschap of attribuut van het ding dat u probeert te voorspellen. Functies zijn de invoer voor uw model; het label (of doel) is de uitvoer.
Voor een huizenprijsvoorspellingsmodel:
Voor een e-mailspamclassificator:
De kwaliteit, kwantiteit en relevantie van uw functies zijn vaak belangrijker dan welk algoritme u kiest.
Gegevens uit de echte wereld zijn rommelig. Machine learning-algoritmen stellen specifieke eisen waaraan ruwe data vrijwel nooit voldoen:
Feature engineering is het proces waarbij de kloof wordt overbrugd tussen onbewerkte gegevens en modelklare invoer.
Wanneer kenmerken zeer verschillende schalen hebben, kunnen modellen worden misleid. Een object met waarden in de duizenden kan een object met waarden tussen 0 en 1 domineren.
Inloggen deelnemen aan de discussie
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X_train)
# Before: age=[22, 65, 34], income=[18000, 95000, 42000]
# After: age=[0.0, 1.0, 0.27], income=[0.0, 1.0, 0.31]
Robuuster voor uitschieters dan min-max-schaling:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)
# Transforms each feature to have mean=0, standard deviation=1
# Feature value → (value - mean) / std_dev
Vuistregel: gebruik standaard standaardisatie. Gebruik min-max wanneer u waarden nodig hebt in een specifiek begrensd bereik (bijvoorbeeld neurale netwerkinvoer).
Categorieën als 'rood/groen/blauw' of 'Londen/Manchester/Birmingham' hebben geen natuurlijke numerieke volgorde. U moet ze coderen voordat u ze aan de meeste algoritmen kunt doorgeven.
Creëert een binaire kolom voor elke categorie. Het beste als categorieën geen inherente volgorde hebben:
import pandas as pd
# Original: colour = ['red', 'green', 'blue', 'red']
df = pd.DataFrame({'colour': ['red', 'green', 'blue', 'red']})
encoded = pd.get_dummies(df['colour'], prefix='colour')
print(encoded)
# colour_blue colour_green colour_red
# 0 0 0 1
# 1 0 1 0
# 2 1 0 0
# 3 0 0 1
Wijst elke categorie toe aan een geheel getal. Alleen gebruiken als de categorieën een natuurlijke volgorde hebben:
from sklearn.preprocessing import LabelEncoder
# Works for: ['low', 'medium', 'high'] → [0, 1, 2]
# DANGER: Don't use for unordered categories like cities —
# the model will incorrectly assume London(0) < Paris(1) < Tokyo(2)
le = LabelEncoder()
df['education_level'] = le.fit_transform(df['education_level'])
# 'school' → 0, 'undergraduate' → 1, 'postgraduate' → 2
Ontbrekende gegevens zijn vrijwel universeel in datasets uit de echte wereld. Je hebt verschillende opties:
Vul ontbrekende waarden in met een berekende vervanging:
from sklearn.impute import SimpleImputer
import numpy as np
# Strategy options: 'mean', 'median', 'most_frequent', 'constant'
imputer = SimpleImputer(strategy='median')
X_imputed = imputer.fit_transform(X)
# For numerical data: median is robust to outliers
# For categorical data: use 'most_frequent'
Soms is het feit dat gegevens ontbreken op zichzelf al informatief:
# Create a binary flag: 1 if income was missing, 0 if present
df['income_missing'] = df['income'].isna().astype(int)
# Then impute the original column
df['income'].fillna(df['income'].median(), inplace=True)
Verwijder een kolom als deze meer dan ~70-80% ontbrekende waarden bevat (zelden informatief). Laat een rij alleen vallen als u over voldoende gegevens beschikt en de rij willekeurig ontbreekt (niet systematisch).
Dit is waar feature-engineering een kunst wordt. U gebruikt domeinkennis om functies te creëren die patronen vastleggen die niet zichtbaar zijn in de onbewerkte gegevens.
df['purchase_datetime'] = pd.to_datetime(df['purchase_datetime'])
# Extract meaningful components
df['hour_of_day'] = df['purchase_datetime'].dt.hour
df['day_of_week'] = df['purchase_datetime'].dt.dayofweek
df['is_weekend'] = (df['day_of_week'] >= 5).astype(int)
df['month'] = df['purchase_datetime'].dt.month
df['is_holiday'] = df['purchase_datetime'].isin(uk_holidays).astype(int)
Een onbewerkte tijdstempel vertelt het model niets direct; deze afgeleide kenmerken leggen patronen bloot zoals "fraudepieken om 3 uur 's nachts" of "verkooppiek in het weekend".
# Square footage × number of bathrooms might be more predictive
# than either feature alone for house prices
df['size_per_bathroom'] = df['sqft'] / df['bathrooms']
# Ratio of income to loan amount — a classic credit risk feature
df['debt_to_income'] = df['loan_amount'] / df['annual_income']
from sklearn.feature_extraction.text import TfidfVectorizer
# Convert raw text to numerical feature matrix
vectorizer = TfidfVectorizer(max_features=1000, stop_words='english')
text_features = vectorizer.fit_transform(df['review_text'])
# Each word becomes a feature; its value reflects how important
# the word is in that document relative to the whole corpus
Nadat u een model heeft gebouwd, kunt u meten welke functies het meest nuttig zijn gebleken. Dit is waardevol, zowel voor het begrijpen van uw model als voor het beslissen welke functies u wilt behouden of laten vallen:
from sklearn.ensemble import RandomForestClassifier
import matplotlib.pyplot as plt
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Get feature importances
importances = pd.Series(
model.feature_importances_,
index=feature_names
).sort_values(ascending=False)
print(importances.head(10))
# income_to_debt_ratio 0.187
# days_since_last_payment 0.143
# credit_utilisation 0.121
# ...
Functies die bijna niet belangrijk zijn, kunnen meestal worden verwijderd zonder dat dit de prestaties beïnvloedt. Het verwijderen ervan vereenvoudigt het model, verkort de trainingstijd en kan de generalisatie verbeteren.
Als je honderden of duizenden functies hebt, kunnen modellen het moeilijk hebben - dit wordt de vloek van de dimensionaliteit genoemd. Dimensionaliteitsreductietechnieken comprimeren kenmerken terwijl de belangrijkste patronen behouden blijven:
from sklearn.decomposition import PCA
# Reduce 100 features to 10 components that capture 95% of variance
pca = PCA(n_components=10)
X_reduced = pca.fit_transform(X_scaled)
print(f"Variance explained: {pca.explained_variance_ratio_.sum():.2%}")
# Variance explained: 94.7%
PCA (Principal Component Analysis) is de meest gebruikelijke aanpak, maar andere omvatten t-SNE (voor visualisatie) en UMAP (voor het behoud van de lokale structuur).
Je hebt een dataset met een kolom 'stad' die 50 verschillende stadsnamen bevat. Welke coderingsaanpak moet u gebruiken?