Skip to content

Normalizarea datelor

Normalizarea transformă valorile datelor într-un interval standard, de obicei între 0 și 1. Este un pas esențial pentru mulți algoritmi de machine learning care sunt sensibili la scala datelor.


De ce avem nevoie de normalizare?

Imaginează-ți că vrei să prezici dacă un client va cumpăra un produs, pe baza vârstei și salariului:

import pandas as pd

df = pd.DataFrame({
    'vârstă': [25, 35, 45, 55],        # Interval: 25-55
    'salariu': [3000, 5000, 7000, 9000], # Interval: 3000-9000
    'ani_experiență': [2, 5, 10, 15]    # Interval: 2-15
})

Problema e că salariul are valori mult mai mari decât vârsta sau experiența. Fără normalizare, algoritmii bazați pe distanțe (cum ar fi KNN) vor considera că diferența de salariu e mult mai importantă decât diferența de vârstă, doar pentru că numerele sunt mai mari.

Algoritmi afectați de scale diferite:

  • KNN - calculează distanțe, deci valorile mari domină
  • Rețele neurale - convergență lentă dacă inputurile au scale diferite
  • SVM - performanță slabă pe date nescalate
  • Regresia liniară - coeficienții devin greu de interpretat

Metoda 1: Min-Max Normalization

Transformă toate valorile în intervalul [0, 1]. Valoarea minimă devine 0, valoarea maximă devine 1.

Formula: $X_{norm} = \frac{X - X_{min}}{X_{max} - X_{min}}$

import numpy as np
from sklearn.preprocessing import MinMaxScaler

# Date originale
X = np.array([[25, 3000], [35, 5000], [45, 7000], [55, 9000]])

# Varianta manuală
X_min = X.min(axis=0)
X_max = X.max(axis=0)
X_norm = (X - X_min) / (X_max - X_min)
print(X_norm)

# Cu sklearn (recomandat)
scaler = MinMaxScaler()
X_norm = scaler.fit_transform(X)
print(X_norm)

Rezultat:

[[0.    0.   ]
 [0.333 0.333]
 [0.667 0.667]
 [1.    1.   ]]

Acum ambele coloane au valori între 0 și 1, și niciuna nu domină calculele.

Interval personalizat

Dacă vrei un alt interval (de exemplu, -1 la 1):

scaler = MinMaxScaler(feature_range=(-1, 1))
X_norm = scaler.fit_transform(X)

Metoda 2: Max Absolute Scaling

Împarte fiecare valoare la valoarea maximă absolută din acea coloană. Rezultatul e în intervalul [-1, 1].

Formula: $X_{norm} = \frac{X}{|X_{max}|}$

from sklearn.preprocessing import MaxAbsScaler

scaler = MaxAbsScaler()
X_norm = scaler.fit_transform(X)

Când e utilă: Pentru date sparse (cu multe zerouri) sau date care pot fi negative. Păstrează zerourile ca zerouri și semnul valorilor.


Metoda 3: Normalizarea L1 și L2

Aceste metode normalizează fiecare rând (nu fiecare coloană) astfel încât să aibă o "lungime" unitară.

Norma L1 - suma valorilor absolute devine 1

from sklearn.preprocessing import Normalizer
import numpy as np

normalizer = Normalizer(norm='l1')
X_norm = normalizer.fit_transform(X)

# Verificare: suma absolută pe fiecare rând = 1
print(np.abs(X_norm).sum(axis=1))  # [1. 1. 1. 1.]

Norma L2 - lungimea vectorului devine 1

normalizer = Normalizer(norm='l2')
X_norm = normalizer.fit_transform(X)

# Verificare: norma euclidiană pe fiecare rând = 1
print(np.linalg.norm(X_norm, axis=1))  # [1. 1. 1. 1.]

Când e utilă: În clasificarea textului (TF-IDF), unde vrei să compari documente indiferent de lungimea lor.


Vizualizare: Cum arată datele înainte și după

import matplotlib.pyplot as plt
import numpy as np
from sklearn.preprocessing import MinMaxScaler, StandardScaler, MaxAbsScaler

# Date cu distribuție exponențială
np.random.seed(42)
data = np.random.exponential(scale=2, size=(100, 1))

fig, axes = plt.subplots(1, 4, figsize=(15, 4))

# Original
axes[0].hist(data, bins=20, edgecolor='black')
axes[0].set_title('Original')
axes[0].set_xlabel(f'Range: [{data.min():.1f}, {data.max():.1f}]')

