Skip to content

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.

X_{scaled} = \frac{X - median}{IQR}
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

  1. Împarte datele în train și test
  2. Calculează media și deviația standard doar pe train (fit)
  3. 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ă:

  1. StandardScaler - alegerea default pentru majoritatea cazurilor
  2. RobustScaler - când ai outlieri în date
  3. Întotdeauna fit pe train, transform pe test (pentru a evita data leakage)
  4. Folosește Pipeline pentru un workflow curat și corect
  5. Nu standardiza variabilele categorice sau target-ul (de obicei)