PRACTICAL 8: CLASSIFICATION IMPROVEMENT (BAGGING AND BOOSTING)

Dataset: electric_vehicles.csv (target = drivetrain)

STEPS:
1. Install the libraries:  pip install pandas numpy seaborn matplotlib scikit-learn
2. Load electric_vehicles.csv and drop the rows where drivetrain is missing.
3. Convert the numeric columns to numbers and fill their missing values with the median.
4. Encode the brand column (one-hot) and the target (label encoding).
5. Draw the correlation heatmap and scale the features (StandardScaler).
6. Split the data 80% / 20% (stratified) and create the models: Bagging with Decision Tree, Bagging with Naive Bayes, Logistic Regression and AdaBoost.
7. Train the models, predict and calculate accuracy, precision, sensitivity, standard error and AUC.
8. Calculate the 5-fold cross-validation accuracy and print the summary table and confusion matrices.

CODE:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split, cross_val_score, KFold
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import LogisticRegression
from sklearn.decomposition import PCA
from sklearn.metrics import accuracy_score, confusion_matrix, precision_score, recall_score, roc_auc_score
from sklearn.preprocessing import StandardScaler, MinMaxScaler, LabelEncoder
from sklearn.ensemble import BaggingClassifier, AdaBoostClassifier
def main():
    df = pd.read_csv('C:/Users/Admin/Downloads/electric_vehicles.csv')
    target_col = 'drivetrain'
    df = df.dropna(subset=[target_col]).copy()
    numerical_features = [
        'top_speed_kmh',
        'battery_capacity_kWh',
        'torque_nm',
        'efficiency',
        'range_km',
        'acceleration_0_100_s',
        'fast_charging_power_kw_dc',
        'seats',
        'length_mm',
        'width_mm',
        'height_mm'
    ]
    feature_cols = []
    for col in numerical_features:
        if col in df.columns:
            df[col] = pd.to_numeric(df[col], errors='coerce')
            df[col] = df[col].fillna(df[col].median())
            feature_cols.append(col)
    if 'brand' in df.columns:
        df = pd.get_dummies(df, columns=['brand'], drop_first=True)
        brand_cols = [c for c in df.columns if c.startswith('brand_')]
        feature_cols.extend(brand_cols)
    label_enc = LabelEncoder()
    y = label_enc.fit_transform(df[target_col].astype(str))
    X = df[feature_cols].select_dtypes(include=[np.number])
    plt.figure(figsize=(12, 8))
    sns.heatmap(X.corr(numeric_only=True), annot=False, cmap='coolwarm')
    plt.title("EV Features Correlation Heatmap")
    plt.tight_layout()
    plt.show()
    standard_scaler = StandardScaler()
    minmax_scaler = MinMaxScaler()
    X_std = standard_scaler.fit_transform(X)
    X_mm = minmax_scaler.fit_transform(X)
    X_train_std, X_test_std, y_train, y_test = train_test_split(
        X_std, y, test_size=0.2, random_state=69, stratify=y
    )
    def evaluate_model(y_true, y_pred, y_prob):
        acc = accuracy_score(y_true, y_pred)
        cm = confusion_matrix(y_true, y_pred)
        prec = precision_score(y_true, y_pred, average='weighted', zero_division=0)
        sens = recall_score(y_true, y_pred, average='weighted', zero_division=0)
        se = np.sqrt((acc * (1 - acc)) / len(y_true))
        try:
            n_classes = len(np.unique(y_true))
            if n_classes == 2:
                model_auc = roc_auc_score(y_true, y_prob[:, 1])
            else:
                model_auc = roc_auc_score(y_true, y_prob, multi_class='ovr', average='weighted')
        except Exception:
            model_auc = np.nan
        return {
            'Accuracy': acc,
            'Precision': prec,
            'Sensitivity': sens,
            'Std Error': se,
            'AUC': model_auc,
            'Confusion Matrix': cm
        }
    dt_bag = BaggingClassifier(
        estimator=DecisionTreeClassifier(random_state=69),
        n_estimators=50,
        random_state=69
    )
    nb_bag = BaggingClassifier(
        estimator=GaussianNB(),
        n_estimators=50,
        random_state=69
    )
    log_reg = LogisticRegression(max_iter=1000)
    dt_ada = AdaBoostClassifier(
        estimator=DecisionTreeClassifier(max_depth=1, random_state=69),
        n_estimators=50,
        random_state=69
    )
    dt_bag.fit(X_train_std, y_train)
    nb_bag.fit(X_train_std, y_train)
    log_reg.fit(X_train_std, y_train)
    dt_ada.fit(X_train_std, y_train)
    pred_dt_bag = dt_bag.predict(X_test_std)
    pred_nb_bag = nb_bag.predict(X_test_std)
    pred_log_reg = log_reg.predict(X_test_std)
    pred_dt_ada = dt_ada.predict(X_test_std)
    results = {
        'Bagging DT': evaluate_model(y_test, pred_dt_bag, dt_bag.predict_proba(X_test_std)),
        'Bagging NB': evaluate_model(y_test, pred_nb_bag, nb_bag.predict_proba(X_test_std)),
        'Logistic Regression': evaluate_model(y_test, pred_log_reg, log_reg.predict_proba(X_test_std)),
        'AdaBoost DT': evaluate_model(y_test, pred_dt_ada, dt_ada.predict_proba(X_test_std))
    }
    cv = KFold(n_splits=5, shuffle=True, random_state=69)
    dt_cv_std = np.mean(cross_val_score(dt_bag, X_std, y, cv=cv, scoring='accuracy'))
    summary_df = pd.DataFrame(results).T.drop(columns=['Confusion Matrix'])
    print("\n" + "="*65)
    print("      EV DATASET MODEL EVALUATION SUMMARY (STANDARDIZED)")
    print("="*65)
    print(summary_df.round(4).to_string())
    print("-" * 65)
    print(f"Bagging DT 5-Fold Cross-Validation Accuracy: {dt_cv_std:.4f}")
    print("="*65 + "\n")
    print("--- Detailed Confusion Matrices ---")
    for name, res in results.items():
        print(f"\n{name} Confusion Matrix:")
        print(pd.DataFrame(res['Confusion Matrix'], index=label_enc.classes_, columns=label_enc.classes_))
if __name__ == '__main__':
    main()

EXPLANATION:
- The data is cleaned first (median for missing numbers, one-hot for brand, label encoding for drivetrain) and then standardized.
- Bagging trains 50 copies of a model (decision tree / naive bayes) on random samples and combines their votes to reduce variance.
- AdaBoost trains small trees one after another, and each new tree gives more weight to the records the earlier trees got wrong.
- evaluate_model() gives accuracy, weighted precision and sensitivity, standard error and AUC, so all models can be compared in one table.
- 5-fold cross-validation checks the accuracy on 5 different train/test splits to confirm the result is stable.
