Standardizarea datelor (Z-score)
Standardizarea (sau scalarea) transformă datele astfel încât să aibă media 0 și deviația standard 1. Este cunoscută și ca Z-score normalization și este una dintre cele mai folosite tehnici de preprocesare.
Cum funcționează?
Formula Z-score: $X_{scaled} = \frac{X - \mu}{\sigma}$
Unde: - \mu = media coloanei (valoarea medie) - \sigma = deviația standard (cât de "împrăștiate" sunt valorile)
După standardizare, datele au: - Media = 0 (centrate în jurul lui zero) - Deviația standard = 1 (aceeași "împrăștiere")
Interpretare intuitivă: O valoare standardizată îți spune câte deviații standard e departe de medie. O valoare de +2 înseamnă că e cu 2 deviații standard peste medie; o valoare de -1 înseamnă că e cu o deviație sub medie.
Implementare
Varianta manuală
import numpy as np
# Date originale
X = np.array([[25, 3000],
[35, 5000],
[45, 7000],
[55, 9000]])
# Calculează media și deviația standard pe fiecare coloană
mean = X.mean(axis=0)
std = X.std(axis=0)
# Aplică formula
X_scaled = (X - mean) / std
print("Media după scalare:", X_scaled.mean(axis=0)) # [0, 0]
print("Std după scalare:", X_scaled.std(axis=0)) # [1, 1]
Cu sklearn (recomandat)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Poți vedea parametrii învățați
print("Media originală:", scaler.mean_)
print("Deviația standard:", scaler.scale_)
Vizualizare
import matplotlib.pyplot as plt
import numpy as np
from sklearn.preprocessing import StandardScaler
# Date originale cu scale diferite
np.random.seed(42)
X = np.column_stack([
np.random.normal(100, 15, 200), # vârstă (medie 100, std 15)
np.random.normal(5000, 1000, 200) # salariu (medie 5000, std 1000)
])
# Standardizare
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# Înainte
axes[0].scatter(X[:, 0], X[:, 1], alpha=0.5)
axes[0].set_xlabel('Vârstă')
axes[0].set_ylabel('Salariu')
axes[0].set_title('Înainte de standardizare')
# După - acum ambele axe au aceeași scală
axes[1].scatter(X_scaled[:, 0], X_scaled[:, 1], alpha=0.5)
axes[1].set_xlabel('Vârstă (standardizată)')
axes[1].set_ylabel('Salariu (standardizat)')
axes[1].set_title('După standardizare')
plt.tight_layout()
plt.show()
Când datele au outlieri: RobustScaler
StandardScaler are o problemă: este sensibil la valori extreme (outlieri). Un singur outlier poate "strâmba" media și deviația standard, afectând toate celelalte valori.
Soluția: RobustScaler folosește mediana și IQR (intervalul intercuartilic) în loc de medie și deviație standard. Mediana nu e afectată de outlieri.
from sklearn.preprocessing import RobustScaler, StandardScaler
import numpy as np
# Date cu un outlier mare
X = np.array([[1], [2], [3], [4], [100]]) # 100 este outlier
# StandardScaler - afectat de outlier
std_scaler = StandardScaler()
X_std = std_scaler.fit_transform(X)
# RobustScaler - robust la outlieri
robust_scaler = RobustScaler()
X_robust = robust_scaler.fit_transform(X)
print("StandardScaler:", X_std.flatten())
print("RobustScaler:", X_robust.flatten())
Rezultat:
StandardScaler: [-0.51 -0.49 -0.46 -0.44 1.89] # outlier afectează tot
RobustScaler: [-1. -0.5 0. 0.5 48.5] # valorile normale sunt centrate corect
Observă că la RobustScaler, valorile 1-4 sunt centrate frumos în jurul lui 0, în timp ce outlier-ul (100) e "îndepărtat" dar nu afectează celelalte valori.
Comparație: StandardScaler vs RobustScaler vs MinMaxScaler
| Metodă | Formula | Robustă la outlieri? | Când o folosesc |
|---|---|---|---|
| StandardScaler | (X - \mu) / \sigma | Nu | Date aproximativ normale, fără outlieri |
| RobustScaler | (X - median) / IQR | Da | Date cu outlieri |
| MinMaxScaler | (X - min) / (max - min) | Nu | Când am nevoie de interval fix [0,1] |
Workflow corect: evitarea data leakage
Ca și la normalizare, trebuie să ai grijă să nu "scurgă" informații din setul de test în procesul de standardizare.
Regula de aur
- Împarte datele în train și test
- Calculează media și deviația standard doar pe train (
fit) - Aplică aceiași parametri pe ambele seturi (
transform)
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 1. Split ÎNAINTE de standardizare
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 2. Fit pe train, transform pe ambele
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) # Învață media/std din train
X_test_scaled = scaler.transform(X_test) # Aplică aceiași parametri
Utilizare în Pipeline
Pipeline-ul gestionează automat acest workflow:
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
pipeline = Pipeline([
('scaler', StandardScaler()),
('classifier', LogisticRegression())
])
# Pipeline gestionează automat fit/transform corect
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
Cu Cross-Validation
Când folosești cross-validation, standardizarea trebuie refăcută pentru fiecare fold. Pipeline-ul rezolvă automat această problemă:
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
# Pipeline asigură standardizare corectă în fiecare fold
model = make_pipeline(StandardScaler(), SVC())
scores = cross_val_score(model, X, y, cv=5)
print(f"Acuratețe medie: {scores.mean():.3f} (+/- {scores.std():.3f})")
Standardizare selectivă
Nu toate coloanele au nevoie de standardizare. Coloanele categorice (codificate ca one-hot) sau coloanele binare (0/1) de obicei nu se standardizează.
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
# Definește coloanele
numeric_features = ['vârstă', 'salariu']
categorical_features = ['departament', 'nivel']
# ColumnTransformer aplică transformări diferite pe coloane diferite
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), numeric_features), # Standardizare
('cat', OneHotEncoder(), categorical_features) # One-hot encoding
]
)
X_transformed = preprocessor.fit_transform(df)
Exemple practice
Impactul standardizării la SVM
SVM este foarte sensibil la scale diferite. Iată un exemplu care arată diferența:
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# Încărcare date
data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
# Fără standardizare
svm = SVC(kernel='rbf')
svm.fit(X_train, y_train)
acc_fara = accuracy_score(y_test, svm.predict(X_test))
# Cu standardizare
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
svm_scaled = SVC(kernel='rbf')
svm_scaled.fit(X_train_scaled, y_train)
acc_cu = accuracy_score(y_test, svm_scaled.predict(X_test_scaled))
print(f"SVM fără standardizare: {acc_fara:.3f}")
print(f"SVM cu standardizare: {acc_cu:.3f}")
Vei vedea o îmbunătățire semnificativă a acurateții cu standardizare.
Interpretarea coeficienților în regresie
La regresia liniară, standardizarea ajută la compararea importanței diferitelor features:
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
import numpy as np
# Date cu scale foarte diferite
np.random.seed(42)
X = np.random.randn(100, 3) * [1, 100, 1000] # Scale: 1, 100, 1000
y = 2*X[:, 0] + 0.5*X[:, 1] + 0.001*X[:, 2] + np.random.randn(100)
# Fără standardizare - coeficienții reflectă scala
lr = LinearRegression()
lr.fit(X, y)
print("Coeficienți fără standardizare:", lr.coef_)
# Cu standardizare - coeficienții sunt comparabili
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
lr_scaled = LinearRegression()
lr_scaled.fit(X_scaled, y)
print("Coeficienți cu standardizare:", lr_scaled.coef_)
După standardizare, coeficienții reflectă importanța reală a fiecărei variabile, nu doar scala ei.
Inversarea standardizării
Dacă ai nevoie să transformi valorile înapoi la scala originală:
from sklearn.preprocessing import StandardScaler
import numpy as np
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# După ce obții predicții în spațiul standardizat...
# Inversare la scala originală
X_original = scaler.inverse_transform(X_scaled)
# Verificare
print(np.allclose(X, X_original)) # True
Rezumat
| Ce vreau să obțin | Ce metodă folosesc |
|---|---|
| Date cu medie 0 și std 1 | StandardScaler |
| Robustețe la outlieri | RobustScaler |
| Valori în interval [0,1] | MinMaxScaler |
Reguli de bază:
- StandardScaler - alegerea default pentru majoritatea cazurilor
- RobustScaler - când ai outlieri în date
- Întotdeauna fit pe train, transform pe test (pentru a evita data leakage)
- Folosește Pipeline pentru un workflow curat și corect
- Nu standardiza variabilele categorice sau target-ul (de obicei)