Skip to content

Repository files navigation

Temă Învățare Automată – Partea 1: Închiriere biciclete și Autovit

Analiză exploratorie, preprocesare și antrenare de modele de regresie pentru două seturi de date: predicția numărului de biciclete închiriate pe oră și predicția prețului de vânzare al unui autoturism.

Cuprins

Prezentare generală

Proiectul urmărește trei etape de lucru cu fiecare set de date, cerute de enunțul temei ([InvAut-2025-2026]-Tema1.pdf):

  1. Explorarea și vizualizarea datelor (EDA), pentru a înțelege complexitatea problemei: existența unor trenduri sau cicluri temporale, corelațiile dintre atribute și ținta de prezis, precum și existența datelor lipsă.
  2. Extragerea, standardizarea și selecția atributelor, inclusiv suplimentarea valorilor lipsă, pornind de la seturi de date cu variabile de tipuri diferite (categorice, numerice) și de ordine de mărime diferite.
  3. Antrenarea și compararea mai multor algoritmi de regresie: LinearRegression, SVR, RandomForestRegressor, GradientBoostingRegressor (cu loss="squared_error" și loss="quantile") și QuantileRegressor, fiecare optimizat printr-o căutare de hiperparametri cu validare încrucișată.

Cele două seturi de date au fost alese pentru că pun probleme diferite. Închirierea de biciclete este un set de date curat, cu puține coloane, dominat de sezonalitate și cicluri temporale, potrivit pentru a arăta clar diferența dintre un model liniar și unul care poate învăța interacțiuni nelineare. Autovit este, în schimb, un set de date "murdar" în sensul clasic: 37 de coloane de tipuri diferite (numerice, categorice, text liber), cu procente mari de valori lipsă pe unele coloane (până la 40%) și cu anomalii logice între coloane (de exemplu, o mașină marcată simultan ca "manuală" și "4x4 automat"), care cer o strategie de curățare mult mai elaborată decât o simplă imputare cu media.

Seturile de date

Dataset Notebook Țintă (target) Tip problemă
Închiriere biciclete Homework_bicycle_rent.ipynb total (număr de biciclete închiriate pe oră) Regresie
Autovit Homework_cars_price_prediction.ipynb pret (preț de listare, în euro) Regresie

Structura proiectului

.
├── Homework_bicycle_rent.ipynb            # EDA, preprocesare și modelare pentru inchirierea de biciclete
├── Homework_cars_price_prediction.ipynb   # EDA, preprocesare și modelare pentru pretul masinilor
├── inchiriere-biciclete/
│   ├── train_split.csv                    # 6.878 inregistrari orare
│   ├── eval_split.csv                     # 4.008 inregistrari orare
│   └── results/                           # grafice exportate din notebook (EDA + analiza atributelor)
├── autovit/
│   ├── README.md                          # descrierea coloanelor setului Autovit
│   ├── train_cars_listings.csv            # 18.988 anunturi auto
│   ├── val_cars_listings.csv              # anunturi auto pentru evaluare
│   └── results/                           # grafice exportate din notebook (EDA + curatare + atribute)
└── [InvAut-2025-2026]-Tema1.pdf           # enuntul temei

Instalare și rulare

# activare mediu virtual (deja creat în .venv/)
source .venv/bin/activate

# instalare dependențe, dacă mediul nu este deja configurat
pip install pandas numpy matplotlib seaborn scikit-learn jupyter

# lansare notebook-uri
jupyter notebook Homework_bicycle_rent.ipynb
jupyter notebook Homework_cars_price_prediction.ipynb

Dependențe principale: pandas, numpy, matplotlib, seaborn, scikit-learn.

Modele de învățare automată

Ambele notebook-uri antrenează și compară aceiași cinci algoritmi de regresie din scikit-learn. Pentru fiecare este explicat pe scurt principiul de funcționare și motivul pentru care a fost inclus în comparație.

  • Linear Regression: modelul de bază (baseline). Presupune că ținta este o combinație liniară a atributelor. Este rapid de antrenat și ușor de interpretat, dar limitat atunci când relația reală dintre atribute și țintă nu este liniară.
  • SVR (Support Vector Regression): extinde ideea de regresie prin căutarea unei funcții care aproximează ținta în interiorul unei marje de toleranță (epsilon), penalizând doar predicțiile care depășesc marja. Cu un kernel neliniar (rbf), poate învăța relații de tip curbă fără a avea nevoie de atribute polinomiale construite manual. Hiperparametrii principali sunt kernel, C (penalizarea erorilor mari) și epsilon (lățimea marjei de toleranță).
  • Random Forest Regressor: un ansamblu de arbori de decizie antrenați pe eșantioane bootstrap ale datelor și pe subseturi aleatoare de atribute (bagging). Fiecare arbore învață independent praguri și interacțiuni nelineare, iar media predicțiilor tuturor arborilor reduce varianța și riscul de overfitting al unui singur arbore. Nu necesită standardizarea atributelor.
  • Gradient Boosting Regressor: tot un ansamblu de arbori, dar antrenați secvențial, astfel încât fiecare arbore nou este antrenat să corecteze eroarea (reziduul) lăsată de arborii anteriori. Această strategie produce, de regulă, predicții mai precise decât bagging-ul (Random Forest), cu prețul unui timp de antrenare mai mare și al unei sensibilități mai ridicate la hiperparametri (learning_rate, n_estimators, max_depth, subsample).
  • Quantile Regressor: un model liniar care, în loc să minimizeze eroarea pătratică medie (deci să estimeze media condiționată a țintei), minimizează pinball loss pentru a estima o anumită cuantilă a distribuției țintei, de exemplu mediana. Este util ca punct de comparație direct cu Linear Regression, pentru a vedea cât din diferența de performanță se datorează funcției de pierdere și cât se datorează naturii liniare a modelului.

