4.6 Procesarea datelor
Datele reale vin cu găuri, lungimi diferite și scări incompatibile. Această lecție adună toate operațiile de pregătire pe care programa le cere explicit.
Teorie
Gestionarea datelor lipsă și neregulate
df.isna().sum() # inventarul golurilor
df.dropna(subset=["target"]) # elimini rânduri (doar dacă sunt puține!)
df["pret"] = df["pret"].clip(lower=0) # valori imposibile
df.loc[df["varsta"] > 120, "varsta"] = None # neregulate → lipsă
Imputare: medie, mediană, completare anterioară
| Metodă | Când o folosești |
|---|---|
| media | distribuție simetrică, fără outlieri |
| mediana | distribuții asimetrice / outlieri (alegerea sigură) |
| completare anterioară (ffill) | serii de timp — valoarea de ieri o umple pe azi |
| valoare specială / modă | coloane categorice |
from sklearn.impute import SimpleImputer
df["venit"] = df["venit"].fillna(df["venit"].median())
df["temperatura"] = df["temperatura"].ffill() # serii de timp
imp = SimpleImputer(strategy="median") # varianta sklearn (în Pipeline)
X_train = imp.fit_transform(X_train); X_test = imp.transform(X_test)
Padding pentru secvențe de lungimi variabile
Modelele vor intrări de aceeași lungime; secvențele (propoziții, semnale) au lungimi diferite. Soluția: le aduci la aceeași lungime — completezi cu 0 pe cele scurte, tai pe cele lungi.
def pad(secventa, lungime, valoare=0):
return (secventa + [valoare] * lungime)[:lungime]
secvente = [[1, 2], [3, 4, 5, 6, 7], [8]]
X = [pad(s, 5) for s in secvente]
# [[1,2,0,0,0], [3,4,5,6,7], [8,0,0,0,0]]
Normalizare și standardizare
Standardizare (StandardScaler): (x − μ)/σ → medie 0, deviație 1.
Alegerea implicită pentru majoritatea modelelor.
Normalizare min-max (MinMaxScaler):
(x − min)/(max − min) → interval [0, 1]. Standard pentru pixeli
(împărțirea la 255) și când vrei limite fixe.
Împărțirea în antrenare / validare / test
from sklearn.model_selection import train_test_split
# 70% train, 15% validare, 15% test
X_tr, X_rest, y_tr, y_rest = train_test_split(X, y, test_size=0.3,
random_state=42, stratify=y)
X_va, X_te, y_va, y_te = train_test_split(X_rest, y_rest, test_size=0.5,
random_state=42, stratify=y_rest)
Train = învățare; validare = alegerea modelului/hiperparametrilor; test = verdictul final, atins o singură dată. La serii de timp nu amesteca: împarte cronologic.
Analiza corelației
| Coeficient | Măsoară | Când |
|---|---|---|
| Pearson | relație liniară (−1..1) | numeric–numeric, distribuții rezonabile |
| Spearman | relație monotonă (pe ranguri) | relații neliniare dar monotone, outlieri |
| Kendall | concordanța perechilor | eșantioane mici, multe egalități |
| Chi-Square | dependența între categorii | categoric–categoric |
| Gini (importance) | cât separă un feature clasele | din arbori: feature_importances_ |
df.corr(method="pearson") # sau "spearman", "kendall"
from scipy.stats import chi2_contingency
chi2, p, _, _ = chi2_contingency(pd.crosstab(df["oras"], df["aprobat"]))
# p < 0.05 → dependență semnificativă
Problemă rezolvată: pipeline de curățare complet pentru status pacient
Date medicale cu valori lipsă, coloane pe scări diferite și categorii text — problema concentrează exact materia acestei lecții.
Schema soluției
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
train = pd.read_csv("train_data.csv")
test = pd.read_csv("test_data.csv")
y = train["status"]
X = train.drop(columns=["SampleID", "status"])
numerice = X.select_dtypes("number").columns
categorice = X.select_dtypes("object").columns
preproc = ColumnTransformer([
("num", Pipeline([("imp", SimpleImputer(strategy="median")),
("sc", StandardScaler())]), numerice),
("cat", Pipeline([("imp", SimpleImputer(strategy="most_frequent")),
("oh", OneHotEncoder(handle_unknown="ignore"))]), categorice),
])
model = Pipeline([("prep", preproc),
("clf", RandomForestClassifier(n_estimators=300, random_state=42))])
print(cross_val_score(model, X, y, cv=5, scoring="f1_macro").mean().round(3))
model.fit(X, y)
pd.DataFrame({
"SampleID": test["SampleID"],
"status": model.predict(test.drop(columns=["SampleID"]))
}).to_csv("submission.csv", index=False)
ColumnTransformer aplică rețete diferite coloanelor numerice și categorice,
totul într-un singur pipeline fără leakage.
Probleme propuse
Pentru fiecare caz, alege metoda de imputare și justifică: salariu (asimetric), temperatura orară (serie de timp), culoarea preferată (categoric), numărul de camere (discret).
Generează y = x³ + zgomot. Calculează ambii coeficienți și explică de ce Spearman e aproape 1 iar Pearson nu.
Standardizează o dată înainte de split și o dată corect (după split, fit doar pe train) și compară scorurile pe un dataset mic. De ce varianta greșită pare mai bună?
Alege o problemă tabelară „murdară" de pe platformă și construiește pipeline-ul complet ColumnTransformer ca în problema rezolvată.