# Min-Max
scaler_mm = MinMaxScaler()
data_mm = scaler_mm.fit_transform(data)
axes[1].hist(data_mm, bins=20, edgecolor='black')
axes[1].set_title('Min-Max [0,1]')

# Standardizare (Z-score)
scaler_std = StandardScaler()
data_std = scaler_std.fit_transform(data)
axes[2].hist(data_std, bins=20, edgecolor='black')
axes[2].set_title('Standardizare (Z-score)')

# Max Absolute
scaler_max = MaxAbsScaler()
data_max = scaler_max.fit_transform(data)
axes[3].hist(data_max, bins=20, edgecolor='black')
axes[3].set_title('Max Absolute')

plt.tight_layout()
plt.show()

Observă că forma distribuției rămâne aceeași - normalizarea doar schimbă scala, nu distribuția.


Ce algoritmi au nevoie de normalizare?

Algoritm Necesită normalizare? De ce?
KNN Da Calculează distanțe între puncte
SVM Da Sensibil la scale diferite
Rețele neurale Da Convergență mai rapidă
K-Means Da Calculează distanțe
PCA Da Variația depinde de scală
Regresie liniară Opțional Ajută la interpretarea coeficienților
Decision Trees Nu Bazat pe reguli, nu pe distanțe
Random Forest Nu Bazat pe arbori
XGBoost Nu Bazat pe arbori

Regulă simplă: Dacă algoritmul calculează distanțe sau folosește gradient descent, normalizează datele.


Utilizare corectă în pipeline

O greșeală comună este să normalizezi toate datele înainte de a le împărți în train/test. Problema: informații din setul de test "se scurg" în procesul de normalizare (știi min/max-ul întregului set).

Greșit - data leakage

# ❌ NU așa
scaler = MinMaxScaler()
X_normalized = scaler.fit_transform(X)  # Folosește info din test!
X_train, X_test = train_test_split(X_normalized)

Corect - fit pe train, transform pe test

# ✅ Așa e corect
X_train, X_test, y_train, y_test = train_test_split(X, y)

scaler = MinMaxScaler()
X_train_norm = scaler.fit_transform(X_train)  # Învață din train
X_test_norm = scaler.transform(X_test)        # Aplică pe test

Și mai bine - folosește Pipeline

Pipeline-ul gestionează automat această separare:

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import MinMaxScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

pipeline = Pipeline([
    ('normalizer', MinMaxScaler()),
    ('classifier', KNeighborsClassifier(n_neighbors=5))
])

pipeline.fit(X_train, y_train)
accuracy = pipeline.score(X_test, y_test)

Inversarea normalizării

După ce obții predicții, uneori vrei să le transformi înapoi la scala originală:

scaler = MinMaxScaler()
X_norm = scaler.fit_transform(X)

# ... antrenezi modelul, obții predicții ...

# Inversare la scala originală
X_original = scaler.inverse_transform(X_norm)

Exemplu complet

import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score

# Date
df = pd.DataFrame({
    'vârstă': [25, 30, 35, 40, 45, 50, 55, 60],
    'salariu': [3000, 3500, 4500, 5000, 6000, 7000, 8000, 9000],
    'cumpără': [0, 0, 0, 1, 1, 1, 1, 1]
})

X = df[['vârstă', 'salariu']].values
y = df['cumpără'].values

# Split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42
)

# Fără normalizare
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
acc_fara = accuracy_score(y_test, knn.predict(X_test))
print(f"Acuratețe fără normalizare: {acc_fara:.2f}")

# Cu normalizare
scaler = MinMaxScaler()
X_train_norm = scaler.fit_transform(X_train)
X_test_norm = scaler.transform(X_test)

knn_norm = KNeighborsClassifier(n_neighbors=3)
knn_norm.fit(X_train_norm, y_train)
acc_cu = accuracy_score(y_test, knn_norm.predict(X_test_norm))
print(f"Acuratețe cu normalizare: {acc_cu:.2f}")

Normalizare vs Standardizare

Sunt două tehnici diferite pentru același scop: aducerea datelor la o scală comună.

Aspect Normalizare (Min-Max) Standardizare (Z-score)
Interval rezultat [0, 1] sau personalizat Medie 0, deviație standard 1
Sensibilă la outlieri Da (foarte) Mai puțin
Când o folosesc Rețele neurale, imagini Date cu distribuție normală, PCA

Regulă simplă: - Folosește Min-Max când ai nevoie de valori într-un interval fix (ex: inputuri pentru rețele neurale) - Folosește Standardizare când datele au o distribuție aproximativ normală sau când ai outlieri