Suplimentar, pentru ambele seturi de date, GradientBoostingRegressor este antrenat și cu loss="quantile" pentru α = 0.05, 0.50 și 0.95, ceea ce produce o bandă de predicție în jurul valorii mediane, nu doar o singură valoare punctuală. Secțiunea de regresie cu cuantile din fiecare dataset detaliază acest rezultat.

Căutarea hiperparametrilor

Fiecare model (cu excepția Linear Regression de bază) este optimizat printr-o căutare de hiperparametri cu validare încrucișată în 5 fold-uri (KFold, respectiv cross_val_score/GridSearchCV/RandomizedSearchCV cu scoring='r2'):

  • GridSearchCV (folosit pentru SVR) testează exhaustiv toate combinațiile dintr-o grilă de hiperparametri predefinită. A fost preferat pentru SVR pentru că spațiul de căutare este relativ mic (3 kerneluri × 4-5 valori de C × 3 valori de epsilon).
  • RandomizedSearchCV (folosit pentru Random Forest și Gradient Boosting) eșantionează aleator un număr fix de combinații (50) dintr-un spațiu de hiperparametri mult mai mare. A fost preferat în locul unei căutări exhaustive pentru că spațiul de combinații posibile ar face un GridSearchCV complet nefezabil ca timp de rulare.

Validarea încrucișată în 5 fold-uri asigură că scorul de performanță raportat pentru fiecare combinație de hiperparametri nu depinde de o singură împărțire aleatoare a datelor de antrenare, reducând riscul de a alege hiperparametri care se potrivesc întâmplător unui singur subset de date.

Metricile folosite pentru evaluarea finală, pe setul de test, sunt aceleași pentru ambele dataseturi:

  • MAE (Mean Absolute Error): eroarea medie absolută, exprimată în aceleași unități ca ținta; ușor de interpretat direct.
  • RMSE (Root Mean Squared Error): penalizează mai puternic erorile mari decât MAE, fiind mai sensibil la outlieri.
  • : proporția din variația țintei explicată de model (1.0 = predicție perfectă).

Set de date 1: închiriere biciclete

Date orare culese pe o perioadă de doi ani (2011–2012), cu variabile meteo și de calendar. Obiectivul este predicția coloanei total pentru fiecare oră din setul de test.

Atribute principale: data_ora, sezon, sarbatoare, zi_lucratoare, vreme, temperatura, temperatura_resimtita, umiditate, viteza_vant, ocazionali, inregistrati, total.

Coloană Descriere
data_ora Data și ora înregistrării
sezon Sezon (1 = primăvară, 2 = vară, 3 = toamnă, 4 = iarnă)
sarbatoare Indică dacă ziua respectivă este sărbătoare legală
zi_lucratoare Indică dacă ziua respectivă este zi lucrătoare
vreme Codul condițiilor meteo (însorit, noros, ploaie/ninsoare ușoară, furtună/ger)
temperatura Temperatura (°C)
temperatura_resimtita Temperatura resimțită (°C)
umiditate Umiditatea relativă (%)
viteza_vant Viteza vântului
ocazionali Număr de închirieri de la utilizatori neînregistrați
inregistrati Număr de închirieri de la utilizatori înregistrați (abonați)
total Numărul total de închirieri (ocazionali + inregistrati); ținta de prezis

Setul de antrenare (train_split.csv) conține 6.878 de înregistrări, iar setul de test (eval_split.csv) conține 4.008 înregistrări.

Motivul pentru care acest set de date se pretează bine la o analiză de tip serie de timp este că cererea de biciclete este condiționată puternic de context: ora din zi, ziua săptămânii, sezonul și vremea. Aceste variabile interacționează între ele (de exemplu, temperatura contează diferit în weekend față de zilele lucrătoare), ceea ce face din el un exemplu bun pentru a compara un model liniar simplu cu modele capabile să învețe interacțiuni nelineare.

Explorarea și vizualizarea datelor (EDA)

Fiecare tip de analiză a fost ales pentru a răspunde unei întrebări specifice despre setul de date, nu doar ca listă generică de grafice. Imaginile de mai jos sunt exportate direct din notebook și salvate în inchiriere-biciclete/results/.

Valori lipsă și date de calendar

Verificarea valorilor lipsă și a valorilor de 0 suspecte pe fiecare coloană a confirmat faptul că viteza_vant și umiditate conțin valori de 0 în proporție neobișnuit de mare (899, respectiv 22 de rânduri), fizic improbabile pentru un oraș pe parcursul unui an întreg. Practic, acestea sunt valori lipsă codificate ca zero, nu măsurători reale, tratate ulterior prin imputare (vezi secțiunea de preprocesare).

