{ "cells": [ { "cell_type": "markdown", "id": "e20a1210", "metadata": {}, "source": [ "## Step 1: Import Libraries" ] }, { "cell_type": "code", "execution_count": 22, "id": "7644ff9f", "metadata": {}, "outputs": [], "source": [ "import os\n", "import json\n", "import joblib\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", "from sklearn.model_selection import train_test_split\n", "from sklearn.ensemble import RandomForestClassifier\n", "from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, log_loss, classification_report, confusion_matrix" ] }, { "cell_type": "markdown", "id": "7d9c169c", "metadata": {}, "source": [ "## Step 2: Load Dataset" ] }, { "cell_type": "code", "execution_count": 23, "id": "1a6a30cd", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dataset shape: (101371, 16)\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
DriverLapNumberCompoundStintTyreLifePositionLapTime (s)RaceYearLapTime_DeltaCumulative_DegradationPitStopPitNextLapRaceProgressNormalized_TyreLifePosition_Change
0ALB1MEDIUM12.017100.625Abu Dhabi Grand Prix20230.0000.000000.0172410.1176470.0
1ALB2MEDIUM13.01893.560Abu Dhabi Grand Prix2023-7.065-7.065000.0344830.176471-1.0
2ALB3MEDIUM14.01891.768Abu Dhabi Grand Prix2023-1.792-8.857000.0517240.2352940.0
3ALB4MEDIUM15.01891.591Abu Dhabi Grand Prix2023-0.177-9.034000.0689660.2941180.0
4ALB5MEDIUM16.01891.422Abu Dhabi Grand Prix2023-0.169-9.203000.0862070.3529410.0
\n", "
" ], "text/plain": [ " Driver LapNumber Compound Stint TyreLife Position LapTime (s) \\\n", "0 ALB 1 MEDIUM 1 2.0 17 100.625 \n", "1 ALB 2 MEDIUM 1 3.0 18 93.560 \n", "2 ALB 3 MEDIUM 1 4.0 18 91.768 \n", "3 ALB 4 MEDIUM 1 5.0 18 91.591 \n", "4 ALB 5 MEDIUM 1 6.0 18 91.422 \n", "\n", " Race Year LapTime_Delta Cumulative_Degradation PitStop \\\n", "0 Abu Dhabi Grand Prix 2023 0.000 0.000 0 \n", "1 Abu Dhabi Grand Prix 2023 -7.065 -7.065 0 \n", "2 Abu Dhabi Grand Prix 2023 -1.792 -8.857 0 \n", "3 Abu Dhabi Grand Prix 2023 -0.177 -9.034 0 \n", "4 Abu Dhabi Grand Prix 2023 -0.169 -9.203 0 \n", "\n", " PitNextLap RaceProgress Normalized_TyreLife Position_Change \n", "0 0 0.017241 0.117647 0.0 \n", "1 0 0.034483 0.176471 -1.0 \n", "2 0 0.051724 0.235294 0.0 \n", "3 0 0.068966 0.294118 0.0 \n", "4 0 0.086207 0.352941 0.0 " ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "PitNextLap\n", "0 75542\n", "1 25829\n", "Name: count, dtype: int64\n" ] } ], "source": [ "data_path = r'd:\\B.Tech\\Semester 04\\01 Machine Learning\\LabAssginment\\dataset\\f1_strategy_dataset_v4.csv'\n", "df = pd.read_csv(data_path)\n", "\n", "print('Dataset shape:', df.shape)\n", "display(df.head())\n", "\n", "target_column = 'PitNextLap'\n", "print(df[target_column].value_counts())" ] }, { "cell_type": "markdown", "id": "91763788", "metadata": {}, "source": [ "## Step 3: Prepare Data" ] }, { "cell_type": "code", "execution_count": 24, "id": "d1e0e470", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X_train shape: (81096, 76)\n", "X_test shape: (20275, 76)\n", "Class distribution in train:\n", "PitNextLap\n", "0 0.745203\n", "1 0.254797\n", "Name: proportion, dtype: float64\n" ] } ], "source": [ "X = df.drop(columns=[target_column])\n", "y = df[target_column].astype(int)\n", "\n", "X = pd.get_dummies(X, drop_first=False)\n", "\n", "X_train, X_test, y_train, y_test = train_test_split(\n", " X,\n", " y,\n", " test_size=0.2,\n", " random_state=21,\n", " stratify=y\n", ")\n", "\n", "print('X_train shape:', X_train.shape)\n", "print('X_test shape:', X_test.shape)\n", "print('Class distribution in train:')\n", "print(y_train.value_counts(normalize=True).sort_index())" ] }, { "cell_type": "markdown", "id": "425c5bd4", "metadata": {}, "source": [ "## Step 4: Train RandomForest Model" ] }, { "cell_type": "code", "execution_count": 25, "id": "95540597", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model training completed\n" ] } ], "source": [ "model = RandomForestClassifier(\n", " n_estimators=150,\n", " random_state=21,\n", " n_jobs=-1,\n", " class_weight='balanced_subsample'\n", ")\n", "\n", "model.fit(X_train, y_train)\n", "\n", "y_pred = model.predict(X_test)\n", "y_prob = model.predict_proba(X_test)[:, 1]\n", "\n", "print('Model training completed')" ] }, { "cell_type": "markdown", "id": "a0b9980f", "metadata": {}, "source": [ "## Step 5: Check Model Performance" ] }, { "cell_type": "code", "execution_count": 26, "id": "3316a60c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Accuracy: 0.9694\n", "Precision: 0.9729\n", "Recall: 0.905\n", "F1 Score: 0.9377\n", "ROC AUC: 0.9951\n", "Log Loss: 0.1264\n", "\n", "Classification Report:\n", "\n", " precision recall f1-score support\n", "\n", " 0 0.9683 0.9914 0.9797 15109\n", " 1 0.9729 0.9050 0.9377 5166\n", "\n", " accuracy 0.9694 20275\n", " macro avg 0.9706 0.9482 0.9587 20275\n", "weighted avg 0.9695 0.9694 0.9690 20275\n", "\n" ] } ], "source": [ "accuracy = accuracy_score(y_test, y_pred)\n", "precision = precision_score(y_test, y_pred, zero_division=0)\n", "recall = recall_score(y_test, y_pred, zero_division=0)\n", "f1 = f1_score(y_test, y_pred, zero_division=0)\n", "roc_auc = roc_auc_score(y_test, y_prob)\n", "loss = log_loss(y_test, y_prob)\n", "\n", "print('Accuracy:', round(accuracy, 4))\n", "print('Precision:', round(precision, 4))\n", "print('Recall:', round(recall, 4))\n", "print('F1 Score:', round(f1, 4))\n", "print('ROC AUC:', round(roc_auc, 4))\n", "print('Log Loss:', round(loss, 4))\n", "\n", "print('\\nClassification Report:\\n')\n", "print(classification_report(y_test, y_pred, digits=4, zero_division=0))" ] }, { "cell_type": "markdown", "id": "a99818e1", "metadata": {}, "source": [ "## Step 6: Plot Confusion Matrix" ] }, { "cell_type": "code", "execution_count": 27, "id": "89658dd3", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkYAAAHqCAYAAADh64FkAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAARRlJREFUeJzt3QmcTXUbwPFnxjDGMvYZlK0SJrKGKWuESBTJEsoW2XeTJaSmSPZMqjcqIookayRlJ/syESXJFmYayxjmvp/n3723uWNoRmdmnJnf9/M57733nP8999x7X3k8z//5Xy+Hw+EQAAAAiDefAQAAwN8IjAAAAJwIjAAAAJwIjAAAAJwIjAAAAJwIjAAAAJwIjAAAAJwIjAAAAJwIjAAAAJwIjIA7xKFDh6RevXqSI0cO8fLykkWLFll6/l9++cWcd+bMmZae185q1aplNgBwITAC4vj555/lxRdflHvuuUcyZ84s/v7+8sgjj8ikSZPk8uXLyfpZtW/fXvbs2SOvvfaafPzxx1KpUqU08908//zzJijTzzOhz1GDQj2u21tvvZXk8584cUJGjhwpO3futOiKAaRXPql9AcCd4uuvv5ZnnnlGfH19pV27dlK6dGm5evWq/PDDDzJw4EDZt2+fzJgxI1leW4OFjRs3ytChQ6VHjx7J8hpFihQxr5MxY0ZJDT4+PnLp0iX56quvpEWLFh7HZs+ebQLRK1eu3Na5NTAaNWqUFC1aVMqVK5fo561cufK2Xg9A2kVgBIjI0aNHpWXLliZ4WLNmjRQoUMD9uXTv3l0OHz5sAqfkcubMGXObM2fOZHsNzcZo8JFaNODU7Nunn356Q2A0Z84cadSokXz++ecpci0aoGXJkkUyZcqUIq8HwD4opQEiMnbsWImKipIPPvjAIyhyue+++6R3797ux9euXZNXX31V7r33XvMXvmYqXn75ZYmOjvZ4nu5/4oknTNapcuXKJjDRMt1HH33kHqMlIA3IlGamNIDR57lKUK77celzdFxcq1atkmrVqpngKlu2bFKiRAlzTf82x0gDwerVq0vWrFnNc5s0aSIHDhxI8PU0QNRr0nE6F+qFF14wQUZitW7dWpYtWyYXLlxw79u6dasppemx+M6dOycDBgyQMmXKmPekpbjHH39cdu3a5R6zdu1aeeihh8x9vR5XSc71PnUOkWb/tm/fLjVq1DABketziT/HSMuZ+h3Ff//169eXXLlymcwUgLSNwAgQMeUdDVgefvjhRH0enTp1khEjRkiFChVkwoQJUrNmTQkNDTVZp/g0mGjevLk89thjMn78ePMXrAYXWppTTz/9tDmHatWqlZlfNHHixCR9L3ouDcA0MBs9erR5nSeffFLWr19/y+d988035i/906dPm+CnX79+smHDBpPZ0UAqPs30/PXXX+a96n0NPrSElVj6XjVo+eKLLzyyRSVLljSfZXxHjhwxk9D1vb399tsmcNR5WPp5u4KUUqVKmfesunTpYj4/3TQIcvnzzz9NQKVlNv1sa9euneD16VyyfPnymQDp+vXrZt+7775rSm5TpkyRggULJvq9ArApB5DORUREOPSPQpMmTRI1fufOnWZ8p06dPPYPGDDA7F+zZo17X5EiRcy+devWufedPn3a4evr6+jfv79739GjR824cePGeZyzffv25hzxvfLKK2a8y4QJE8zjM2fO3PS6Xa/x4YcfuveVK1fOERAQ4Pjzzz/d+3bt2uXw9vZ2tGvX7obX69Chg8c5n3rqKUeePHlu+ppx30fWrFnN/ebNmzvq1Klj7l+/ft2RP39+x6hRoxL8DK5cuWLGxH8f+vmNHj3avW/r1q03vDeXmjVrmmNhYWEJHtMtrhUrVpjxY8aMcRw5csSRLVs2R9OmTf/1PQJIG8gYId2LjIw0n0H27NkT9VksXbrU3Gp2Ja7+/fub2/hzkYKCgkypykUzElrm0myIVVxzk7788kuJjY1N1HP++OMP08Wl2avcuXO79z/44IMmu+V6n3F17drV47G+L83GuD7DxNCSmZa/Tp48acp4eptQGU1pmdLb++//TGkGR1/LVSb88ccfE/2aeh4tsyWGLpmgnYmahdIMl5bWNGsEIH0gMEK6p/NWlJaIEuPXX381f1nrvKO48ufPbwIUPR5X4cKFbziHltPOnz9v2Wf/7LPPmvKXlvgCAwNNSe+zzz67ZZDkuk4NMuLT8tTZs2fl4sWLt3wv+j5UUt5Lw4YNTRA6b948042m84Pif5Yuev1aZixevLgJbvLmzWsCy927d0tERESiX/Ouu+5K0kRrXTJAg0UNHCdPniwBAQGJfi4AeyMwQrqngZHOHdm7d2+SPov4k59vJkOGDAnudzgct/0arvkvLn5+frJu3TozZ6ht27YmcNBg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v36mK+3HH3+UsmXLSv369eX06dN8E0AiXLx40fy50X9gALAeXWlIUZoheuihh2Tq1KnmcWxsrBQqVEh69uwpQ4YM4dsAkkAzRgsXLpSmTZvyuQEWIWOEFHP16lXZvn271K1b95//A3p7m8cbN27kmwAApDoCI6SYs2fPyvXr1yUwMNBjvz4+efIk3wQAINURGAEAADgRGCHF5M2bVzJkyCCnTp3y2K+P8+fPzzcBAEh1BEZIMZkyZZKKFSvK6tWr3ft08rU+Dg4O5psAAKQ6n9S+AKQv2qrfvn17qVSpklSuXFkmTpxo2o9feOGF1L40wBaioqLk8OHD7sdHjx6VnTt3Su7cuaVw4cKpem1AWkC7PlKctuqPGzfOTLguV66cTJ482bTxA/h3a9euldq1a9+wX//BMXPmTD5C4D8iMAIAAHBijhEAAIATgREAAIATgREAAIATgREAAIATgREAAIATgREAAIATgREAAIATgREAAIATgRGQzj3//PPStGlT9+NatWpJnz59UmVFZy8vL7lw4UKKvzYAuBAYAXdwwKKBgm76A7z33XefjB49Wq5du5asr/vFF1/Iq6++mqixBDMA0hp+RBa4gzVo0EA+/PBDiY6OlqVLl0r37t0lY8aMEhIS4jHu6tWrJniygv4YKQCkV2SMgDuYr6+v5M+fX4oUKSLdunWTunXryuLFi93lr9dee00KFiwoJUqUMON/++03adGiheTMmdMEOE2aNJFffvnFfb7r169Lv379zPE8efLIoEGDxOFweLxm/FKaBmWDBw+WQoUKmevRzNUHH3xgzuv6MdNcuXKZzJZel4qNjZXQ0FApVqyY+Pn5SdmyZWXBggUer6OB3v3332+O63niXicApBYCI8BGNIjQ7JBavXq1hIeHy6pVq2TJkiUSExMj9evXl+zZs8v3338v69evl2zZspmsk+s548ePN7/A/r///U9++OEHOXfunCxcuPCWr9muXTv59NNPZfLkyXLgwAF59913zXk1UPr888/NGL2OP/74QyZNmmQea1D00UcfSVhYmOzbt0/69u0rzz33nHz33XfuAO7pp5+Wxo0by86dO6VTp04yZMiQZP70ACARHADuSO3bt3c0adLE3I+NjXWsWrXK4evr6xgwYIA5FhgY6IiOjnaP//jjjx0lSpQwY130uJ+fn2PFihXmcYECBRxjx451H4+JiXHcfffd7tdRNWvWdPTu3dvcDw8P13SSee2EfPvtt+b4+fPn3fuuXLniyJIli2PDhg0eYzt27Oho1aqVuR8SEuIICgryOD548OAbzgUAKY05RsAdTDNBmp3RbJCWp1q3bi0jR440c43KlCnjMa9o165dcvjwYZMxiuvKlSvy888/S0REhMnqVKlSxX3Mx8dHKlWqdEM5zUWzORkyZJCaNWsm+pr1Gi5duiSPPfaYx37NWpUvX97c18xT3OtQwcHBiX4NAEguBEbAHUzn3kyfPt0EQDqXSAMZl6xZs3qMjYqKkooVK8rs2bNvOE++fPluu3SXVHod6uuvv5a77rrL45jOUQKAOxmBEXAH0+BHJzsnRoUKFWTevHkSEBAg/v7+CY4pUKCAbN68WWrUqGEea+v/9u3bzXMTolkpzVTp3CCd+B2fK2Olk7pdgoKCTAB07Nixm2aaSpUqZSaRx7Vp06ZEvU8ASE5MvgbSiDZt2kjevHlNJ5pOvj569KhZZ6hXr15y/PhxM6Z3797yxhtvyKJFi+TgwYPy0ksv3XJBxaJFi0r79u2lQ4cO5jmuc3722WfmuHbLaTealvzOnDljskVayhswYICZcD1r1ixTxvvxxx9lypQp5rHq2rWrHDp0SAYOHGgmbs+ZM8dMCgeA1EZgBKQRWbJkkXXr1knhwoVNx5dmZTp27GjmGLkySP3795e2bduaYEfn9GgQ89RTT93yvFrKa968uQmiSpYsKZ07d5aLFy+aY1oqGzVqlOkoCwwMlB49epj9ukDk8OHDTXeaXod2xmlpTdv3lV6jdrRpsKWt/Nq99vrrryf7ZwQA/8ZLZ2D/6ygAAIB0gIwRAACAE4ERAACAE4ERAACAE4ERAACAE4ERAACAE4ERAACAE4ERAACAE4ERAACAE4ERAACAE4ERAACAE4ERAACAE4ERAACA/O3/eNkL5+0vWWgAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cm = confusion_matrix(y_test, y_pred)\n", "\n", "plt.figure(figsize=(6, 5))\n", "sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')\n", "plt.xlabel('Predicted')\n", "plt.ylabel('Actual')\n", "plt.title('Confusion Matrix')\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "d710186f", "metadata": {}, "source": [ "## Step 7: Save Model and Metrics" ] }, { "cell_type": "code", "execution_count": 28, "id": "e2060814", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Saved model: d:\\B.Tech\\Semester 04\\01 Machine Learning\\LabAssginment\\outputs\\F1_Strategy\\model\\model.joblib\n", "Saved columns: d:\\B.Tech\\Semester 04\\01 Machine Learning\\LabAssginment\\outputs\\F1_Strategy\\model\\feature_columns.json\n", "Saved labels: d:\\B.Tech\\Semester 04\\01 Machine Learning\\LabAssginment\\outputs\\F1_Strategy\\model\\label_classes.json\n", "Saved target info: d:\\B.Tech\\Semester 04\\01 Machine Learning\\LabAssginment\\outputs\\F1_Strategy\\model\\target_info.json\n", "Saved metrics: d:\\B.Tech\\Semester 04\\01 Machine Learning\\LabAssginment\\outputs\\F1_Strategy\\metrics\\final_metircs.csv\n", "Saved predictions: d:\\B.Tech\\Semester 04\\01 Machine Learning\\LabAssginment\\outputs\\F1_Strategy\\metrics\\prediction.csv\n" ] } ], "source": [ "base_output = r'd:\\B.Tech\\Semester 04\\01 Machine Learning\\LabAssginment\\outputs\\F1_Strategy'\n", "model_dir = os.path.join(base_output, 'model')\n", "metrics_dir = os.path.join(base_output, 'metrics')\n", "\n", "os.makedirs(model_dir, exist_ok=True)\n", "os.makedirs(metrics_dir, exist_ok=True)\n", "\n", "model_path = os.path.join(model_dir, 'model.joblib')\n", "columns_path = os.path.join(model_dir, 'feature_columns.json')\n", "labels_path = os.path.join(model_dir, 'label_classes.json')\n", "info_path = os.path.join(model_dir, 'target_info.json')\n", "metrics_path = os.path.join(metrics_dir, 'final_metircs.csv')\n", "predictions_path = os.path.join(metrics_dir, 'prediction.csv')\n", "\n", "joblib.dump(model, model_path)\n", "with open(columns_path, 'w', encoding='utf-8') as f:\n", " json.dump(list(X.columns), f, indent=2)\n", "with open(labels_path, 'w', encoding='utf-8') as f:\n", " json.dump([0, 1], f, indent=2)\n", "with open(info_path, 'w', encoding='utf-8') as f:\n", " json.dump({'target_col': target_column, 'model_name': 'RandomForestClassifier'}, f, indent=2)\n", "\n", "final_metrics = pd.DataFrame([\n", " {'metric': 'accuracy', 'value': accuracy},\n", " {'metric': 'precision', 'value': precision},\n", " {'metric': 'recall', 'value': recall},\n", " {'metric': 'f1', 'value': f1},\n", " {'metric': 'roc_auc', 'value': roc_auc},\n", " {'metric': 'log_loss', 'value': loss}\n", "])\n", "final_metrics.to_csv(metrics_path, index=False)\n", "\n", "prediction_df = pd.DataFrame({\n", " 'Actual': y_test,\n", " 'Predicted': y_pred,\n", " 'Probability': y_prob\n", "})\n", "prediction_df.to_csv(predictions_path, index=False)\n", "\n", "print('Saved model:', model_path)\n", "print('Saved columns:', columns_path)\n", "print('Saved labels:', labels_path)\n", "print('Saved target info:', info_path)\n", "print('Saved metrics:', metrics_path)\n", "print('Saved predictions:', predictions_path)" ] }, { "cell_type": "markdown", "id": "25072de9", "metadata": {}, "source": [ "## Step 8: Final Summary" ] }, { "cell_type": "code", "execution_count": 29, "id": "ebe897d6", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
MetricValue
0Accuracy0.969371
1Precision0.972945
2Recall0.904955
3F1 Score0.937719
4ROC AUC0.995099
5Log Loss0.126425
\n", "
" ], "text/plain": [ " Metric Value\n", "0 Accuracy 0.969371\n", "1 Precision 0.972945\n", "2 Recall 0.904955\n", "3 F1 Score 0.937719\n", "4 ROC AUC 0.995099\n", "5 Log Loss 0.126425" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Final model used: RandomForestClassifier\n", "Target column: PitNextLap\n" ] } ], "source": [ "summary = pd.DataFrame([\n", " {'Metric': 'Accuracy', 'Value': accuracy},\n", " {'Metric': 'Precision', 'Value': precision},\n", " {'Metric': 'Recall', 'Value': recall},\n", " {'Metric': 'F1 Score', 'Value': f1},\n", " {'Metric': 'ROC AUC', 'Value': roc_auc},\n", " {'Metric': 'Log Loss', 'Value': loss}\n", "])\n", "\n", "display(summary)\n", "print('Final model used: RandomForestClassifier')\n", "print('Target column:', target_column)" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }