{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Formula One Project: Modeling\n",
    "\n",
    "DUE: December 4th, 2024 (Wed)  \n",
    "Name(s): Sean O'Connor, Connor Coles    \n",
    "Class: CSCI 349 - Intro to Data Mining  \n",
    "Semester: Fall 2024  \n",
    "Instructor: Brian King  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Assignment Description\n",
    "\n",
    "Copy over the important cells from the previous step that read in and cleaned your data to this new notebook file. You do not need to copy over all your EDA and plots describing your data, only the code that prepares your data for modeling. This notebook is about exploring the development of predictive models. Some initial preliminary work on applying some modeling techniques should be completed.\n",
    "Be sure to commit and push all supporting code that you've completed in this file. Include in this notebook a summary cell at the top that details your accomplishments, challenges, and what you expect to accomplish for your final steps. Be sure to update your readme.md in your repository."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Importing Libraries\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import os\n",
    "\n",
    "import fastf1\n",
    "import fastf1.plotting\n",
    "from fastf1.ergast.structure import FastestLap\n",
    "\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sklearn.linear_model import LinearRegression\n",
    "from sklearn.ensemble import RandomForestRegressor\n",
    "from sklearn.metrics import mean_squared_error, r2_score\n",
    "from sklearn.tree import DecisionTreeRegressor\n",
    "from sklearn.ensemble import GradientBoostingRegressor\n",
    "from sklearn.svm import SVR\n",
    "import xgboost as xgb"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# FastF1 general setup\n",
    "cache_dir = '../data/cache'\n",
    "if not os.path.exists(cache_dir):\n",
    "    os.makedirs(cache_dir)\n",
    "\n",
    "fastf1.Cache.enable_cache(cache_dir)\n",
    "fastf1.plotting.setup_mpl(misc_mpl_mods=False, color_scheme=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define years and sessions of interest\n",
    "years = [2021, 2022, 2023, 2024]\n",
    "sessions = ['Race']  # Qualifying and Race sessions\n",
    "# events = ['Brazil', 'Australia', 'Italy', 'United States'] \n",
    "events = ['Great Britain', 'Mexico', 'Brazil', 'Bahrain', 'United States'] "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "core           INFO \tLoading data for British Grand Prix - Race [v3.4.4]\n",
      "req            INFO \tNo cached data found for session_info. Loading data...\n",
      "_api           INFO \tFetching session info data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for driver_info. Loading data...\n",
      "_api           INFO \tFetching driver list...\n",
      "req            INFO \tData has been written to cache!\n",
      "logger      WARNING \tFailed to load result data from Ergast!\n",
      "core        WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n",
      "req            INFO \tNo cached data found for session_status_data. Loading data...\n",
      "_api           INFO \tFetching session status data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for lap_count. Loading data...\n",
      "_api           INFO \tFetching lap count data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for track_status_data. Loading data...\n",
      "_api           INFO \tFetching track status data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for _extended_timing_data. Loading data...\n",
      "_api           INFO \tFetching timing data...\n",
      "_api           INFO \tParsing timing data...\n",
      "_api        WARNING \tDriver  4: Ignoring late data for a previously processed lap.The data may contain errors (previous: 43; current 44)\n",
      "_api        WARNING \tDriver 55: Ignoring late data for a previously processed lap.The data may contain errors (previous: 7; current 8)\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for timing_app_data. Loading data...\n",
      "_api           INFO \tFetching timing app data...\n",
      "req            INFO \tData has been written to cache!\n",
      "core           INFO \tProcessing timing data...\n",
      "logger      WARNING \tFailed to add first lap time from Ergast!\n",
      "req            INFO \tNo cached data found for car_data. Loading data...\n",
      "_api           INFO \tFetching car data...\n",
      "_api           INFO \tParsing car data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for position_data. Loading data...\n",
      "_api           INFO \tFetching position data...\n",
      "_api           INFO \tParsing position data...\n",
      "_api        WARNING \tDriver 241: Position data is incomplete!\n",
      "_api        WARNING \tDriver 242: Position data is incomplete!\n",
      "_api        WARNING \tDriver 243: Position data is incomplete!\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for weather_data. Loading data...\n",
      "_api           INFO \tFetching weather data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for race_control_messages. Loading data...\n",
      "_api           INFO \tFetching race control messages...\n",
      "req            INFO \tData has been written to cache!\n",
      "core           INFO \tFinished loading data for 20 drivers: ['33', '44', '77', '16', '4', '3', '14', '5', '31', '55', '10', '63', '7', '18', '99', '22', '6', '47', '9', '11']\n",
      "core           INFO \tLoading data for Mexico City Grand Prix - Race [v3.4.4]\n",
      "req            INFO \tNo cached data found for session_info. Loading data...\n",
      "_api           INFO \tFetching session info data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for driver_info. Loading data...\n",
      "_api           INFO \tFetching driver list...\n",
      "req            INFO \tData has been written to cache!\n",
      "logger      WARNING \tFailed to load result data from Ergast!\n",
      "core        WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n",
      "req            INFO \tNo cached data found for session_status_data. Loading data...\n",
      "_api           INFO \tFetching session status data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for lap_count. Loading data...\n",
      "_api           INFO \tFetching lap count data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for track_status_data. Loading data...\n",
      "_api           INFO \tFetching track status data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for _extended_timing_data. Loading data...\n",
      "_api           INFO \tFetching timing data...\n",
      "_api           INFO \tParsing timing data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for timing_app_data. Loading data...\n",
      "_api           INFO \tFetching timing app data...\n",
      "req            INFO \tData has been written to cache!\n",
      "core           INFO \tProcessing timing data...\n",
      "logger      WARNING \tFailed to add first lap time from Ergast!\n",
      "req            INFO \tNo cached data found for car_data. Loading data...\n",
      "_api           INFO \tFetching car data...\n",
      "_api           INFO \tParsing car data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for position_data. Loading data...\n",
      "_api           INFO \tFetching position data...\n",
      "_api           INFO \tParsing position data...\n",
      "_api        WARNING \tDriver 241: Position data is incomplete!\n",
      "_api        WARNING \tDriver 242: Position data is incomplete!\n",
      "_api        WARNING \tDriver 243: Position data is incomplete!\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for weather_data. Loading data...\n",
      "_api           INFO \tFetching weather data...\n",
      "req            INFO \tData has been written to cache!\n",
      "req            INFO \tNo cached data found for race_control_messages. Loading data...\n",
      "_api           INFO \tFetching race control messages...\n",
      "req            INFO \tData has been written to cache!\n",
      "core           INFO \tFinished loading data for 20 drivers: ['77', '44', '33', '11', '10', '55', '3', '16', '5', '7', '99', '14', '6', '47', '9', '63', '22', '4', '31', '18']\n",
      "core           INFO \tLoading data for São Paulo Grand Prix - Race [v3.4.4]\n",
      "req            INFO \tUsing cached data for session_info\n",
      "req            INFO \tUsing cached data for driver_info\n",
      "logger      WARNING \tFailed to load result data from Ergast!\n",
      "core        WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n",
      "req            INFO \tUsing cached data for session_status_data\n",
      "req            INFO \tUsing cached data for lap_count\n",
      "req            INFO \tUsing cached data for track_status_data\n",
      "req            INFO \tUsing cached data for _extended_timing_data\n",
      "req            INFO \tUsing cached data for timing_app_data\n",
      "core           INFO \tProcessing timing data...\n",
      "logger      WARNING \tFailed to add first lap time from Ergast!\n",
      "req            INFO \tUsing cached data for car_data\n",
      "req            INFO \tUsing cached data for position_data\n",
      "req            INFO \tUsing cached data for weather_data\n",
      "req            INFO \tUsing cached data for race_control_messages\n",
      "core           INFO \tFinished loading data for 20 drivers: ['77', '33', '55', '11', '4', '16', '10', '31', '5', '44', '3', '14', '99', '18', '22', '6', '63', '47', '9', '7']\n",
      "core           INFO \tLoading data for Bahrain Grand Prix - Race [v3.4.4]\n",
      "req            INFO \tUsing cached data for session_info\n",
      "req            INFO \tUsing cached data for driver_info\n",
      "Request for URL https://ergast.com/api/f1/2021/1/results.json failed; using cached response\n",
      "Traceback (most recent call last):\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/requests_cache/session.py\", line 286, in _resend\n",
      "    response = self._send_and_cache(request, actions, cached_response, **kwargs)\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/requests_cache/session.py\", line 254, in _send_and_cache\n",
      "    response = super().send(request, **kwargs)\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/fastf1/req.py\", line 134, in send\n",
      "    lim.limit()\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/fastf1/req.py\", line 104, in limit\n",
      "    raise RateLimitExceededError(self._info)\n",
      "fastf1.req.RateLimitExceededError: any API: 500 calls/h\n",
      "req            INFO \tUsing cached data for session_status_data\n",
      "req            INFO \tUsing cached data for lap_count\n",
      "req            INFO \tUsing cached data for track_status_data\n",
      "req            INFO \tUsing cached data for _extended_timing_data\n",
      "req            INFO \tUsing cached data for timing_app_data\n",
      "core           INFO \tProcessing timing data...\n",
      "Request for URL https://ergast.com/api/f1/2021/1/laps/1.json failed; using cached response\n",
      "Traceback (most recent call last):\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/requests_cache/session.py\", line 286, in _resend\n",
      "    response = self._send_and_cache(request, actions, cached_response, **kwargs)\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/requests_cache/session.py\", line 254, in _send_and_cache\n",
      "    response = super().send(request, **kwargs)\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/fastf1/req.py\", line 134, in send\n",
      "    lim.limit()\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/fastf1/req.py\", line 104, in limit\n",
      "    raise RateLimitExceededError(self._info)\n",
      "fastf1.req.RateLimitExceededError: any API: 500 calls/h\n",
      "req            INFO \tUsing cached data for car_data\n",
      "req            INFO \tUsing cached data for position_data\n",
      "req            INFO \tUsing cached data for weather_data\n",
      "req            INFO \tUsing cached data for race_control_messages\n",
      "core           INFO \tFinished loading data for 20 drivers: ['44', '33', '77', '4', '11', '16', '3', '55', '22', '18', '7', '99', '31', '63', '5', '47', '10', '6', '14', '9']\n",
      "core           INFO \tLoading data for United States Grand Prix - Race [v3.4.4]\n",
      "req            INFO \tUsing cached data for session_info\n",
      "req            INFO \tUsing cached data for driver_info\n",
      "logger      WARNING \tFailed to load result data from Ergast!\n",
      "core        WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n",
      "req            INFO \tUsing cached data for session_status_data\n",
      "req            INFO \tUsing cached data for lap_count\n",
      "req            INFO \tUsing cached data for track_status_data\n",
      "req            INFO \tUsing cached data for _extended_timing_data\n",
      "req            INFO \tUsing cached data for timing_app_data\n",
      "core           INFO \tProcessing timing data...\n",
      "core        WARNING \tDriver  7: Lap timing integrity check failed for 1 lap(s)\n",
      "Request for URL https://ergast.com/api/f1/2021/17/laps/1.json failed; using cached response\n",
      "Traceback (most recent call last):\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/requests_cache/session.py\", line 286, in _resend\n",
      "    response = self._send_and_cache(request, actions, cached_response, **kwargs)\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/requests_cache/session.py\", line 254, in _send_and_cache\n",
      "    response = super().send(request, **kwargs)\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/fastf1/req.py\", line 134, in send\n",
      "    lim.limit()\n",
      "  File \"/opt/homebrew/Caskroom/miniconda/base/envs/csci349/lib/python3.10/site-packages/fastf1/req.py\", line 104, in limit\n",
      "    raise RateLimitExceededError(self._info)\n",
      "fastf1.req.RateLimitExceededError: any API: 500 calls/h\n",
      "req            INFO \tUsing cached data for car_data\n",
      "req            INFO \tUsing cached data for position_data\n",
      "req            INFO \tUsing cached data for weather_data\n",
      "req            INFO \tUsing cached data for race_control_messages\n",
      "core           INFO \tFinished loading data for 20 drivers: ['33', '44', '11', '16', '55', '3', '4', '10', '77', '22', '31', '99', '18', '6', '7', '47', '9', '5', '14', '63']\n",
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Great Britain Race (2022): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Mexico Race (2022): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Brazil Race (2022): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Bahrain Race (2022): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with United States Race (2022): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Great Britain Race (2023): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Mexico Race (2023): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Brazil Race (2023): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Bahrain Race (2023): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with United States Race (2023): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Great Britain Race (2024): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Mexico Race (2024): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Brazil Race (2024): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with Bahrain Race (2024): Failed to load any schedule data.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "logger      WARNING \tFailed to load schedule from FastF1 backend!\n",
      "req            INFO \tNo cached data found for season_schedule. Loading data...\n",
      "_api           INFO \tFetching season schedule...\n",
      "logger      WARNING \tFailed to load schedule from F1 API backend!\n",
      "logger      WARNING \tFailed to load schedule from Ergast API backend!\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error with United States Race (2024): Failed to load any schedule data.\n",
      "Weather Data:\n",
      "                    Time  AirTemp  Humidity  Pressure  Rainfall  TrackTemp  \\\n",
      "0 0 days 00:00:44.848000     28.4      41.3    1007.7     False       51.1   \n",
      "1 0 days 00:01:44.867000     28.5      41.0    1007.8     False       51.1   \n",
      "2 0 days 00:02:44.883000     28.5      41.2    1007.7     False       51.3   \n",
      "3 0 days 00:03:44.881000     28.6      39.7    1007.7     False       51.3   \n",
      "4 0 days 00:04:44.882000     28.6      39.8    1007.8     False       51.8   \n",
      "\n",
      "   WindDirection  WindSpeed  Year Session  \n",
      "0            155        0.3  2021    Race  \n",
      "1            220        0.1  2021    Race  \n",
      "2            339        0.1  2021    Race  \n",
      "3             22        0.3  2021    Race  \n",
      "4            180        0.1  2021    Race  \n",
      "Lap Data:\n",
      "                    Time Driver DriverNumber LapTime  LapNumber  Stint  \\\n",
      "0 0 days 01:04:25.585000    VER           33     NaT        1.0    1.0   \n",
      "1 0 days 01:04:28.484000    HAM           44     NaT        1.0    1.0   \n",
      "2 0 days 01:07:06.686000    HAM           44     NaT        2.0    1.0   \n",
      "3 0 days 01:44:15.822000    HAM           44     NaT        3.0    2.0   \n",
      "4 0 days 01:46:53.038000    HAM           44     NaT        4.0    2.0   \n",
      "\n",
      "              PitOutTime              PitInTime            Sector1Time  \\\n",
      "0                    NaT                    NaT                    NaT   \n",
      "1                    NaT                    NaT                    NaT   \n",
      "2                    NaT 0 days 01:07:00.518000 0 days 00:00:43.800000   \n",
      "3 0 days 01:41:08.889000                    NaT                    NaT   \n",
      "4                    NaT                    NaT 0 days 00:01:33.760000   \n",
      "\n",
      "             Sector2Time  ...            LapStartDate TrackStatus Position  \\\n",
      "0                    NaT  ... 2021-07-18 14:03:22.569         124      NaN   \n",
      "1 0 days 00:00:40.610000  ... 2021-07-18 14:03:22.569         124      3.0   \n",
      "2 0 days 00:01:10.685000  ... 2021-07-18 14:05:28.494          45      2.0   \n",
      "3 0 days 00:01:15.154000  ... 2021-07-18 14:42:00.114           1      2.0   \n",
      "4 0 days 00:00:37.907000  ... 2021-07-18 14:45:15.832          12      2.0   \n",
      "\n",
      "  Deleted  DeletedReason  FastF1Generated  IsAccurate  Year          Event  \\\n",
      "0   False                            True       False  2021  Great Britain   \n",
      "1   False                           False       False  2021  Great Britain   \n",
      "2   False                           False       False  2021  Great Britain   \n",
      "3   False                           False       False  2021  Great Britain   \n",
      "4   False                           False       False  2021  Great Britain   \n",
      "\n",
      "  Session  \n",
      "0    Race  \n",
      "1    Race  \n",
      "2    Race  \n",
      "3    Race  \n",
      "4    Race  \n",
      "\n",
      "[5 rows x 34 columns]\n"
     ]
    }
   ],
   "source": [
    "# Data holders\n",
    "weather_data_list = []\n",
    "lap_data_list = []\n",
    "\n",
    "# Loop through years and sessions\n",
    "for year in years:\n",
    "    for event_name in events:   \n",
    "        for session_name in sessions:\n",
    "            try:\n",
    "                # Load the session\n",
    "                session = fastf1.get_session(year, event_name, session_name)\n",
    "                session.load()\n",
    "                \n",
    "                # Process weather data\n",
    "                weather_data = session.weather_data\n",
    "                weather_df = pd.DataFrame(weather_data)\n",
    "                weather_df['Year'] = year\n",
    "                weather_df['Session'] = session_name\n",
    "                weather_data_list.append(weather_df)\n",
    "\n",
    "                # Process lap data\n",
    "                lap_data = session.laps\n",
    "                lap_df = pd.DataFrame(lap_data)\n",
    "                lap_df['Year'] = year\n",
    "                lap_df['Event'] = event_name\n",
    "                lap_df['Session'] = session_name\n",
    "                lap_data_list.append(lap_df)\n",
    "        \n",
    "            except Exception as e:\n",
    "                print(f\"Error with {event_name} {session_name} ({year}): {e}\")\n",
    "\n",
    "# Combine weather and lap data into separate DataFrames\n",
    "if weather_data_list:\n",
    "    weather_data_combined = pd.concat(weather_data_list, ignore_index=True)\n",
    "    print(\"Weather Data:\")\n",
    "    print(weather_data_combined.head())\n",
    "\n",
    "if lap_data_list:\n",
    "    lap_data_combined = pd.concat(lap_data_list, ignore_index=True)\n",
    "    print(\"Lap Data:\")\n",
    "    print(lap_data_combined.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Convert lap times to seconds for easier modeling\n",
    "def convert_timedelta_to_seconds(td):\n",
    "    if pd.isna(td):\n",
    "        return None\n",
    "    return td.total_seconds()\n",
    "\n",
    "# Process lap times\n",
    "lap_data_combined['LapTime_seconds'] = lap_data_combined['LapTime'].apply(convert_timedelta_to_seconds)\n",
    "lap_data_combined['Sector1Time_seconds'] = lap_data_combined['Sector1Time'].apply(convert_timedelta_to_seconds)\n",
    "lap_data_combined['Sector2Time_seconds'] = lap_data_combined['Sector2Time'].apply(convert_timedelta_to_seconds)\n",
    "lap_data_combined['Sector3Time_seconds'] = lap_data_combined['Sector3Time'].apply(convert_timedelta_to_seconds)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                     Time Driver DriverNumber                LapTime  \\\n",
      "0 2024-01-01 00:39:08.419    MAZ            9                    NaT   \n",
      "1 2024-01-01 00:39:08.419    VER           33 0 days 00:01:58.245000   \n",
      "2 2024-01-01 00:39:09.712    HAM           44 0 days 00:01:59.538000   \n",
      "3 2024-01-01 00:39:12.872    LEC           16 0 days 00:02:02.698000   \n",
      "4 2024-01-01 00:39:13.946    BOT           77 0 days 00:02:03.772000   \n",
      "\n",
      "   LapNumber  Stint PitOutTime PitInTime Sector1Time            Sector2Time  \\\n",
      "0        1.0    1.0        NaT       NaT         NaT                    NaT   \n",
      "1        1.0    1.0        NaT       NaT         NaT 0 days 00:00:53.703000   \n",
      "2        1.0    1.0        NaT       NaT         NaT 0 days 00:00:54.367000   \n",
      "3        1.0    1.0        NaT       NaT         NaT 0 days 00:00:55.232000   \n",
      "4        1.0    1.0        NaT       NaT         NaT 0 days 00:00:56.397000   \n",
      "\n",
      "   ... Sector3Time_seconds AirTemp Humidity Pressure  Rainfall  TrackTemp  \\\n",
      "0  ...                 NaN    20.3     31.5    785.0     False       47.3   \n",
      "1  ...              32.579    20.3     31.5    785.0     False       47.3   \n",
      "2  ...              32.463    20.3     31.5    785.0     False       47.3   \n",
      "3  ...              34.383    20.3     31.5    785.0     False       47.3   \n",
      "4  ...              34.235    20.3     31.5    785.0     False       47.3   \n",
      "\n",
      "   WindDirection  WindSpeed Year_weather Session_weather  \n",
      "0            302        0.7         2021            Race  \n",
      "1            302        0.7         2021            Race  \n",
      "2            302        0.7         2021            Race  \n",
      "3            302        0.7         2021            Race  \n",
      "4            302        0.7         2021            Race  \n",
      "\n",
      "[5 rows x 47 columns]\n"
     ]
    }
   ],
   "source": [
    "# Merge weather data with lap data\n",
    "# Convert timedelta to datetime for merging\n",
    "def convert_time_for_merge(df, base_date='2024-01-01'):\n",
    "    if 'Time' in df.columns:\n",
    "        # Create a base datetime and add the timedelta\n",
    "        base = pd.Timestamp(base_date)\n",
    "        if df['Time'].dtype == 'timedelta64[ns]':\n",
    "            df['Time'] = base + df['Time']\n",
    "    return df\n",
    "\n",
    "# Process both dataframes\n",
    "weather_data_combined = convert_time_for_merge(weather_data_combined)\n",
    "lap_data_combined = convert_time_for_merge(lap_data_combined)\n",
    "\n",
    "# Now merge based on nearest timestamp\n",
    "merged_data = pd.merge_asof(\n",
    "    lap_data_combined.sort_values('Time'),\n",
    "    weather_data_combined.sort_values('Time'),\n",
    "    on='Time',\n",
    "    suffixes=('_lap', '_weather')\n",
    ")\n",
    "\n",
    "print(merged_data.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Index(['Time', 'Driver', 'DriverNumber', 'LapTime', 'LapNumber', 'Stint',\n",
      "       'PitOutTime', 'PitInTime', 'Sector1Time', 'Sector2Time', 'Sector3Time',\n",
      "       'Sector1SessionTime', 'Sector2SessionTime', 'Sector3SessionTime',\n",
      "       'SpeedI1', 'SpeedI2', 'SpeedFL', 'SpeedST', 'IsPersonalBest',\n",
      "       'Compound', 'TyreLife', 'FreshTyre', 'Team', 'LapStartTime',\n",
      "       'LapStartDate', 'TrackStatus', 'Position', 'Deleted', 'DeletedReason',\n",
      "       'FastF1Generated', 'IsAccurate', 'Year_lap', 'Event', 'Session_lap',\n",
      "       'LapTime_seconds', 'Sector1Time_seconds', 'Sector2Time_seconds',\n",
      "       'Sector3Time_seconds', 'AirTemp', 'Humidity', 'Pressure', 'Rainfall',\n",
      "       'TrackTemp', 'WindDirection', 'WindSpeed', 'Year_weather',\n",
      "       'Session_weather'],\n",
      "      dtype='object')\n"
     ]
    }
   ],
   "source": [
    "# Select features for modeling\n",
    "feature_columns = [\n",
    "    'TrackTemp', 'AirTemp', 'Humidity', 'Pressure', 'WindSpeed',\n",
    "    'TyreLife', 'SpeedI1', 'SpeedI2', 'SpeedFL', 'SpeedST'\n",
    "]\n",
    "\n",
    "target_column = 'LapTime_seconds'\n",
    "\n",
    "# Print available columns\n",
    "print(merged_data.columns)\n",
    "\n",
    "# Remove rows with missing values\n",
    "model_data = merged_data[feature_columns + [target_column]].dropna()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Split the data into training and testing sets\n",
    "X = model_data[feature_columns]\n",
    "y = model_data[target_column]\n",
    "\n",
    "X_train, X_test, y_train, y_test = train_test_split(\n",
    "    X, y, test_size=0.2, random_state=42\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Scale the features\n",
    "scaler = StandardScaler()\n",
    "X_train_scaled = scaler.fit_transform(X_train)\n",
    "X_test_scaled = scaler.transform(X_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Train and evaluate multiple models\n",
    "\n",
    "# Initialize models\n",
    "models = {\n",
    "    'Linear Regression': LinearRegression(),\n",
    "    'Random Forest': RandomForestRegressor(n_estimators=100, random_state=42),\n",
    "    'Decision Tree': DecisionTreeRegressor(random_state=42),\n",
    "    'Gradient Boosting': GradientBoostingRegressor(n_estimators=100, random_state=42),\n",
    "    'Support Vector Regressor': SVR(kernel='rbf', C=1.0, epsilon=0.1),\n",
    "    'XGBoost': xgb.XGBRegressor(n_estimators=100, random_state=42)\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Linear Regression model trained.\n",
      "Predictions made with Linear Regression model.\n",
      "Metrics calculated with Linear Regression model.\n",
      "Random Forest model trained.\n",
      "Predictions made with Random Forest model.\n",
      "Metrics calculated with Random Forest model.\n",
      "Decision Tree model trained.\n",
      "Predictions made with Decision Tree model.\n",
      "Metrics calculated with Decision Tree model.\n",
      "Gradient Boosting model trained.\n",
      "Predictions made with Gradient Boosting model.\n",
      "Metrics calculated with Gradient Boosting model.\n",
      "Support Vector Regressor model trained.\n",
      "Predictions made with Support Vector Regressor model.\n",
      "Metrics calculated with Support Vector Regressor model.\n",
      "XGBoost model trained.\n",
      "Predictions made with XGBoost model.\n",
      "Metrics calculated with XGBoost model.\n"
     ]
    }
   ],
   "source": [
    "# Train and evaluate each model\n",
    "results = {}\n",
    "for name, model in models.items():\n",
    "    # Train the model\n",
    "    model.fit(X_train_scaled, y_train)\n",
    "    print(f\"{name} model trained.\")\n",
    "    \n",
    "    # Make predictions\n",
    "    y_pred = model.predict(X_test_scaled)\n",
    "    print(f\"Predictions made with {name} model.\")\n",
    "    \n",
    "    # Calculate metrics\n",
    "    mse = mean_squared_error(y_test, y_pred)\n",
    "    rmse = np.sqrt(mse)\n",
    "    r2 = r2_score(y_test, y_pred)\n",
    "    print(f\"Metrics calculated with {name} model.\")\n",
    "    \n",
    "    results[name] = {\n",
    "        'RMSE': rmse,\n",
    "        'R2': r2\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Model Performance:\n",
      "\n",
      "Linear Regression:\n",
      "RMSE: 7.33 seconds\n",
      "R2 Score: 0.709\n",
      "\n",
      "Random Forest:\n",
      "RMSE: 2.48 seconds\n",
      "R2 Score: 0.967\n",
      "\n",
      "Decision Tree:\n",
      "RMSE: 3.42 seconds\n",
      "R2 Score: 0.937\n",
      "\n",
      "Gradient Boosting:\n",
      "RMSE: 2.93 seconds\n",
      "R2 Score: 0.953\n",
      "\n",
      "Support Vector Regressor:\n",
      "RMSE: 7.51 seconds\n",
      "R2 Score: 0.694\n",
      "\n",
      "XGBoost:\n",
      "RMSE: 2.79 seconds\n",
      "R2 Score: 0.958\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 86280 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Print results\n",
    "print(\"\\nModel Performance:\")\n",
    "for name, metrics in results.items():\n",
    "    print(f\"\\n{name}:\")\n",
    "    print(f\"RMSE: {metrics['RMSE']:.2f} seconds\")\n",
    "    print(f\"R2 Score: {metrics['R2']:.3f}\")\n",
    "\n",
    "# Visualize predictions vs actual values for the best model\n",
    "plt.figure(figsize=(10, 6))\n",
    "best_model_name = max(results.items(), key=lambda x: x[1]['R2'])[0]\n",
    "best_model = models[best_model_name]\n",
    "y_pred = best_model.predict(X_test_scaled)\n",
    "\n",
    "plt.scatter(y_test, y_pred, alpha=0.5)\n",
    "plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)\n",
    "plt.xlabel('Actual Lap Time (seconds)')\n",
    "plt.ylabel('Predicted Lap Time (seconds)')\n",
    "plt.title(f'Actual vs Predicted Lap Times\\n{best_model_name}')\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 36840 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Feature importance for Random Forest\n",
    "if 'Random Forest' in models:\n",
    "    rf_model = models['Random Forest']\n",
    "    feature_importance = pd.DataFrame({\n",
    "        'feature': feature_columns,\n",
    "        'importance': rf_model.feature_importances_\n",
    "    }).sort_values('importance', ascending=False)\n",
    "    \n",
    "    plt.figure(figsize=(10, 6))\n",
    "    sns.barplot(x='importance', y='feature', data=feature_importance)\n",
    "    plt.title('Feature Importance in Predicting Lap Times')\n",
    "    plt.tight_layout()\n",
    "    plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "csci349",
   "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.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