Descompunerea data_ora în an, luna, zi, ora și zi_a_saptamanii, urmată de maparea codurilor numerice (sezon, vreme, sarbatoare, zi_lucratoare, zi_a_saptamanii) în etichete text, a fost necesară pentru că, altfel, informația temporală ar fi rămas "blocată" într-un singur string, inutilizabilă direct de un model de regresie.

Distribuția și outlierii variabilei țintă

Boxplot-urile pe categorii (an, lună, sezon, zi a săptămânii, oră) pentru variabila total sunt utile pentru a compara rapid mediana și dispersia închirierilor între grupuri și pentru a observa vizual outlierii pe fiecare categorie. Mai jos este boxplot-ul pe oră, care arată deja vizual vârfurile de cerere din timpul zilei, și boxplot-ul general al lui total, care arată câteva valori extreme situate mult peste restul distribuției.

Boxplot total inchirieri pe ora Boxplot general al variabilei total

Pornind de la aceste valori extreme, a fost aplicată o detecție de outlieri cu regula 3-sigma: au fost eliminate din antrenare cele 95 de rânduri în care total se abate cu mai mult de 3 deviații standard față de medie. Regula 3-sigma a fost preferată unei metode mai complexe pentru că valorile extreme observate în boxplot sunt puține și izolate, nu o coadă grea a distribuției.

Histograma cu estimare de densitate (KDE) a lui total confirmă acest lucru: distribuția este puternic asimetrică la dreapta (multe ore cu puține închirieri, puține ore cu foarte multe), ceea ce explică de ce media și mediana din boxplot diferă vizibil.

Histograma variabilei total

Relații între atribute

Pairplot-ul pe variabilele continue a fost folosit pentru a depista rapid relații bivariate, de exemplu relația aproape liniară dintre temperatura și temperatura_resimtita, și eventuale coliniarități între atribute, importante mai târziu la selecția de atribute.

Pairplot variabile continue

Heatmap-ul de corelație (Pearson) cuantifică atât corelația fiecărui atribut cu total, cât și corelațiile puternice dintre atribute (de exemplu între temperatura și temperatura_resimtita). Graficul de tip regresie de mai jos ilustrează exact una dintre aceste corelații: relația dintre temperatura și total, în care se observă o tendință crescătoare, dar cu o dispersie mare, semn că temperatura singură nu explică toată variația cererii.

Heatmap corelatie Pearson Regplot temperatura vs total

Barplot-urile cu suma totală de închirieri pe categorie (sezon, vreme) completează boxplot-urile de mai sus: dacă boxplot-ul arată distribuția pe o singură oră, barplot-ul arată volumul agregat pe întreaga categorie, evidențiind cât de mult contribuie fiecare sezon sau tip de vreme la totalul închirierilor.

Barplot total pe sezon Barplot total pe vreme

Analiză de tip serie de timp

Acest tip de analiză a fost ales pentru că închirierile de biciclete au un caracter puternic ciclic. Graficul pe oră, segmentat pe sezon, arată clar că pe zilele lucrătoare apar două vârfuri de cerere, la orele de navetă (7–9 și 17–19), consistente pe toate cele patru sezoane, dar cu amplitudine mai mică iarna. Fără acest tip de grafic, comportamentul bimodal ar fi complet invizibil într-o simplă corelație numerică (coeficientul de corelație dintre ora și total este aproape de zero, tocmai pentru că relația nu este monotonă).

Lineplot total pe ora Lineplot total pe ora, segmentat pe sezon

La nivel săptămânal și lunar, graficele arată o cerere relativ constantă în zilele lucrătoare, ușor mai scăzută în weekend, și o sezonalitate anuală clară, cu vârf în lunile de vară și minim iarna, corelată direct cu graficul de temperatură discutat mai jos.

Lineplot total pe zi a saptamanii Lineplot total pe luna

Relația dintre temperatură și cerere

Graficul mediei închirierilor pe intervale de temperatură, cu marcarea temperaturii la care închirierile sunt maxime, arată explicit că relația dintre temperatură și cerere nu este liniară: închirierile cresc odată cu temperatura până la un punct de confort (în jur de 30–34°C în acest set de date), apoi tind să se stabilizeze sau să scadă ușor la temperaturi foarte ridicate. Această observație este folosită direct în etapa de feature engineering, prin indicatorii de temperatură optimă/rece/caldă și prin termenii polinomiali descriși mai jos.

Medie inchirieri pe intervale de temperatura

Preprocesare și feature engineering

Imputarea valorilor lipsă

Valorile de 0 de la viteza_vant și umiditate, identificate în etapa de EDA ca fiind fizic improbabile (vântul sau umiditatea nu pot fi aproape niciodată 0.00), sunt înlocuite cu media pe combinația lună/an a valorilor nenule. Alegerea unei medii lunare (nu a mediei globale) ține cont de faptul că vântul și umiditatea au un caracter sezonier: o medie globală ar introduce o eroare sistematică în lunile cu vreme atipică față de restul anului.

Eliminarea scurgerii de informație (target leakage)

