{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":104857,"databundleVersionId":12651513,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:40:17.757976Z","iopub.execute_input":"2025-06-14T15:40:17.758196Z","iopub.status.idle":"2025-06-14T15:40:17.764272Z","shell.execute_reply.started":"2025-06-14T15:40:17.758170Z","shell.execute_reply":"2025-06-14T15:40:17.763657Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.ensemble import RandomForestRegressor\nfrom sklearn.metrics import mean_squared_error\nfrom sklearn.preprocessing import StandardScaler, OneHotEncoder\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.impute import SimpleImputer\nimport xgboost as xgb\nfrom catboost import CatBoostRegressor\nimport lightgbm as lgb\nimport os\nimport joblib","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:40:21.583346Z","iopub.execute_input":"2025-06-14T15:40:21.583847Z","iopub.status.idle":"2025-06-14T15:40:27.206543Z","shell.execute_reply.started":"2025-06-14T15:40:21.583826Z","shell.execute_reply":"2025-06-14T15:40:27.205778Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Loading","metadata":{}},{"cell_type":"code","source":"for dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\ntrain_df = pd.read_csv('/kaggle/input/burnout-datathon-ieeecsmuj/train.csv')\ntest_df = pd.read_csv('/kaggle/input/burnout-datathon-ieeecsmuj/test.csv')\nval_df = pd.read_csv('/kaggle/input/burnout-datathon-ieeecsmuj/val.csv')\n\nprint(\"Training set shape:\", train_df.shape)\nprint(\"Test set shape:\", test_df.shape)\nprint(\"Validation set shape:\", val_df.shape)\n\nprint(\"\\nTraining set info:\")\ntrain_df.info()\nprint(\"\\nFirst few rows of training set:\")\ntrain_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:40:31.235135Z","iopub.execute_input":"2025-06-14T15:40:31.235875Z","iopub.status.idle":"2025-06-14T15:40:50.863590Z","shell.execute_reply.started":"2025-06-14T15:40:31.235848Z","shell.execute_reply":"2025-06-14T15:40:50.862816Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Exploratory Data Analysis","metadata":{}},{"cell_type":"markdown","source":"## Target Variable Analysis","metadata":{}},{"cell_type":"code","source":"print(\"\\n--- Target Variable Analysis ---\")\nplt.figure(figsize=(10, 6))\nsns.histplot(train_df['Lap_Time_Seconds'], kde=True)\nplt.title('Distribution of Lap Times')\nplt.xlabel('Lap Time (seconds)')\nplt.ylabel('Frequency')\nplt.show()\n\nq1 = train_df['Lap_Time_Seconds'].quantile(0.25)\nq3 = train_df['Lap_Time_Seconds'].quantile(0.75)\niqr = q3 - q1\nlower_bound = q1 - 1.5 * iqr\nupper_bound = q3 + 1.5 * iqr\noutliers = train_df[(train_df['Lap_Time_Seconds'] < lower_bound) | (train_df['Lap_Time_Seconds'] > upper_bound)]\nprint(f\"Number of outliers in lap times (IQR method): {len(outliers)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:41:21.295619Z","iopub.execute_input":"2025-06-14T15:41:21.296387Z","iopub.status.idle":"2025-06-14T15:41:28.509090Z","shell.execute_reply.started":"2025-06-14T15:41:21.296351Z","shell.execute_reply":"2025-06-14T15:41:28.508432Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Categorical Features Analysis","metadata":{}},{"cell_type":"code","source":"print(\"\\n--- Categorical Features Analysis ---\")\ncat_cols = ['category_x', 'Track_Condition', 'Tire_Compound_Front', 'Tire_Compound_Rear',\n            'Penalty', 'Session', 'weather']\n\nplt.figure(figsize=(15, 20))\nfor i, col in enumerate(cat_cols, 1):\n    plt.subplot(4, 2, i)\n    sns.boxplot(x=col, y='Lap_Time_Seconds', data=train_df)\n    plt.xticks(rotation=45, ha='right')\n    plt.title(f'Lap Time by {col}')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:41:33.844220Z","iopub.execute_input":"2025-06-14T15:41:33.844502Z","iopub.status.idle":"2025-06-14T15:41:39.764097Z","shell.execute_reply.started":"2025-06-14T15:41:33.844480Z","shell.execute_reply":"2025-06-14T15:41:39.763318Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Numerical Features Analysis","metadata":{}},{"cell_type":"code","source":"print(\"\\n--- Numerical Features Analysis ---\")\nnum_cols = [\n    'Circuit_Length_km', 'Laps', 'Grid_Position', 'Avg_Speed_kmh', 'Humidity_%',\n    'Championship_Points', 'Championship_Position', 'Corners_per_Lap',\n    'Tire_Degradation_Factor_per_Lap', 'Pit_Stop_Duration_Seconds',\n    'Ambient_Temperature_Celsius', 'Track_Temperature_Celsius',\n    'starts', 'finishes', 'with_points', 'podiums', 'wins', 'years_active', 'year_x', 'min_year'\n]\n\nexisting_num_cols = [col for col in num_cols if col in train_df.columns]\ncorr_data = train_df[existing_num_cols + ['Lap_Time_Seconds']].replace([np.inf, -np.inf], np.nan).dropna()\ncorr_matrix = corr_data.corr()\n\nplt.figure(figsize=(20, 15))\nmask = np.triu(np.ones_like(corr_matrix, dtype=bool))\nsns.heatmap(corr_matrix, mask=mask, annot=True, fmt=\".2f\",\n            cmap='coolwarm', center=0, vmin=-1, vmax=1,\n            linewidths=0.5, linecolor='white', cbar_kws={\"shrink\": .8})\nplt.title('Correlation Matrix of Numerical Features with Lap Time', pad=20, fontsize=16)\nplt.xticks(rotation=45, ha='right')\nplt.yticks(rotation=0)\nplt.tight_layout()\nplt.show()\n\nif 'Lap_Time_Seconds' in corr_matrix.columns:\n    lap_time_corr = corr_matrix['Lap_Time_Seconds'].sort_values(ascending=False)\n    print(\"\\nTop Positive Correlations with Lap Time:\")\n    print(lap_time_corr.head(10))\n    print(\"\\nTop Negative Correlations with Lap Time:\")\n    print(lap_time_corr.tail(10))\nelse:\n    print(\"\\n'Lap_Time_Seconds' not found in correlation matrix (perhaps only one row in corr_data).\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:41:45.995726Z","iopub.execute_input":"2025-06-14T15:41:45.996583Z","iopub.status.idle":"2025-06-14T15:41:49.651564Z","shell.execute_reply.started":"2025-06-14T15:41:45.996550Z","shell.execute_reply":"2025-06-14T15:41:49.650698Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Time-Based Analysis","metadata":{}},{"cell_type":"code","source":"print(\"\\n--- Time-Based Analysis ---\")\nplt.figure(figsize=(12, 6))\ntrain_df['Lap_Time_Seconds'] = train_df['Lap_Time_Seconds'].replace([float('inf'), float('-inf')], np.nan)\ntrain_df['year_x'] = train_df['year_x'].replace([float('inf'), float('-inf')], np.nan)\nsns.lineplot(x='year_x', y='Lap_Time_Seconds', data=train_df, estimator='median')\nplt.title('Median Lap Time by Year')\nplt.xlabel('Year')\nplt.ylabel('Lap Time (seconds)')\nplt.grid(True)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:42:09.615701Z","iopub.execute_input":"2025-06-14T15:42:09.616063Z","iopub.status.idle":"2025-06-14T15:42:50.175740Z","shell.execute_reply.started":"2025-06-14T15:42:09.616041Z","shell.execute_reply":"2025-06-14T15:42:50.175077Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Preprocessing and Feature Engineering ","metadata":{}},{"cell_type":"markdown","source":"## Handling Missing Values","metadata":{}},{"cell_type":"code","source":"print(\"\\n--- Handling Missing Values ---\")\ntrain_df['Penalty'] = train_df['Penalty'].fillna('No Penalty')\ntest_df['Penalty'] = test_df['Penalty'].fillna('No Penalty')\nval_df['Penalty'] = val_df['Penalty'].fillna('No Penalty')\n\nprint(\"Missing values in training set after initial imputation:\")\nprint(train_df.isnull().sum().sort_values(ascending=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:42:56.421801Z","iopub.execute_input":"2025-06-14T15:42:56.422485Z","iopub.status.idle":"2025-06-14T15:42:57.706169Z","shell.execute_reply.started":"2025-06-14T15:42:56.422462Z","shell.execute_reply":"2025-06-14T15:42:57.705381Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Feature Engineering","metadata":{}},{"cell_type":"code","source":"print(\"\\n--- Feature Engineering ---\")\n\nglobal_min_year = pd.concat([train_df['year_x'], test_df['year_x'], val_df['year_x']]).min()\n\ndef engineer_features(df, global_min_year_val):\n    df['starts_safe'] = df['starts'].replace(0, np.nan)\n\n    df['Experience'] = df['years_active'] / (df['year_x'] - global_min_year_val + 1).replace(0, np.nan)\n    df['Finish_Rate'] = df['finishes'] / df['starts_safe']\n    df['Podium_Rate'] = df['podiums'] / df['starts_safe']\n    df['Win_Rate'] = df['wins'] / df['starts_safe']\n    df['Points_Rate'] = df['with_points'] / df['starts_safe']\n\n    df['Speed_to_Circuit_Ratio'] = df['Avg_Speed_kmh'] / df['Circuit_Length_km'].replace(0, np.nan)\n\n    df['Tire_Impact'] = df['Tire_Degradation_Factor_per_Lap'] * df['Laps']\n\n    df['Position_Potential'] = df['Grid_Position'] - df['Championship_Position']\n\n    df['Weather_Impact'] = np.where(df['weather'] == 'rainy', 1, 0)\n    df['Track_Condition_Impact'] = np.where(df['Track_Condition'] == 'wet', 1, 0)\n\n    df = df.drop(columns=['starts_safe'], errors='ignore')\n    df = df.replace([np.inf, -np.inf], np.nan)\n\n    return df\n\ntrain_df = engineer_features(train_df, global_min_year)\ntest_df = engineer_features(test_df, global_min_year)\nval_df = engineer_features(val_df, global_min_year)\n\nselected_features = [\n    'Circuit_Length_km', 'Laps', 'Grid_Position', 'Avg_Speed_kmh',\n    'Humidity_%', 'Championship_Points', 'Championship_Position',\n    'Corners_per_Lap', 'Tire_Degradation_Factor_per_Lap',\n    'Ambient_Temperature_Celsius', 'Track_Temperature_Celsius',\n    'Pit_Stop_Duration_Seconds',\n    'Experience', 'Finish_Rate', 'Podium_Rate', 'Win_Rate',\n    'Points_Rate', 'Speed_to_Circuit_Ratio', 'Tire_Impact',\n    'Position_Potential', 'Weather_Impact', 'Track_Condition_Impact',\n    'category_x', 'Track_Condition', 'Tire_Compound_Front',\n    'Tire_Compound_Rear', 'Session', 'weather'\n]\n\ntarget = 'Lap_Time_Seconds'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:43:03.984143Z","iopub.execute_input":"2025-06-14T15:43:03.984710Z","iopub.status.idle":"2025-06-14T15:43:13.197306Z","shell.execute_reply.started":"2025-06-14T15:43:03.984687Z","shell.execute_reply":"2025-06-14T15:43:13.196683Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model Development ","metadata":{}},{"cell_type":"markdown","source":"## Preprocessing Pipeline","metadata":{}},{"cell_type":"code","source":"X_train = train_df[selected_features]\ny_train = train_df[target]\nX_val = val_df[selected_features]\ny_val = val_df[target]\n\nnumeric_features = X_train.select_dtypes(include=np.number).columns.tolist()\ncategorical_features = X_train.select_dtypes(include='object').columns.tolist()\n\nprint(f\"\\nNumeric features for preprocessing: {numeric_features}\")\nprint(f\"Categorical features for preprocessing: {categorical_features}\")\n\nif not (set(numeric_features) | set(categorical_features) == set(selected_features)):\n    uncovered_features = set(selected_features) - (set(numeric_features) | set(categorical_features))\n    print(f\"Warning: Some selected features are not recognized as numeric or categorical: {uncovered_features}\")\n\nnumeric_transformer = Pipeline(steps=[\n    ('imputer', SimpleImputer(strategy='median')),\n    ('scaler', StandardScaler())])\n\ncategorical_transformer = Pipeline(steps=[\n    ('imputer', SimpleImputer(strategy='most_frequent')),\n    ('onehot', OneHotEncoder(handle_unknown='ignore'))])\n\npreprocessor = ColumnTransformer(\n    transformers=[\n        ('num', numeric_transformer, numeric_features),\n        ('cat', categorical_transformer, categorical_features)])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:43:26.535671Z","iopub.execute_input":"2025-06-14T15:43:26.535977Z","iopub.status.idle":"2025-06-14T15:43:26.931469Z","shell.execute_reply.started":"2025-06-14T15:43:26.535956Z","shell.execute_reply":"2025-06-14T15:43:26.930698Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Hyperparameter Tuning","metadata":{}},{"cell_type":"code","source":"\"\"\"\nfrom sklearn.model_selection import cross_val_score\nimport optuna\ndef objective(trial):\n    params = {\n        'n_estimators': trial.suggest_int('n_estimators', 100, 1000),\n        'max_depth': trial.suggest_int('max_depth', 10, 20),\n        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3, log=True),\n        'subsample': trial.suggest_float('subsample', 0.6, 1.0),\n        'colsample_bytree': trial.suggest_float('colsample_bytree', 0.6, 1.0),\n        'min_child_weight': trial.suggest_int('min_child_weight', 1, 10),\n        'gamma': trial.suggest_float('gamma', 0, 0.5),\n        'reg_alpha': trial.suggest_float('reg_alpha', 0, 1),\n        'reg_lambda': trial.suggest_float('reg_lambda', 0, 1),\n        'random_state': 42,\n        'tree_method': 'hist',\n        'device': 'cuda'\n    }\n    \n    model = xgb.XGBRegressor(**params)\n    pipeline = Pipeline(steps=[\n        ('preprocessor', preprocessor),\n        ('model', model)\n    ])\n    \n    scores = cross_val_score(pipeline, X_train, y_train, \n                           cv=5, scoring='neg_root_mean_squared_error')\n    return -scores.mean()\n\nstudy = optuna.create_study(direction='minimize')\nstudy.optimize(objective, n_trials=20,n_jobs=-1)\n\nprint(\"Best trial:\")\ntrial = study.best_trial\nprint(f\"RMSE: {trial.value:.4f}\")\nprint(\"Best params:\")\nprint(trial.params)\n\"\"\"","metadata":{"trusted":true,"execution":{"execution_failed":"2025-06-14T12:19:11.692Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n--- Model Training, Saving, and Evaluation ---\")\n\nmodels = {\n    'XGBoost': xgb.XGBRegressor(n_estimators=433, max_depth=18, learning_rate= 0.2550333930882739, subsample=0.8332531303240234, colsample_bytree=0.9992660783644403, min_child_weight=4, gamma=0.004522254153302818,reg_alpha=0.2355599512208678,reg_lambda=0.5449468735593271),\n}\n\nresults = {}\nfor name, model in models.items():\n    pipeline = Pipeline(steps=[\n        ('preprocessor', preprocessor),\n        ('model', model)])\n\n    print(f\"Training {name}...\")\n    pipeline.fit(X_train, y_train)\n\n    model_filename = f'{name.lower()}_pipeline.joblib'\n    joblib.dump(pipeline, model_filename)\n    print(f\"Saved {name} pipeline to {model_filename}\")\n\n    y_pred = pipeline.predict(X_val)\n    rmse = np.sqrt(mean_squared_error(y_val, y_pred))\n    results[name] = rmse\n    print(f\"{name} RMSE: {rmse:.4f}\\n\")\n\n\nbest_model_name = min(results, key=results.get)\nbest_rmse = results[best_model_name]\n\nprint(f\"\\nAutomatically selected '{best_model_name}' as the final model due to lowest RMSE: {best_rmse:.4f}\")\n\nprint(f\"\\nLoading {best_model_name} as the final model for predictions...\")\nfinal_model_filename = f'{best_model_name.lower()}_pipeline.joblib'\nfinal_model = joblib.load(final_model_filename)\nprint(f\"Successfully loaded {best_model_name} from {final_model_filename}\")\n\nX_test = test_df[selected_features]\ntest_predictions = final_model.predict(X_test)\n\nsubmission = pd.DataFrame({\n    'Unique ID': test_df['Unique ID'],\n    'Lap_Time_Seconds': test_predictions\n})\n\nsubmission.to_csv('solution.csv', index=False)\nprint(\"\\nSubmission file created successfully!\")\nprint(submission.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-14T15:43:55.615799Z","iopub.execute_input":"2025-06-14T15:43:55.616365Z","iopub.status.idle":"2025-06-14T15:45:33.153903Z","shell.execute_reply.started":"2025-06-14T15:43:55.616340Z","shell.execute_reply":"2025-06-14T15:45:33.153074Z"}},"outputs":[],"execution_count":null}]}