Used Car Price Prediction — Regression Comparison

By Eldar · Published July 25, 2026

Compare Linear Regression, Random Forest, and Gradient Boosting models to predict used car prices from mileage, age, brand, fuel type, transmission, and engine size.

  • regression
  • price-prediction
  • model-comparison
  • scikit-learn
  • eda
11 cells1 experiment3 views0 forks

Inside this notebook

# 🚗 Used Car Price Prediction — Regression Model Comparison ## Goal Train a model to predict a used car's resale price from its **mileage, age, brand, fuel type, transmission, and engine size**, then compare several regression approaches. ## Dataset [Used Car Price Prediction Dataset](https://www.kaggle.com/datasets/taeefnajib/used-car-price-prediction-dataset) — 4,009 car listings with 13 attributes (Brand, Model, Model Year, Mileage, Fuel Type, Engine, Transmission, Price, and more). ## Approach (Branched) | Step | What happens | |------|-------------| | 1 | Load & inspect the data | | 2 | Feature engineering (Age, Engine Size, encode categoricals) | | 3 | Train/test split + scaling | | 4 | **Fork →** 3 branches (Linear Regression, Random Forest, Gradient Boosting) train in parallel | | 5 | **Converge →** Compare metrics, pick the winner | | 6 | Predict prices for 4 example cars + SHAP explanation | | 7 | Summary of findings |

# ⚙️ Install dependencies
import sys
!pip install -q kagglehub pandas numpy matplotlib seaborn scikit-learn shap
# 📦 Load Dataset
import kagglehub
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Download dataset from Kaggle
path = kagglehub.dataset_download("taeefnajib/used-car-price-prediction-dataset")
print("Dataset downloaded to:", path)

# Load CSV
df = pd.read_csv(f"{path}/used_cars.csv")
print(f"\nShape: {df.shape}")
print(f"\nColumns:\n{list(df.columns)}")
print(f"\nDtypes:\n{df.dtypes.value_counts()}")
print(f"\nFirst 5 rows:")
df.head()
Dataset downloaded to: /root/.cache/kagglehub/datasets/taeefnajib/used-car-price-prediction-dataset/versions/1

Shape: (4009, 12)

Columns:
['brand', 'model', 'model_year', 'milage', 'fuel_type', 'engine', 'transmission', 'ext_col', 'int_col', 'accident', 'clean_title', 'price']

Dtypes:
object    11
int64      1
Name: count, dtype: int64

First 5 rows:
# 🔍 Inspect missing values & basic stats

print("=== Missing Values ===")
print(df.isnull().sum())

print("\n=== Summary Statistics (numeric) ===")
print(df.describe().round(2))

print("\n=== Unique values per categorical column ===")
for col in ['brand', 'fuel_type', 'transmission']:
    print(f"{col}: {df[col].nunique()} unique | top 10: {df[col].value_counts().head(10).to_dict()}")

print("\n=== Price distribution ===")
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
sns.histplot(df['price'], bins=50, kde=True, ax=axes[0])
axes[0].set_title('Price Distribution')
sns.boxplot(x=df['price'], ax=axes[1])
axes[1].set_title('Price Boxplot')
…
=== Missing Values ===
brand             0
model             0
model_year        0
milage            0
fuel_type       170
engine            0
transmission      0
ext_col           0
int_col           0
accident        113
clean_title     596
price             0
dtype: int64

=== Summary Statistics (numeric) ===
       model_year
count     4009.00
mean      2015.52
std          6.10
min       1974.00
25%       2012.00
50%       2017.00
75%       2020.00
max       2024.00

=== Unique values per c…
# 🔧 Feature Engineering

# 1. Clean price: remove $ and commas → numeric
df['price'] = df['price'].str.replace('$', '', regex=False).str.replace(',', '', regex=False)
df['price'] = pd.to_numeric(df['price'], errors='coerce')

# 2. Clean mileage: remove commas and ' mi.' suffix → numeric
df['milage'] = df['milage'].str.replace(',', '', regex=False).str.replace(' mi.', '', regex=False)
df['milage'] = pd.to_numeric(df['milage'], errors='coerce')

# 3. Model year → numeric
df['model_year'] = pd.to_numeric(df['model_year'], errors='coerce')

# 4. Derive Age from Model Year
current_year = 2026
df['age'] = current_year - df['model_year']

# 5. Parse Engine string → displacement in litres
…
=== Missing values ===
price                    0
milage                   0
age                      0
engine_size            217
fuel_type              170
transmission_simple      0
dtype: int64

Rows before dropna(price): 4009
Rows after dropna(price): 4009

Final shape: (4009, 15)

=== Sample ===
      brand  age  milage      fuel_type  engine_size transmission_simple  \
0      Ford   13   51000  E85 Flex Fuel          3.7           Automatic   
1   Hyundai    5   34742       Gasoline…
# 🎯 Train/Test Split & Preprocessing — Fork Point

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline

# Define feature sets
numeric_features = ['age', 'milage', 'engine_size']
categorical_features = ['brand', 'fuel_type', 'transmission_simple']

X = df[numeric_features + categorical_features].copy()
y = df['price'].copy()

# Train/test split (80/20)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
…
Train size: 3207 | Test size: 802
Processed feature count: 67
Feature names (first 10): ['age', 'milage', 'engine_size', 'brand_Alfa', 'brand_Aston', 'brand_Audi', 'brand_BMW', 'brand_Bentley', 'brand_Buick', 'brand_Cadillac'] ...
Training set shape: (3207, 67)
# 💾 Save preprocessed data for experiment branches
import joblib
import json

# Save all preprocessing artifacts
joblib.dump(preprocessor, '/home/user/preprocessor.pkl')
np.save('/home/user/X_train_processed.npy', X_train_processed)
np.save('/home/user/X_test_processed.npy', X_test_processed)
np.save('/home/user/y_train.npy', y_train.values)
np.save('/home/user/y_test.npy', y_test.values)

with open('/home/user/feature_names.json', 'w') as f:
    json.dump(feature_names, f)

# Also save original data for branches that want to do their own preprocessing
X_train.to_parquet('/home/user/X_train.parquet')
X_test.to_parquet('/home/user/X_test.parquet')
y_train.to_frame('price').to_parquet('/home/user/y_train.parquet')
…
Preprocessed data saved to /home/user/
-rw-r--r-- 1 root root  17K Jul 25 21:26 /home/user/X_test.parquet
-rw-r--r-- 1 root root 420K Jul 25 21:26 /home/user/X_test_processed.npy
-rw-r--r-- 1 root root  48K Jul 25 21:26 /home/user/X_train.parquet
-rw-r--r-- 1 root root 1.7M Jul 25 21:26 /home/user/X_train_processed.npy
-rw-r--r-- 1 root root 1.2K Jul 25 21:26 /home/user/feature_names.json
-rw-r--r-- 1 root root  110 Jul 25 21:15 /home/user/model_gb_metrics.json
-rw-r--r-- 1 root root 1.7K…
Used Car Price Prediction — Regression Comparison | Clusy