Coloanele inregistrati și ocazionali sunt eliminate din matricea de atribute înainte de antrenare, pentru că suma lor este identică cu total. Dacă ar fi păstrate, orice model ar "învăța" trivial relația total = inregistrati + ocazionali, obținând un scor aproape perfect fără să învețe nimic util despre relația dintre vreme, calendar și cerere.

Encodare ciclică (sinus/cosinus) pentru ora și luna

Orele și lunile sunt variabile ciclice, dar reprezentate ca numere întregi simple ele ar părea la distanța maximă posibilă (23 față de 0). Un model liniar ar interpreta greșit această distanță. Transformarea sin(2π·x/perioadă) / cos(2π·x/perioadă) proiectează variabila pe un cerc, astfel încât valorile apropiate în timp rămân apropiate și numeric, indiferent de punctul de "tăiere" al ciclului.

One-hot encoding pentru atributele categorice

Atributele sezon, vreme, sarbatoare, zi_lucratoare și zi_a_saptamanii sunt codificate one-hot. Deși sunt reprezentate în datele brute prin coduri numerice (1, 2, 3, 4), aceste coduri nu au o relație de ordine reală (sezonul 4 nu este "de două ori mai mult" decât sezonul 2); one-hot encoding evită ca un model liniar să presupună o astfel de relație de ordine inexistentă.

Atribute de interacțiune și indicatori contextuali

Modelele liniare (Linear Regression, Quantile Regression) nu pot învăța singure interacțiuni între atribute: ele presupun că fiecare atribut contribuie independent și aditiv la predicție. De aceea, o parte din atributele derivate au fost create manual, pornind exact de la pattern-urile observate în etapa de EDA:

  • rush_hour, morning_commute, evening_commute, midday, late_night, business_hours: indicatori binari pentru intervalele orare identificate vizual ca având comportament distinct (vârfurile de navetă observate în graficele de tip serie de timp).
  • weekend_x_rush_hour, workday_commute, summer_evening, winter_morning: combinații care surprind faptul că același interval orar are un efect diferit asupra cererii în funcție de tipul zilei sau de sezon (de exemplu, orele de prânz sunt aglomerate în weekend, dar nu și în zilele lucrătoare).
  • temp_optimal, temp_cold, temp_hot, temp_squared, temp_cubed: pornind de la graficul mediei închirierilor pe intervale de temperatură, care arată o relație de tip "clopot" (creștere, apoi scădere), acești indicatori permit unui model liniar să aproximeze o curbă, nu doar o dreaptă.
  • peak_season_x_good_weather, bad_weather_x_temp, temp_x_humidity, temp_x_windspeed, temp_x_ora: produse între variabile a căror influență comună asupra cererii este mai mare decât suma influențelor separate (de exemplu, o temperatură plăcută contează mult mai mult atunci când vremea este și senină).

Standardizare și selecție de atribute

  • StandardScaler este aplicat înainte de Linear Regression și SVR, deoarece aceste modele sunt sensibile la scala atributelor: fără standardizare, un atribut cu valori mari (ex. umiditate, 0–100) ar domina nejustificat coeficienții față de un atribut cu valori mici (ex. viteza_vant).
  • SelectKBest (f_regression) combinat cu validare încrucișată a fost folosit pentru a alege automat numărul optim de atribute din cele peste 50 generate, testând mai multe valori de k și păstrându-l pe cel cu cel mai bun scor R² mediu pe cele 5 fold-uri. Cel mai bun rezultat a fost obținut cu 57 de atribute. Graficul de mai jos arată corelația fiecărui atribut (inclusiv cele derivate prin feature engineering) cu ținta total, folosit ca punct de plecare pentru a înțelege ce atribute merită păstrate.

Corelatia atributelor cu variabila total

Rezultate

Model MAE RMSE
Linear Regression 61.66 85.57 0.78
Linear Regression + selecție de atribute prin CV (57 atribute) 61.66 85.57 0.78
SVR (kernel RBF, C=100) 41.15 68.60 0.86
Random Forest Regressor 30.74 52.85 0.92
Gradient Boosting Regressor 29.98 48.87 0.93
Gradient Boosting (loss quantile, mediana) 31.30 n/a 0.92
Quantile Regressor 60.50 88.41 0.77

Gradient Boosting Regressor obține cel mai bun rezultat dintre modelele testate, ceea ce confirmă observația din etapa de EDA: relația dintre variabilele meteo/de calendar și cererea de biciclete este puternic nelineară (vârfuri de navetă, curbă de confort termic), iar un model care poate învăța automat astfel de praguri și interacțiuni are un avantaj clar față de un model liniar. Linear Regression și Quantile Regressor rămân în urmă cu aproximativ 15 puncte procentuale de R² față de Gradient Boosting, chiar și după selecția de atribute și adăugarea manuală de termeni de interacțiune.

Regresie cu cuantile. Pentru varianta loss="quantile" (α = 0.05, 0.50, 0.95), acoperirea observată pe setul de test pentru intervalul 0.05–0.95 a fost de 80.14%, sub ținta teoretică de 90%. Diferența arată că intervalele generate sunt puțin prea înguste, adică modelul este ceva mai încrezător decât ar trebui în limitele superioară și inferioară estimate. Este un aspect discutat explicit în enunțul temei ca fiind o capcană frecventă a acestei metode.

Set de date 2: Autovit (preț autoturisme)

Anunțuri de vânzare mașini extrase de pe autovit.ro: 18.988 de rânduri în setul de antrenare, cu 37 de coloane. Obiectivul este predicția coloanei pret (în euro). Spre deosebire de setul de biciclete, acesta este un set de date eterogen: coloane numerice (Km, Putere), categorice (Marca, Combustibil, Tip_Caroserie), text liber sub formă de listă (Siguranta, listată ca ['ABS', 'ESP', ...]) și coloane cu procente mari de valori lipsă (Tara_de_origine lipsește la aproape 40% din rânduri).

Coloane principale: Descrierea completă a fiecărei coloane este în autovit/README.md.

Explorarea și vizualizarea datelor (EDA)

Imaginile de mai jos sunt exportate direct din notebook și salvate în autovit/results/.

Distribuția prețului și a variabilelor numerice

Graficul cu patru panouri de mai jos arată distribuția lui pret sub patru forme diferite: histogramă, boxplot, violin plot și histogramă pe scală logaritmică (log1p). Această combinație a fost aleasă pentru că prețul unei mașini second-hand este, prin natura pieței, puternic asimetric la dreapta (multe mașini ieftine, puține foarte scumpe), lucru vizibil clar în histograma simplă, dar transformarea logaritmică arată mai clar forma distribuției pentru majoritatea mașinilor, unde se concentrează cea mai multă informație utilă pentru model.

Distributia pretului

Histogramele individuale ale variabilelor Km și Putere completează tabloul: Km are un vârf clar aproape de 0 (mașini foarte noi sau chiar 0 km), apoi o distribuție largă până spre 300.000 km, iar Putere are mai multe vârfuri distincte, corespunzând motorizărilor standard (de exemplu, motoare de 90, 150 sau 190 CP foarte comune pe piață).

Histograma kilometraj Histograma putere

Distribuții categorice

Top 10 valori pentru Combustibil, Cutie_de_viteze, Transmisie și Tip_Caroserie arată rapid ce categorii domină piața (de exemplu, Diesel și Benzină rămân cele mai comune tipuri de combustibil, iar cutia automată depășește deja cutia manuală ca frecvență în acest set de date), informație relevantă pentru a decide care categorii merită tratate individual și care pot fi grupate la "altele".

Distributii categorice

Prețul în funcție de categorie (boxplot-uri pentru combustibil, cutie de viteze, transmisie, caroserie) a fost analizat pentru a vedea dacă apartenența la o categorie mută semnificativ mediana prețului, nu doar frecvența ei. Diferențele de mediană observate aici au motivat direct crearea unor atribute binare precum Is_Automatic, Is_AWD sau Is_SUV în etapa de feature engineering.

Pret pe categorii

Pret pe tip de combustibil Pret pe tip de caroserie

Preț, vechime și kilometraj

Evoluția prețului mediu și median în funcție de anul fabricației, respectiv de vechimea mașinii (Car_Age), a fost analizată pentru a confirma și cuantifica trendul de depreciere: prețul scade constant odată cu vechimea, dar nu liniar, ci mai abrupt în primii ani și mai lent ulterior. Această observație a motivat direct atributul Car_Age (vezi feature engineering).

Pret pe an de fabricatie Pret pe varsta masinii

Diagramele de dispersie preț-kilometraj și preț-putere arată cele două relații cele mai puternice cu prețul: kilometrajul mare este asociat cu prețuri mai mici (mai ales pentru mașinile recente, colorate mai deschis în graficul din stânga), iar puterea motorului este cel mai bun predictor individual al prețului (Putere are corelația Pearson cea mai mare cu pret, 0.74, dintre toate atributele numerice originale).

Pret vs kilometraj Pret vs putere, pe tip de combustibil

Corelații între atribute

Heatmap-ul de corelație (Pearson) pe toate atributele numerice, inclusiv cele derivate prin feature engineering, oferă o imagine de ansamblu asupra relațiilor din date și a coliniarităților dintre atribute (de exemplu, între Consum_Urban și Consum_Extraurban).

Heatmap corelatie Pearson, Autovit

Curățarea și corectarea anomaliilor

Setul de date Autovit conține, pe lângă valori lipsă, și anomalii logice: combinații de valori care sunt individual valide, dar contradictorii împreună. Aceste anomalii au fost identificate explicit înainte de imputare, pentru că altfel s-ar fi propagat ca "adevăr" în etapele următoare.

Conflict între Transmisie și Cutie_de_viteze. 1.062 de rânduri aveau, de exemplu, Transmisie = "4x4 (automat)" combinat cu Cutie_de_viteze = "Manuala", o combinație imposibilă din punct de vedere fizic. Coloana Cutie_de_viteze a fost tratată drept sursa de adevăr (are doar 1 valoare lipsă, deci este mult mai completă și mai fiabilă), iar Transmisie a fost corectată pentru a fi consistentă cu ea.

