Fundamente de Data Science

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
mediadistribuție simetrică, fără outlieri
medianadistribuț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

CoeficientMăsoarăCând
Pearsonrelație liniară (−1..1)numeric–numeric, distribuții rezonabile
Spearmanrelație monotonă (pe ranguri)relații neliniare dar monotone, outlieri
Kendallconcordanța perechiloreșantioane mici, multe egalități
Chi-Squaredependența între categoriicategoric–categoric
Gini (importance)cât separă un feature claseledin 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

Predicția statusului pacientului (Simulare OJIA) Mediu Rezolvată — 80p

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

1. Alege imputarea Exercițiu

Pentru fiecare caz, alege metoda de imputare și justifică: salariu (asimetric), temperatura orară (serie de timp), culoarea preferată (categoric), numărul de camere (discret).

2. Pearson vs Spearman Exercițiu

Generează y = x³ + zgomot. Calculează ambii coeficienți și explică de ce Spearman e aproape 1 iar Pearson nu.

3. Leakage demonstrat Exercițiu

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ă?

4. Curățare la concurs Platformă

Alege o problemă tabelară „murdară" de pe platformă și construiește pipeline-ul complet ColumnTransformer ca în problema rezolvată.