Mașini pe combustibil clasic marcate și ca electrice. 1.342 de rânduri aveau Combustibil egal cu "Diesel" sau "Benzina", dar aveau totuși completată coloana Vehicule_electrice (care descrie un sistem electric prezent pe mașină). Aceasta nu este o eroare, ci o etichetare incompletă: mașina este de fapt un hibrid, dar anunțul o listase doar după combustibilul principal. Aceste rânduri au fost recategorisite ca "Hibrid diesel" sau "Hibrid benzina", păstrând astfel informația despre sistemul electric în loc să o ignore.

Anomalie combustibil clasic si dispozitiv electric

O verificare inversă (mașini electrice cu caracteristici de motor cu ardere internă) nu a găsit niciun rând suspect, deci nu a fost necesară nicio corecție suplimentară în acea direcție.

Numărul de uși mai mare de 5. Doar 10 rânduri (din aproape 19.000) aveau Numar_de_portiere peste 5, fizic imposibil pentru un autoturism obișnuit. Graficul de mai jos arată clar cât de izolate sunt aceste valori față de restul distribuției, concentrată aproape în întregime pe 4 și 5 uși.

Distributia numarului de usi

Fiind doar 10 rânduri dintr-un set de aproape 19.000, ștergerea lor ar fi fost la fel de justificată, dar au fost plafonate (capate) la 5 în loc de eliminate, pentru a nu pierde restul informației valide din acele rânduri (marcă, preț, kilometraj etc.), informație care nu are nicio legătură cu eroarea de la numărul de uși.

Garanția oferită de dealer. Coloana Garantie_dealer_(inclusa_in_pret) avea o medie de 7.21 luni, o mediană de 7 luni și o valoare maximă de 120 de luni, cu o deviație standard mare (9.36). Boxplot-ul de mai jos arată un număr mare de valori extreme peste marginea superioară a distribuției normale.

Outlieri garantie dealer

Primul pas a fost o detecție de outlieri prin z-score (prag |z| > 3), care elimină valorile extreme izolate. După acest pas, histograma rămasă (mai jos) arată un tipar clar: vârfuri ascuțite exact la multipli de 6 luni (6, 12, 24, 36...), pentru că garanțiile comerciale sunt vândute ca pachete standard, nu ca durate continue alese liber. Pornind de la acest tipar, valorile care nu sunt simultan divizibile cu 2 și cu 3 (adică nu sunt un multiplu curat de 6) au fost plafonate la cea mai apropiată valoare validă, pentru că cel mai probabil reprezintă zgomot de extragere a datelor (de exemplu, "11 luni" în loc de "12 luni") în jurul unui pachet comercial real, nu o garanție negociată individual.

Distributia garantiei dupa curatare

Imputarea valorilor lipsă

Spre deosebire de setul de biciclete, unde doar două coloane aveau valori lipsă, la Autovit majoritatea coloanelor au valori lipsă, unele în proporție foarte mare. Strategia generală a fost imputarea ierarhică: fiecare valoare lipsă este completată cu cea mai frecventă valoare (moda) sau cu media calculată pe cel mai specific grup posibil (de exemplu, Marca + Model + Anul_fabricatiei), iar dacă acel grup nu are nicio altă înregistrare validă, se trece la un grup mai larg (de exemplu doar Marca), până la un fallback global pe tot setul de date. Motivul pentru care a fost preferată această abordare unei simple imputări cu media globală este că majoritatea coloanelor lipsă descriu proprietăți specifice mărcii și modelului (capacitate cilindrică, normă de poluare, țară de origine): toate exemplarele de "Dacia Logan 2018" au, practic, aceeași capacitate cilindrică, deci completarea cu valoarea tipică a acelui model este mult mai precisă decât completarea cu media pe întreg setul de date, care amestecă mașini foarte diferite.

Câteva exemple concrete de imputare condiționată, cu numărul de valori lipsă tratate:

  • Versiune (4.042 valori lipsă): completată cu "Standard", pornind de la ideea că absența unei versiuni specificate în anunț înseamnă cel mai probabil versiunea de bază.
  • Numar_de_portiere (229 valori lipsă) și Numar_locuri (4.194 valori lipsă): completate cu moda calculată pe Tip_Caroserie (tipul caroseriei este cel mai puternic predictor: Cabrio și Coupe au aproape mereu 2 uși, Sedan are 4, restul au 5), apoi pe Marca dacă tipul de caroserie lipsește și el.
  • Transmisie (2.109 valori lipsă): completată ierarhic, întâi pe baza modei calculate pe Model, apoi pe combinația Marca + Tip_Caroserie + Cutie_de_viteze. Datele confirmă o asociere puternică: cutia automată este aproape mereu însoțită de tracțiune "4x4 (automat)", iar cutia manuală de tracțiune "Fata".
  • Capacitate_cilindrica (507 valori lipsă) și Putere (5 valori lipsă): pentru mașinile electrice, capacitatea cilindrică nu are sens fizic și este tratată separat; pentru restul, valorile lipsă sunt completate printr-o căutare pe combinația Marca + Model + Combustibil + Putere (pentru capacitate), respectiv Model + Capacitate_cilindrica (pentru putere), pentru că aceste caracteristici tehnice sunt aproape constante în interiorul aceluiași model și motorizări.
  • Tara_de_origine (7.527 valori lipsă, aproape 40% din rânduri): completată cu moda pe Marca + Model, apoi doar pe Marca. Deși procentul de valori lipsă este foarte mare, țara de origine este o proprietate stabilă a modelului (aproape toate exemplarele "Dacia Logan" provin din același grup de țări), deci imputarea pe grup rămâne fiabilă chiar și cu atât de multe valori lipsă.
  • Norma_de_poluare (5.679 valori lipsă): completată pe baza combinației Marca + Model + Anul_fabricatiei, apoi doar pe Anul_fabricatiei. Datele arată o relație aproape perfect monotonă între an și normă de poluare (2005–2008 → Euro 4, 2009–2014 → Euro 5, 2015 și ulterior → Euro 6), deci anul de fabricație singur este deja un predictor foarte bun.
  • Coloanele de dotări Audio_si_tehnologie, Confort_si_echipamente_optionale, Electronice_si_sisteme_de_asistenta, Siguranta (543, 122, 304, respectiv 492 valori lipsă): înainte de imputare, s-a verificat dacă lungimea listei de dotări corelează cu prețul, grupând mașinile pe quartile de preț (Low, Medium, High, Very High). Rezultatul a confirmat că da (de exemplu, lungimea medie a listei de dotări de siguranță crește de la 172 caractere în categoria "Low" la valori mult mai mari în categoriile superioare), ceea ce a validat decizia de a păstra aceste coloane ca atribute utile, nu de a le elimina. Valorile lipsă au fost completate ierarhic pe Marca + Model + Versiune, apoi pe grupuri din ce în ce mai largi.
  • Consum_Urban, Consum_Extraurban, Emisii_CO2: completate ierarhic pe Marca + Model + Anul_fabricatiei + Combustibil, apoi pe combinații din ce în ce mai largi, din același motiv: consumul și emisiile sunt, în esență, o caracteristică tehnică a motorizării, nu ceva specific fiecărui anunț individual.

După toate aceste etape, rândurile care mai aveau valori lipsă nerezolvate (de exemplu combinații foarte rare, fără niciun exemplu similar în setul de date) au fost eliminate: setul de antrenare a scăzut de la 18.988 la 18.491 de rânduri (aproximativ 2.6%), iar setul de test a rămas cu 4.618 rânduri.

Feature engineering

Corelația fiecărui atribut numeric (original și derivat) cu pret a fost calculată explicit și folosită ca argument pentru păstrarea sau prioritizarea atributelor derivate:

Corelatia atributelor cu variabila pret

  • Car_Age = 2024 - Anul_fabricatiei: mașinile se apreciază/depreciază în funcție de vechime, nu de anul absolut al fabricației; graficul preț-vechime din EDA arată deja o relație monotonă mult mai clară decât preț-an. Car_Age are o corelație de -0.48 cu prețul, la fel de puternică precum Anul_fabricatiei însuși, dar mai ușor de interpretat.

Histograma varsta masinii

  • Power_Per_Liter = Putere / (Capacitate_cilindrica / 1000): normalizează puterea motorului la volumul cilindrilor, surprinzând tehnologia motorului (turbo, asistență electrică), nu doar mărimea lui brută. Corelație de 0.53 cu prețul.
  • Km_Per_Year = Km / Car_Age: distinge o mașină veche, dar puțin folosită, de una la fel de veche, dar intens folosită; Km singur amestecă cele două informații (cât de veche este mașina și cât de mult a fost condusă).
  • Avg_Consumption, Efficiency_Rating = Putere / Avg_Consumption, Power_Efficiency = Putere / (Avg_Consumption + 1): raportul putere/consum s-a dovedit al doilea și al treilea cel mai bun predictor dintre toate atributele (corelații de 0.63, respectiv 0.60, imediat după Putere, 0.74), confirmând că nu puterea sau consumul separat contează cel mai mult, ci eficiența motorului, adică tehnologia din spatele lui.
  • Is_Luxury: indicator binar pentru cele 20 de mărci cu prețul mediu cel mai mare (Rolls-Royce, Lamborghini, Ferrari, McLaren, Aston Martin, Bentley...), pentru a surprinde prima de brand, o informație pe care nicio coloană tehnică nu o poate exprima direct.
  • Is_Electric_Hybrid, Is_Automatic, Is_AWD, Is_SUV, Low_Emission: indicatori binari pentru segmente asociate constant cu un preț mai mare pe piața second-hand (motorizări electrificate, cutie automată, tracțiune integrală, caroserie SUV, normă de poluare Euro 6).
  • Premium_Features_Count (suma indicatorilor Is_Luxury, Is_Automatic, Is_AWD, Low_Emission, Is_First_Owner, Is_Accident_Free): un scor agregat de "cât de premium este anunțul", motivat de faptul că fiecare componentă corelează individual cu prețul. Scorul agregat ajunge la o corelație de 0.52, a doua cea mai mare dintre toate atributele derivate.
  • Features_Score (lungimea medie a celor patru liste de dotări): în etapa de imputare s-a observat deja că lungimea listelor de dotări crește odată cu prețul; acest atribut transformă acea observație într-un număr utilizabil direct de model (corelație 0.39), în loc să lase informația "îngropată" în text nestructurat.
  • Depreciation_Per_Year = Km_Per_Year × Car_Age și Age_Mileage_Score = Car_Age × log1p(Km): două moduri diferite de a combina vechimea și kilometrajul într-un singur semnal de "uzură"; transformarea logaritmică din a doua formulă reduce influența valorilor extreme de kilometraj, consecventă cu asimetria observată în histograma lui Km.
  • High_Mileage, Is_Recent, High_Power_Density, Doors_Seats_Ratio, Good_electric_car: indicatori de prag sau rapoarte derivate direct din tiparele observate în histograme și în tabelul de corelații (de exemplu, Is_Recent, mașină de cel mult 3 ani, are o corelație de 0.47 cu prețul, aproape la fel de puternică precum anul de fabricație brut).

De reținut despre modelare: înainte de antrenare, toate coloanele de tip text/categorie (Marca, Model, Combustibil, Tip_Caroserie, Transmisie, Cutie_de_viteze etc.) sunt eliminate din matricea de atribute, iar modelele folosesc doar coloanele numerice originale și cele derivate prin feature engineering (inclusiv indicatorii binari de mai sus). Spre deosebire de setul de biciclete, unde variabilele categorice sunt păstrate prin one-hot encoding, aici informația categorică ajunge la model doar indirect, prin intermediul atributelor binare/agregate construite manual (Is_Luxury, Is_Automatic, Is_AWD, Is_SUV, Low_Emission etc.). Aceasta explică de ce atât de mult din feature engineering-ul de mai sus s-a concentrat pe transformarea informației categorice în indicatori numerici.

  • SelectKBest (f_regression) combinat cu validare încrucișată a fost folosit și aici pentru a alege automat numărul optim de atribute; cel mai bun rezultat a fost obținut cu 35 de atribute numerice.

Rezultate

Model MAE (EUR) RMSE (EUR)
Linear Regression 7.712,37 13.461,23 0.76
Linear Regression + selecție de atribute prin CV (35 atribute) 7.712,37 13.461,23 0.76
SVR (kernel liniar, C=100) 6.738,74 15.283,73 0.69
Random Forest Regressor 3.635,31 8.213,98 0.91
Gradient Boosting Regressor 3.695,67 8.278,81 0.91
Gradient Boosting (loss quantile, mediana) 3.580,69 n/a 0.91
Quantile Regressor 6.698,17 14.896,05 0.71

Ca și la biciclete, modelele bazate pe arbori de decizie (Random Forest, Gradient Boosting) obțin rezultate net superioare modelelor liniare, ceea ce arată că prețul unei mașini depinde de interacțiuni nelineare între atribute precum vechimea, kilometrajul, puterea și dotările, nu de o combinație liniară simplă a acestora. Random Forest și Gradient Boosting ajung la performanțe aproape identice aici (R² = 0.91 pentru amândouă), spre deosebire de setul de biciclete, unde Gradient Boosting avea un avantaj mai clar; o explicație posibilă este că Putere singură are deja o corelație foarte puternică (0.74) cu prețul, ceea ce reduce nevoia de a învăța interacțiuni foarte complexe pentru a obține un scor bun.

Regresie cu cuantile. Pentru varianta loss="quantile" (α = 0.05, 0.50, 0.95), acoperirea observată pe setul de test pentru intervalul 0.05–0.95 a fost de 84.02%, apropiată de ținta teoretică de 90% și sensibil mai bună decât cea obținută pe setul de biciclete (80.14%).

Concluzii generale

  • Cele două seturi de date au cerut strategii de preprocesare foarte diferite: la biciclete, câteva coloane cu valori de 0 improbabile și encodare ciclică; la Autovit, imputare ierarhică pe zeci de coloane și corectarea unor anomalii logice explicite (conflicte între transmisie și cutia de viteze, mașini hibride etichetate greșit, garanții și numere de uși aberante).
  • Plafonarea (capping-ul) a fost preferată ștergerii de fiecare dată când numărul de rânduri afectate era mic și restul informației din acele rânduri era valid: numărul de uși peste 5 (10 rânduri) și garanțiile care nu erau multipli de 6 luni sunt corectate la cea mai apropiată valoare plauzibilă, nu eliminate.
  • În ambele seturi de date, Gradient Boosting Regressor este printre cele mai bune modele (R² = 0.93 pentru biciclete, R² = 0.91 pentru Autovit), confirmând că relațiile din ambele probleme sunt puternic nelineare și beneficiază de modele capabile să învețe automat praguri și interacțiuni, nu doar combinații liniare de atribute.
  • Regresia cu cuantile oferă, în ambele cazuri, o alternativă utilă atunci când este nevoie de un interval de încredere pentru predicție, nu doar de o valoare punctuală; acoperirea intervalului de 90% a ieșit ușor subestimată pentru biciclete (80.14%) și aproape de țintă pentru Autovit (84.02%).
  • Diferența majoră de tratare a categoriilor (one-hot encoding la biciclete, indicatori binari derivați manual la Autovit) arată două strategii valide pentru a aduce informație categorică la un model de regresie, alese în funcție de numărul de categorii posibile: bicicletele au câteva categorii cu puține valori distincte, ușor de codificat one-hot, în timp ce Autovit are coloane cu sute de mărci/modele distincte, unde one-hot encoding direct ar fi generat mii de coloane rare.

Releases

Packages

Contributors

Languages