{ "cells": [ { "cell_type": "markdown", "id": "md_0", "metadata": {}, "source": [ "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/f1_miami_strategy_oracle_26ai.ipynb) [![Oracle AI Database](https://img.shields.io/badge/Oracle-AI%20Database%2026ai-C74634?style=flat-square&logo=oracle)](https://www.oracle.com/database/) [![FastF1](https://img.shields.io/badge/Data-FastF1-E8002D?style=flat-square)](https://docs.fastf1.dev/) [![License: UPL](https://img.shields.io/badge/license-UPL-green?style=flat-square)](https://opensource.org/licenses/UPL)\n", "\n", "# \ud83c\udfce\ufe0f F1 Miami GP \u2014 Race Strategy Intelligence with Oracle AI Database 26ai\n", "\n", "**Author:** [Mariana Antaya](https://www.instagram.com/mar_antaya/) | **Notebook:** `f1_miami_strategy_oracle_26ai.ipynb`\n", "\n", "> *\"Miami 2026 is in 18 days. I wanted to answer a simple question: what tyre strategy actually wins here? Four years of data, four Oracle paradigms, one database.\"*\n", "\n", "---\n", "\n", "## What you'll learn\n", "\n", "This notebook walks through four Oracle AI Database 26ai paradigms using **real Formula 1 data** from the FastF1 API \u2014 no synthetic datasets, no toy examples.\n", "\n", "| # | Paradigm | Oracle Feature | F1 Question |\n", "|---|---|---|---|\n", "| 1 | **Relational SQL** | CTEs, window functions | What tyre strategy wins at Miami? (2022\u20132025) |\n", "| 2 | **Hybrid Search** | `VECTOR_DISTANCE()` + `CONTAINS()` + RRF | Which past races *felt* like Miami? |\n", "| 3 | **JSON Document** | `CLOB IS JSON` + `JSON_VALUE()` | What were the race conditions? |\n", "| 4 | **Property Graph** | `GRAPH_TABLE()` | Which drivers share team history? |\n", "| \u26a1 | **Convergent** | All four combined | Full race brief, one function call |\n", "\n", "### Why this matters for developers\n", "\n", "Answering these questions normally requires four completely separate databases \u2014 a relational DB for lap times, a vector store for semantic search, a graph DB for relationships, and a document store for unstructured metadata. **Oracle AI Database 26ai does all of it in one.** Same connection, same query language, no glue code.\n", "\n", "---\n", "\n", "## Prerequisites\n", "\n", "- [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed and running\n", "- Python 3.9+\n", "- No API keys required \u2014 all embeddings run locally with `sentence-transformers`\n", "\n", "> **\u26a0\ufe0f Google Colab note:** Colab does not support Docker in its hosted runtime.\n", "> To run this notebook in Colab, use the **Oracle Autonomous Database Free Tier**:\n", "> 1. Sign up at [oracle.com/autonomous-database](https://www.oracle.com/autonomous-database/free-trial/)\n", "> 2. Download your wallet zip file\n", "> 3. Replace the `connect_oracle()` call with your wallet credentials\n", ">\n", "> For the full local experience with Docker, run this notebook locally in VS Code or JupyterLab.\n" ] }, { "cell_type": "markdown", "id": "md_1", "metadata": {}, "source": [ "---\n", "## Section 1: Environment Setup" ] }, { "cell_type": "code", "execution_count": 1, "id": "code_2", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:41:42.463974Z", "iopub.status.busy": "2026-04-15T10:41:42.463762Z", "iopub.status.idle": "2026-04-15T10:41:42.985826Z", "shell.execute_reply": "2026-04-15T10:41:42.985271Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u2705 All packages installed\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49m/Users/marianaantaya/Downloads/f1-notebook/venv/bin/python3 -m pip install --upgrade pip\u001b[0m\n" ] } ], "source": [ "import subprocess, sys, sysconfig\n", "\n", "PACKAGES = [\n", " \"oracledb==2.4.1\",\n", " \"fastf1==3.4.0\",\n", " \"sentence-transformers==3.0.1\",\n", " \"pandas==2.2.2\",\n", " \"numpy==1.26.4\",\n", " \"plotly==5.22.0\",\n", "]\n", "\n", "# Auto-detect if --break-system-packages is needed (PEP 668 / externally-managed)\n", "cmd = [sys.executable, \"-m\", \"pip\", \"install\", \"-q\"]\n", "marker = sysconfig.get_path(\"stdlib\")\n", "if marker and \"site-packages\" not in (getattr(sys, \"prefix\", \"\") or \"\"):\n", " # Check for EXTERNALLY-MANAGED marker (PEP 668)\n", " import pathlib\n", " ext_managed = any(\n", " (pathlib.Path(p) / \"EXTERNALLY-MANAGED\").exists()\n", " for p in [sysconfig.get_path(\"stdlib\"), sysconfig.get_path(\"platstdlib\")]\n", " if p\n", " )\n", " if ext_managed:\n", " cmd.append(\"--break-system-packages\")\n", "subprocess.check_call(cmd + PACKAGES)\n", "print(\"\u2705 All packages installed\")" ] }, { "cell_type": "markdown", "id": "md_3", "metadata": {}, "source": [ "---\n", "### Start Oracle AI Database 26ai (Docker)\n", "\n", "The cell below automates the full Oracle lifecycle:\n", "1. Pulls `ghcr.io/gvenzl/oracle-free:23.26.0` if not cached (~800MB, one-time)\n", "2. Starts the container, waits until `FREEPDB1` is open read/write\n", "3. Creates a dedicated `F1` user with `DB_DEVELOPER_ROLE` and Oracle Text privileges\n", "\n", "> **First pull:** ~2\u20133 minutes. Subsequent runs reuse the existing container and are instant." ] }, { "cell_type": "code", "execution_count": 2, "id": "code_4", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:41:42.988009Z", "iopub.status.busy": "2026-04-15T10:41:42.987889Z", "iopub.status.idle": "2026-04-15T10:41:45.832961Z", "shell.execute_reply": "2026-04-15T10:41:45.832322Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u2705 Container 'f1_oracle' already running\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u2705 Vector memory already 256M\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u2705 F1 user created with DB_DEVELOPER_ROLE + Oracle Text privileges\n" ] } ], "source": [ "import subprocess, time\n", "\n", "CONTAINER = \"f1_oracle\"\n", "PORT = 1521\n", "ADMIN_PWD = \"F1Oracle26ai!\"\n", "F1_PWD = \"F1Str4tegy!\"\n", "PDB = \"FREEPDB1\"\n", "IMAGE = \"container-registry.oracle.com/database/free:latest\"\n", "\n", "def sh(*args, check=True):\n", " return subprocess.run(list(args), capture_output=True, text=True, check=check)\n", "\n", "def oracle_is_open():\n", " \"\"\"Check if FREEPDB1 is open by writing a temp SQL file \u2014 avoids shell escaping.\"\"\"\n", " probe = sh(\n", " \"docker\", \"exec\", CONTAINER, \"bash\", \"-c\",\n", " \"cat > /tmp/check_pdb.sql << 'SQLEOF'\\n\"\n", " \"SELECT OPEN_MODE FROM V$PDBS WHERE NAME='FREEPDB1';\\n\"\n", " \"EXIT;\\n\"\n", " \"SQLEOF\\n\"\n", " \"sqlplus -s / as sysdba @/tmp/check_pdb.sql\",\n", " check=False,\n", " )\n", " return probe.returncode == 0 and \"READ WRITE\" in probe.stdout.upper()\n", "\n", "def wait_ready(timeout_s=600, poll_s=12):\n", " start = time.time()\n", " while time.time() - start < timeout_s:\n", " if oracle_is_open():\n", " return True\n", " print(f\" \u23f3 Waiting ({int(time.time()-start)}s)...\", end=\"\\r\")\n", " time.sleep(poll_s)\n", " raise TimeoutError(f\"Oracle did not open within {timeout_s}s\")\n", "\n", "def create_f1_user():\n", " \"\"\"Create F1 user via heredoc SQL file \u2014 avoids all shell escaping issues.\"\"\"\n", " sql_script = (\n", " f\"cat > /tmp/create_f1.sql << 'SQLEOF'\\n\"\n", " f\"ALTER SESSION SET CONTAINER={PDB};\\n\"\n", " f\"BEGIN\\n\"\n", " f\" EXECUTE IMMEDIATE 'DROP USER f1 CASCADE';\\n\"\n", " f\"EXCEPTION WHEN OTHERS THEN NULL;\\n\"\n", " f\"END;\\n\"\n", " f\"/\\n\"\n", " f\"CREATE USER f1 IDENTIFIED BY \\\"{F1_PWD}\\\"\\n\"\n", " f\" DEFAULT TABLESPACE users QUOTA UNLIMITED ON users;\\n\"\n", " f\"GRANT DB_DEVELOPER_ROLE TO f1;\\n\"\n", " f\"GRANT CREATE SESSION TO f1;\\n\"\n", " f\"GRANT EXECUTE ON CTXSYS.CTX_DDL TO f1;\\n\"\n", " f\"EXIT;\\n\"\n", " f\"SQLEOF\\n\"\n", " f\"sqlplus -s \\\"sys/{ADMIN_PWD} as sysdba\\\" @/tmp/create_f1.sql\"\n", " )\n", " result = sh(\"docker\", \"exec\", CONTAINER, \"bash\", \"-c\", sql_script, check=False)\n", " if \"Grant succeeded\" in result.stdout or \"User created\" in result.stdout:\n", " print(\"\u2705 F1 user created with DB_DEVELOPER_ROLE + Oracle Text privileges\")\n", " elif \"already exists\" in result.stdout.lower():\n", " print(\"\u2705 F1 user already exists\")\n", " else:\n", " # Verify directly\n", " verify = sh(\"docker\", \"exec\", CONTAINER, \"bash\", \"-c\",\n", " f\"cat > /tmp/verify_f1.sql << 'SQLEOF'\\n\"\n", " f\"ALTER SESSION SET CONTAINER={PDB};\\n\"\n", " f\"SELECT USERNAME FROM DBA_USERS WHERE USERNAME='F1';\\n\"\n", " f\"EXIT;\\n\"\n", " f\"SQLEOF\\n\"\n", " f\"sqlplus -s / as sysdba @/tmp/verify_f1.sql\",\n", " check=False)\n", " if \"F1\" in verify.stdout:\n", " print(\"\u2705 F1 user verified\")\n", " else:\n", " print(f\"\u26a0\ufe0f User creation may have failed. Trying as sysdba locally...\")\n", " # Fallback: connect as sysdba locally (no password needed)\n", " sh(\"docker\", \"exec\", CONTAINER, \"bash\", \"-c\",\n", " f\"cat > /tmp/create_f1_v2.sql << 'SQLEOF'\\n\"\n", " f\"ALTER SESSION SET CONTAINER={PDB};\\n\"\n", " f\"CREATE USER f1 IDENTIFIED BY \\\"{F1_PWD}\\\"\\n\"\n", " f\" DEFAULT TABLESPACE users QUOTA UNLIMITED ON users;\\n\"\n", " f\"GRANT DB_DEVELOPER_ROLE TO f1;\\n\"\n", " f\"GRANT CREATE SESSION TO f1;\\n\"\n", " f\"GRANT EXECUTE ON CTXSYS.CTX_DDL TO f1;\\n\"\n", " f\"EXIT;\\n\"\n", " f\"SQLEOF\\n\"\n", " f\"sqlplus -s / as sysdba @/tmp/create_f1_v2.sql\",\n", " check=False)\n", " print(\"\u2705 F1 user created via sysdba fallback\")\n", "\n", "def configure_vector_memory(target_mb=256):\n", " \"\"\"Enable HNSW vector indexes by allocating vector_memory_size in SGA.\"\"\"\n", " check = sh(\"docker\", \"exec\", CONTAINER, \"bash\", \"-c\",\n", " \"echo -e \\\"SHOW PARAMETER vector_memory_size;\\nEXIT;\\\" | sqlplus -s / as sysdba\",\n", " check=False)\n", " # Parse current value \u2014 look for a line like \"vector_memory_size ... 256M\"\n", " for line in check.stdout.splitlines():\n", " if \"vector_memory_size\" in line and f\"{target_mb}M\" in line:\n", " print(f\"\u2705 Vector memory already {target_mb}M\")\n", " return\n", " print(f\"\u2699\ufe0f Setting vector_memory_size={target_mb}M (requires DB bounce)...\")\n", " sh(\"docker\", \"exec\", CONTAINER, \"bash\", \"-c\",\n", " f\"echo -e \\\"ALTER SYSTEM SET vector_memory_size={target_mb}M SCOPE=SPFILE;\\n\"\n", " f\"SHUTDOWN IMMEDIATE;\\nSTARTUP;\\nEXIT;\\\" | sqlplus -s / as sysdba\",\n", " check=False)\n", " # Wait for DB to come back up after bounce\n", " wait_ready()\n", " print(f\"\u2705 Vector memory set to {target_mb}M \u2014 HNSW indexes enabled\")\n", "\n", "def setup_oracle():\n", " ps = sh(\"docker\", \"ps\", \"--filter\", f\"name=^{CONTAINER}$\",\n", " \"--format\", \"{{.Names}}\", check=False)\n", " if CONTAINER in ps.stdout:\n", " print(f\"\u2705 Container '{CONTAINER}' already running\")\n", " configure_vector_memory()\n", " create_f1_user()\n", " return\n", " ps_a = sh(\"docker\", \"ps\", \"-a\", \"--filter\", f\"name=^{CONTAINER}$\",\n", " \"--format\", \"{{.Names}}\", check=False)\n", " if CONTAINER in ps_a.stdout:\n", " print(f\"\u25b6\ufe0f Starting existing container...\")\n", " sh(\"docker\", \"start\", CONTAINER)\n", " else:\n", " print(f\"\ud83d\udc33 Starting {IMAGE} (first pull ~2-3 min)...\")\n", " sh(\"docker\", \"run\", \"-d\",\n", " \"--name\", CONTAINER,\n", " \"-p\", f\"{PORT}:{PORT}\",\n", " \"-e\", f\"ORACLE_PASSWORD={ADMIN_PWD}\",\n", " IMAGE)\n", " print(\"\u23f3 Waiting for FREEPDB1...\")\n", " wait_ready()\n", " print(\"\\n\u2705 Oracle AI Database 26ai is ready\")\n", " configure_vector_memory()\n", " create_f1_user()\n", "\n", "setup_oracle()\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "code_5", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:41:45.834658Z", "iopub.status.busy": "2026-04-15T10:41:45.834555Z", "iopub.status.idle": "2026-04-15T10:41:45.996277Z", "shell.execute_reply": "2026-04-15T10:41:45.995706Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u2705 Connected: Oracle AI Database 26ai Free Release 23.26.1.0.0 - Develop, Learn, and Run for Free\n", "Version 23.26.1.0.0\n" ] } ], "source": [ "import oracledb, time\n", "\n", "DSN = f\"127.0.0.1:{PORT}/{PDB}\"\n", "\n", "def connect_oracle(user=\"f1\", password=F1_PWD, dsn=DSN,\n", " max_retries=5, retry_delay=8):\n", " \"\"\"Connect with retry \u2014 Oracle may still be warming internal services.\"\"\"\n", " for attempt in range(max_retries):\n", " try:\n", " conn = oracledb.connect(\n", " user=user, password=password, dsn=dsn\n", " )\n", " cur = conn.cursor()\n", " cur.execute(\"SELECT BANNER_FULL FROM V$VERSION\")\n", " print(f\"\u2705 Connected: {cur.fetchone()[0]}\")\n", " cur.close()\n", " return conn\n", " except Exception as e:\n", " if attempt < max_retries - 1:\n", " print(f\" Retry {attempt+1}/{max_retries}: {e}\")\n", " time.sleep(retry_delay)\n", " else:\n", " raise RuntimeError(\n", " f\"Could not connect after {max_retries} attempts. \"\n", " \"Ensure the Docker setup cell above ran successfully first.\"\n", " ) from e\n", "\n", "CONN = connect_oracle()" ] }, { "cell_type": "markdown", "id": "md_6", "metadata": {}, "source": [ "---\n", "## Section 2: Schema \u2014 Four Paradigms in One Database\n", "\n", "Five tables covering all four Oracle AI Database 26ai paradigms.\n", "\n", "The critical column is `embedding VECTOR(384, FLOAT32)` in `race_events`.\n", "This is a **native Oracle 26ai vector type** \u2014 no external vector database needed.\n", "It lives in the same table as the message text, enabling hybrid search in a single SQL query.\n", "\n", "```\n", "race_laps \u2192 Paradigm 1: Relational SQL\n", "pit_stops \u2192 Paradigm 1: Relational SQL\n", "race_events \u2192 Paradigm 2: Hybrid (VECTOR column + Oracle Text index + HNSW index)\n", "race_metadata \u2192 Paradigm 3: JSON Document (CLOB IS JSON + JSON_VALUE)\n", "driver_teams \u2192 Paradigm 4: Property Graph edge table\n", "team_circuits \u2192 Paradigm 4: Property Graph edge table\n", "```" ] }, { "cell_type": "code", "execution_count": 4, "id": "code_7", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:41:45.998258Z", "iopub.status.busy": "2026-04-15T10:41:45.998041Z", "iopub.status.idle": "2026-04-15T10:41:47.694667Z", "shell.execute_reply": "2026-04-15T10:41:47.693220Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " \u26a0\ufe0f ORA-42404: graph element table name DRIVER_TEAMS already defined\n", "Help: https://docs.oracle.com/error-help/db/ora-42404/\n", "\u2705 Schema created:\n", " race_laps, pit_stops \u2192 Paradigm 1: Relational SQL\n", " race_events \u2192 Paradigm 2: Hybrid (VECTOR + Oracle Text)\n", " race_metadata \u2192 Paradigm 3: JSON Document\n", " driver_career graph \u2192 Paradigm 4: Property Graph\n" ] } ], "source": [ "def run_ddl(conn, statements):\n", " cur = conn.cursor()\n", " for stmt in statements:\n", " try:\n", " cur.execute(stmt)\n", " conn.commit()\n", " except oracledb.DatabaseError as e:\n", " code = e.args[0].code if e.args else 0\n", " # Ignore: table does not exist (942), object does not exist (4043)\n", " # 942: table does not exist 4043: object does not exist\n", " # 439: feature not enabled 955: name already used\n", " if code not in (942, 4043, 439, 955):\n", " print(f\" \u26a0\ufe0f {str(e)[:120]}\")\n", " cur.close()\n", "\n", "SCHEMA = [\n", " # Drop existing objects cleanly\n", " \"BEGIN EXECUTE IMMEDIATE 'DROP TABLE race_events'; EXCEPTION WHEN OTHERS THEN NULL; END;\",\n", " \"BEGIN EXECUTE IMMEDIATE 'DROP TABLE pit_stops'; EXCEPTION WHEN OTHERS THEN NULL; END;\",\n", " \"BEGIN EXECUTE IMMEDIATE 'DROP TABLE race_laps'; EXCEPTION WHEN OTHERS THEN NULL; END;\",\n", " \"BEGIN EXECUTE IMMEDIATE 'DROP TABLE race_metadata'; EXCEPTION WHEN OTHERS THEN NULL; END;\",\n", " \"BEGIN EXECUTE IMMEDIATE 'DROP TABLE team_circuits'; EXCEPTION WHEN OTHERS THEN NULL; END;\",\n", " \"BEGIN EXECUTE IMMEDIATE 'DROP TABLE driver_teams'; EXCEPTION WHEN OTHERS THEN NULL; END;\",\n", "\n", " # \u2500\u2500 Paradigm 1: Relational \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " \"\"\"CREATE TABLE race_laps (\n", " id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n", " season NUMBER(4) NOT NULL,\n", " race_name VARCHAR2(100) NOT NULL,\n", " circuit VARCHAR2(100),\n", " driver VARCHAR2(10) NOT NULL,\n", " team VARCHAR2(80),\n", " lap_number NUMBER(3),\n", " lap_time_s FLOAT,\n", " position NUMBER(2),\n", " compound VARCHAR2(12),\n", " tyre_life NUMBER(3),\n", " is_personal_best NUMBER(1) DEFAULT 0,\n", " is_pit_in_lap NUMBER(1) DEFAULT 0, -- 1 if driver pitted this lap\n", " is_pit_out_lap NUMBER(1) DEFAULT 0 -- 1 if driver exited pits this lap\n", " )\"\"\",\n", "\n", " \"\"\"CREATE TABLE pit_stops (\n", " id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n", " season NUMBER(4) NOT NULL,\n", " race_name VARCHAR2(100) NOT NULL,\n", " circuit VARCHAR2(100),\n", " driver VARCHAR2(10) NOT NULL,\n", " team VARCHAR2(80),\n", " stop_number NUMBER(2),\n", " lap_in NUMBER(3), -- lap on which driver entered pits\n", " compound_in VARCHAR2(12), -- tyre fitted AFTER this stop\n", " compound_out VARCHAR2(12), -- tyre removed AT this stop\n", " stop_duration_s FLOAT -- stationary time (seconds)\n", " )\"\"\",\n", "\n", " # \u2500\u2500 Paradigm 2: Hybrid \u2014 Vector + Full-Text \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " # VECTOR(384, FLOAT32): native Oracle 26ai vector column\n", " # Oracle Text index on .message: enables CONTAINS() keyword search\n", " # HNSW vector index on .embedding: enables VECTOR_DISTANCE() semantic search\n", " # Both on the SAME table \u2014 the converged database advantage\n", " \"\"\"CREATE TABLE race_events (\n", " id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n", " season NUMBER(4) NOT NULL,\n", " race_name VARCHAR2(100) NOT NULL,\n", " circuit VARCHAR2(100),\n", " lap_number NUMBER(3),\n", " event_type VARCHAR2(50),\n", " message VARCHAR2(1000) NOT NULL,\n", " embedding VECTOR(384, FLOAT32)\n", " )\"\"\",\n", "\n", " # \u2500\u2500 Paradigm 3: JSON Document \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " \"\"\"CREATE TABLE race_metadata (\n", " id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n", " season NUMBER(4) NOT NULL,\n", " race_name VARCHAR2(100) NOT NULL,\n", " circuit VARCHAR2(100),\n", " race_context CLOB NOT NULL,\n", " CONSTRAINT ck_meta_json CHECK (race_context IS JSON)\n", " )\"\"\",\n", "\n", " # \u2500\u2500 Paradigm 4: Property Graph edge tables \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " \"\"\"CREATE TABLE driver_teams (\n", " id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n", " driver VARCHAR2(10) NOT NULL,\n", " team VARCHAR2(80) NOT NULL,\n", " season NUMBER(4) NOT NULL,\n", " wins NUMBER(3) DEFAULT 0,\n", " podiums NUMBER(3) DEFAULT 0,\n", " CONSTRAINT uq_dt UNIQUE (driver, team, season)\n", " )\"\"\",\n", " \"\"\"CREATE TABLE team_circuits (\n", " id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n", " team VARCHAR2(80) NOT NULL,\n", " circuit VARCHAR2(100) NOT NULL,\n", " season NUMBER(4) NOT NULL,\n", " best_finish NUMBER(2) DEFAULT 20,\n", " CONSTRAINT uq_tc UNIQUE (team, circuit, season)\n", " )\"\"\",\n", "\n", " # Performance indexes\n", " \"CREATE INDEX idx_laps_race ON race_laps(season, race_name)\",\n", " \"CREATE INDEX idx_laps_driver ON race_laps(driver, season)\",\n", " \"CREATE INDEX idx_pits_race ON pit_stops(season, race_name)\",\n", " \"CREATE INDEX idx_events_race ON race_events(season, race_name)\",\n", "\n", " # Oracle Text index \u2014 enables CONTAINS() keyword search on message column\n", " \"\"\"CREATE INDEX idx_events_text ON race_events(message)\n", " INDEXTYPE IS CTXSYS.CONTEXT\n", " PARAMETERS ('SYNC (ON COMMIT)')\"\"\",\n", "\n", " # HNSW approximate nearest-neighbour vector index\n", " # Same algorithm as Pinecone/Chroma \u2014 running natively inside Oracle\n", " # TARGET ACCURACY 95 = 95% recall guarantee at query time\n", " \"\"\"CREATE VECTOR INDEX idx_events_vec ON race_events(embedding)\n", " ORGANIZATION INMEMORY NEIGHBOR GRAPH\n", " DISTANCE COSINE\n", " WITH TARGET ACCURACY 95\"\"\",\n", "\n", " # SQL Property Graph \u2014 driver career network\n", " # Enables GRAPH_TABLE() traversal without Neo4j\n", " \"\"\"CREATE PROPERTY GRAPH driver_career\n", " VERTEX TABLES (\n", " driver_teams\n", " KEY (driver) LABEL Driver\n", " PROPERTIES (driver, wins, podiums),\n", " driver_teams AS team_vertex\n", " KEY (team) LABEL Team\n", " PROPERTIES (team),\n", " team_circuits AS circuit_vertex\n", " KEY (circuit) LABEL Circuit\n", " PROPERTIES (circuit)\n", " )\n", " EDGE TABLES (\n", " driver_teams\n", " KEY (id)\n", " SOURCE KEY (driver) REFERENCES driver_teams(driver)\n", " DESTINATION KEY (team) REFERENCES driver_teams(team)\n", " LABEL DROVE_FOR PROPERTIES (season, wins),\n", " team_circuits\n", " KEY (id)\n", " SOURCE KEY (team) REFERENCES driver_teams(team)\n", " DESTINATION KEY (circuit) REFERENCES team_circuits(circuit)\n", " LABEL RACED_AT PROPERTIES (season, best_finish)\n", " )\"\"\",\n", "]\n", "\n", "run_ddl(CONN, SCHEMA)\n", "print(\"\u2705 Schema created:\")\n", "print(\" race_laps, pit_stops \u2192 Paradigm 1: Relational SQL\")\n", "print(\" race_events \u2192 Paradigm 2: Hybrid (VECTOR + Oracle Text)\")\n", "print(\" race_metadata \u2192 Paradigm 3: JSON Document\")\n", "print(\" driver_career graph \u2192 Paradigm 4: Property Graph\")" ] }, { "cell_type": "markdown", "id": "md_8", "metadata": {}, "source": [ "---\n", "## Section 3: Embeddings\n", "\n", "`all-MiniLM-L6-v2` encodes text as 384-dimensional float32 vectors.\n", "Sentences with similar *meaning* land close together in vector space \u2014\n", "so `\"SAFETY CAR DEPLOYED LAP 47\"` and `\"VSC CALLED LAP 51\"` are nearby\n", "even though the words differ completely.\n", "\n", "Oracle stores these vectors natively in the `VECTOR(384, FLOAT32)` column.\n", "No serialisation, no external service \u2014 the `oracledb` driver accepts\n", "`array.array('f', ...)` directly as a bind variable." ] }, { "cell_type": "code", "execution_count": 5, "id": "code_9", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:41:47.698584Z", "iopub.status.busy": "2026-04-15T10:41:47.698271Z", "iopub.status.idle": "2026-04-15T10:41:54.162893Z", "shell.execute_reply": "2026-04-15T10:41:54.162231Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/marianaantaya/Downloads/f1-notebook/venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", " from tqdm.autonotebook import tqdm, trange\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u23f3 Loading all-MiniLM-L6-v2 (downloads once, ~90MB)...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u2705 Embedding model ready \u2014 384 dimensions, runs locally\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Sample: 384 dims | first 3 values: [0.0019892840646207333, 0.07707614451646805, -0.0509980171918869]\n" ] } ], "source": [ "import array\n", "from sentence_transformers import SentenceTransformer\n", "\n", "print(\"\u23f3 Loading all-MiniLM-L6-v2 (downloads once, ~90MB)...\")\n", "_MODEL = SentenceTransformer(\"all-MiniLM-L6-v2\")\n", "print(\"\u2705 Embedding model ready \u2014 384 dimensions, runs locally\")\n", "\n", "def to_vec(text: str) -> array.array:\n", " \"\"\"Encode text to Oracle-compatible VECTOR(384, FLOAT32).\"\"\"\n", " v = _MODEL.encode(text, normalize_embeddings=True)\n", " return array.array(\"f\", v.tolist())\n", "\n", "sample = to_vec(\"Safety car deployed lap 47\")\n", "print(f\"Sample: {len(sample)} dims | first 3 values: {list(sample[:3])}\")" ] }, { "cell_type": "markdown", "id": "md_10", "metadata": {}, "source": [ "---\n", "## Section 4: Data Ingestion \u2014 FastF1 \u2192 Oracle AI Database 26ai\n", "\n", "FastF1 pulls directly from F1's official live timing service.\n", "For each race session we populate all four paradigm tables.\n", "\n", "### Fix note \u2014 pit stop detection\n", "A common mistake is inferring pit stops from compound *changes* between laps.\n", "This is unreliable because FastF1 sometimes fills in missing compound data,\n", "creating false transitions.\n", "\n", "**This notebook uses `PitInTime` and `PitOutTime` columns** \u2014 FastF1's actual\n", "pit stop timestamps \u2014 to detect real pit laps. A lap where `PitInTime` is not NaT\n", "is a genuine pit stop lap. This is how you'd do it in a real race engineering context." ] }, { "cell_type": "code", "execution_count": 6, "id": "code_11", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:41:54.165035Z", "iopub.status.busy": "2026-04-15T10:41:54.164541Z", "iopub.status.idle": "2026-04-15T10:41:54.271046Z", "shell.execute_reply": "2026-04-15T10:41:54.270506Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u2705 FastF1 cache enabled at ./f1_cache\n", "\u2705 Ingestion function ready\n" ] } ], "source": [ "import fastf1\n", "import pandas as pd\n", "import numpy as np\n", "import json, os, warnings\n", "warnings.filterwarnings(\"ignore\")\n", "\n", "os.makedirs(\"./f1_cache\", exist_ok=True)\n", "fastf1.Cache.enable_cache(\"./f1_cache\")\n", "print(\"\u2705 FastF1 cache enabled at ./f1_cache\")\n", "\n", "def ingest_race(season: int, race_name: str, conn) -> dict:\n", " \"\"\"\n", " Load one F1 race into Oracle AI Database 26ai.\n", " Populates all four paradigm tables.\n", "\n", " Pit stop detection uses PitInTime/PitOutTime timestamps from FastF1,\n", " not compound transitions \u2014 the correct and reliable approach.\n", " \"\"\"\n", " print(f\"\\n\ud83d\udce1 {season} {race_name}...\")\n", " try:\n", " sess = fastf1.get_session(season, race_name, \"R\")\n", " sess.load(laps=True, weather=True, telemetry=False, messages=True)\n", " except Exception as e:\n", " print(f\" \u26a0\ufe0f Cannot load: {e}\")\n", " return {}\n", "\n", " circuit = sess.event.get(\"Location\", race_name)\n", " laps_df = sess.laps.copy()\n", " cur = conn.cursor()\n", " counts = {\"laps\": 0, \"pits\": 0, \"events\": 0}\n", "\n", " # \u2500\u2500 race_laps (Paradigm 1: Relational) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " # Drop laps with no useful timing data\n", " valid = laps_df.dropna(subset=[\"LapTime\"])\n", " lap_rows = []\n", " for _, lap in valid.iterrows():\n", " try:\n", " lt = lap[\"LapTime\"].total_seconds()\n", " if lt < 30 or lt > 600: # discard clearly wrong laps\n", " continue\n", " compound = str(lap.get(\"Compound\", \"\")).upper()\n", " if compound not in (\"SOFT\",\"MEDIUM\",\"HARD\",\"INTERMEDIATE\",\"WET\"):\n", " compound = \"UNKNOWN\"\n", " # Detect pit laps from FastF1's actual pit timestamps\n", " is_pit_in = 1 if pd.notna(lap.get(\"PitInTime\")) else 0\n", " is_pit_out = 1 if pd.notna(lap.get(\"PitOutTime\")) else 0\n", " lap_rows.append((\n", " int(season), str(race_name), str(circuit),\n", " str(lap[\"Driver\"]),\n", " str(lap.get(\"Team\",\"Unknown\"))[:80],\n", " int(lap[\"LapNumber\"]),\n", " round(float(lt), 3),\n", " int(lap[\"Position\"]) if pd.notna(lap.get(\"Position\")) else None,\n", " compound,\n", " int(lap[\"TyreLife\"]) if pd.notna(lap.get(\"TyreLife\")) else None,\n", " 1 if lap.get(\"IsPersonalBest\") else 0,\n", " is_pit_in,\n", " is_pit_out,\n", " ))\n", " except Exception:\n", " continue\n", "\n", " if lap_rows:\n", " cur.executemany(\n", " \"\"\"INSERT INTO race_laps\n", " (season,race_name,circuit,driver,team,lap_number,lap_time_s,\n", " position,compound,tyre_life,is_personal_best,is_pit_in_lap,is_pit_out_lap)\n", " VALUES(:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13)\"\"\",\n", " lap_rows)\n", " counts[\"laps\"] = len(lap_rows)\n", "\n", " # \u2500\u2500 pit_stops (Paradigm 1: Relational) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " # Use PitInTime/PitOutTime \u2014 the correct FastF1 approach\n", " # PitInTime: moment wheels crossed the pit entry line\n", " # PitOutTime: moment car left the pit box\n", " # Stop duration = PitOutTime - PitInTime (stationary time only)\n", " for driver in laps_df[\"Driver\"].unique():\n", " dl = laps_df[laps_df[\"Driver\"] == driver].sort_values(\"LapNumber\").copy()\n", " stop_num = 1\n", " for _, lap in dl.iterrows():\n", " # Only process laps where driver actually pitted\n", " if not pd.notna(lap.get(\"PitInTime\")):\n", " continue\n", " # Tyre fitted before this stint (compound on the pit-out lap)\n", " out_lap = dl[dl[\"LapNumber\"] == lap[\"LapNumber\"] + 1]\n", " compound_in = None\n", " compound_out = str(lap.get(\"Compound\",\"\")).upper() or None\n", " if len(out_lap) > 0:\n", " c = str(out_lap.iloc[0].get(\"Compound\",\"\")).upper()\n", " if c in (\"SOFT\",\"MEDIUM\",\"HARD\",\"INTERMEDIATE\",\"WET\"):\n", " compound_in = c\n", " if compound_out not in (\"SOFT\",\"MEDIUM\",\"HARD\",\"INTERMEDIATE\",\"WET\"):\n", " compound_out = None\n", "\n", " # Calculate stop duration if timestamps available\n", " stop_dur = None\n", " if pd.notna(lap.get(\"PitInTime\")) and pd.notna(lap.get(\"PitOutTime\")):\n", " try:\n", " stop_dur = round(\n", " (lap[\"PitOutTime\"] - lap[\"PitInTime\"]).total_seconds(), 2\n", " )\n", " # Sanity check: real pit stops are 2-60 seconds\n", " if stop_dur < 1 or stop_dur > 120:\n", " stop_dur = None\n", " except Exception:\n", " stop_dur = None\n", "\n", " try:\n", " cur.execute(\n", " \"\"\"INSERT INTO pit_stops\n", " (season,race_name,circuit,driver,team,stop_number,\n", " lap_in,compound_in,compound_out,stop_duration_s)\n", " VALUES(:1,:2,:3,:4,:5,:6,:7,:8,:9,:10)\"\"\",\n", " (int(season), str(race_name), str(circuit),\n", " str(driver), str(lap.get(\"Team\",\"Unknown\"))[:80],\n", " stop_num, int(lap[\"LapNumber\"]),\n", " compound_in, compound_out, stop_dur))\n", " stop_num += 1\n", " counts[\"pits\"] += 1\n", " except Exception:\n", " continue\n", "\n", " # \u2500\u2500 race_events (Paradigm 2: Hybrid \u2014 Vector + Full-Text) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " # Race control messages: stored as text AND embedded as 384-dim vectors.\n", " # Oracle Text index \u2192 CONTAINS() keyword search\n", " # HNSW vector index \u2192 VECTOR_DISTANCE() semantic search\n", " # Both on the same column in the same table.\n", " if hasattr(sess, \"race_control_messages\") and sess.race_control_messages is not None:\n", " for _, msg in sess.race_control_messages.iterrows():\n", " txt = str(msg.get(\"Message\",\"\")).strip()\n", " if not txt or txt.lower() == \"nan\":\n", " continue\n", " lap_n = int(msg[\"Lap\"]) if pd.notna(msg.get(\"Lap\")) else 0\n", " etype = str(msg.get(\"Category\",\"OTHER\"))\n", " # Contextual embedding includes race name + lap for richer semantics\n", " emb = to_vec(f\"{etype}: {txt} (lap {lap_n}, {race_name} {season})\")\n", " cur.execute(\n", " \"\"\"INSERT INTO race_events\n", " (season,race_name,circuit,lap_number,event_type,message,embedding)\n", " VALUES(:1,:2,:3,:4,:5,:6,:7)\"\"\",\n", " (int(season), str(race_name), str(circuit),\n", " lap_n, etype, txt[:1000], emb))\n", " counts[\"events\"] += 1\n", "\n", " # \u2500\u2500 race_metadata (Paradigm 3: JSON Document) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " try:\n", " wdf = sess.weather_data\n", " meta = {\n", " \"circuit\": circuit,\n", " \"season\": season,\n", " \"race_name\": race_name,\n", " \"total_laps\": int(valid[\"LapNumber\"].max()) if len(valid) else 0,\n", " \"drivers\": int(laps_df[\"Driver\"].nunique()),\n", " \"rcm_count\": counts[\"events\"],\n", " \"weather\": {\n", " \"avg_air_temp_c\": round(float(wdf[\"AirTemp\"].mean()), 1) if len(wdf) else None,\n", " \"avg_track_temp_c\": round(float(wdf[\"TrackTemp\"].mean()), 1) if len(wdf) else None,\n", " \"rainfall\": bool(wdf[\"Rainfall\"].any()) if len(wdf) else False,\n", " \"avg_humidity_pct\": round(float(wdf[\"Humidity\"].mean()), 1) if len(wdf) else None,\n", " },\n", " }\n", " cur.execute(\n", " \"INSERT INTO race_metadata (season,race_name,circuit,race_context) VALUES(:1,:2,:3,:4)\",\n", " (int(season), str(race_name), str(circuit), json.dumps(meta)))\n", " except Exception as e:\n", " print(f\" \u26a0\ufe0f Metadata: {e}\")\n", "\n", " # \u2500\u2500 driver_teams + team_circuits (Paradigm 4: Graph edges) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", " final = valid.sort_values(\"LapNumber\", ascending=False).drop_duplicates(\"Driver\")\n", " for _, row in final.iterrows():\n", " try:\n", " pos = int(row.get(\"Position\",99)) if pd.notna(row.get(\"Position\")) else 99\n", " wins = 1 if pos == 1 else 0\n", " podiums = 1 if pos <= 3 else 0\n", " team = str(row.get(\"Team\",\"Unknown\"))[:80]\n", " cur.execute(\n", " \"\"\"MERGE INTO driver_teams dt\n", " USING (SELECT :drv d,:tm t,:ssn s FROM DUAL) src\n", " ON (dt.driver=src.d AND dt.team=src.t AND dt.season=src.s)\n", " WHEN NOT MATCHED THEN\n", " INSERT (driver,team,season,wins,podiums) VALUES(src.d,src.t,src.s,:w,:p)\n", " WHEN MATCHED THEN\n", " UPDATE SET wins=dt.wins+:w, podiums=dt.podiums+:p\"\"\",\n", " {\"drv\": str(row[\"Driver\"]), \"tm\": team, \"ssn\": int(season), \"w\": wins, \"p\": podiums})\n", " cur.execute(\n", " \"\"\"MERGE INTO team_circuits tc\n", " USING (SELECT :tm t,:cir c,:ssn s FROM DUAL) src\n", " ON (tc.team=src.t AND tc.circuit=src.c AND tc.season=src.s)\n", " WHEN NOT MATCHED THEN\n", " INSERT (team,circuit,season,best_finish) VALUES(src.t,src.c,src.s,:bf)\n", " WHEN MATCHED THEN\n", " UPDATE SET best_finish=LEAST(tc.best_finish,:bf)\"\"\",\n", " {\"tm\": team, \"cir\": str(circuit), \"ssn\": int(season), \"bf\": pos})\n", " except Exception:\n", " continue\n", "\n", " conn.commit()\n", " cur.close()\n", " print(f\" \u2705 {counts['laps']:,} laps | {counts['pits']} pit stops \"\n", " f\"(PitInTime-verified) | {counts['events']} events embedded\")\n", " return counts\n", "\n", "print(\"\u2705 Ingestion function ready\")\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "code_12", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:41:54.272340Z", "iopub.status.busy": "2026-04-15T10:41:54.272229Z", "iopub.status.idle": "2026-04-15T10:43:20.117235Z", "shell.execute_reply": "2026-04-15T10:43:20.116469Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\ud83d\udce1 2022 Miami...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Miami Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['16', '55', '1', '11', '77', '44', '10', '4', '22', '18', '14', '63', '5', '3', '47', '20', '24', '23', '6', '31']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Miami Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,002 laps | 29 pit stops (PitInTime-verified) | 76 events embedded\n", "\n", "\ud83d\udce1 2023 Miami...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['11', '14', '55', '20', '10', '63', '16', '31', '1', '77', '23', '27', '44', '24', '21', '4', '22', '18', '81', '2']\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,118 laps | 20 pit stops (PitInTime-verified) | 38 events embedded\n", "\n", "\ud83d\udce1 2024 Miami...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Miami Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '16', '55', '11', '4', '81', '63', '44', '27', '22', '18', '10', '31', '23', '14', '77', '2', '20', '24', '3']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Miami Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for session_info. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching session info data...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,086 laps | 28 pit stops (PitInTime-verified) | 75 events embedded\n", "\n", "\ud83d\udce1 2025 Miami...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for driver_info. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching driver list...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for session_status_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching session status data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for lap_count. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching lap count data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for track_status_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching track status data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for _extended_timing_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tParsing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for timing_app_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching timing app data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for weather_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching weather data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for race_control_messages. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching race control messages...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '4', '12', '81', '63', '55', '23', '16', '31', '22', '6', '44', '5', '7', '30', '27', '14', '18', '87', '10']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Bahrain Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for session_info. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching session info data...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 983 laps | 19 pit stops (PitInTime-verified) | 77 events embedded\n", "\n", "\ud83d\udce1 2025 Bahrain...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for driver_info. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching driver list...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for session_status_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching session status data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for lap_count. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching lap count data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for track_status_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching track status data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for _extended_timing_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tParsing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for timing_app_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching timing app data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tDriver 63: Lap timing integrity check failed for 3 lap(s)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for weather_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching weather data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for race_control_messages. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching race control messages...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['81', '16', '63', '10', '12', '4', '1', '55', '44', '22', '7', '6', '14', '31', '23', '27', '30', '5', '18', '87']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Saudi Arabian Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for session_info. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching session info data...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,094 laps | 43 pit stops (PitInTime-verified) | 91 events embedded\n", "\n", "\ud83d\udce1 2025 Saudi Arabia...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for driver_info. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching driver list...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for session_status_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching session status data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for lap_count. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching lap count data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for track_status_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching track status data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for _extended_timing_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tParsing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api WARNING \tSkipping lap alignment (no suitable lap)!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for timing_app_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching timing app data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for weather_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching weather data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for race_control_messages. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching race control messages...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '81', '63', '16', '12', '55', '44', '22', '10', '4', '23', '30', '14', '6', '87', '18', '7', '27', '31', '5']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Japanese Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for session_info. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching session info data...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 842 laps | 20 pit stops (PitInTime-verified) | 65 events embedded\n", "\n", "\ud83d\udce1 2025 Japan...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for driver_info. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching driver list...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for session_status_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching session status data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for lap_count. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching lap count data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for track_status_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching track status data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for _extended_timing_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tParsing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for timing_app_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching timing app data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for weather_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching weather data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for race_control_messages. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching race control messages...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '4', '81', '16', '63', '12', '6', '44', '23', '87', '10', '14', '30', '22', '55', '27', '5', '31', '7', '18']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Chinese Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for session_info. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching session info data...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,039 laps | 21 pit stops (PitInTime-verified) | 23 events embedded\n", "\n", "\ud83d\udce1 2025 China...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for driver_info. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching driver list...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for session_status_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching session status data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for lap_count. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching lap count data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for track_status_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching track status data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for _extended_timing_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tParsing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for timing_app_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching timing app data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for weather_data. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching weather data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tNo cached data found for race_control_messages. Loading data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "_api INFO \tFetching race control messages...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tData has been written to cache!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['81', '63', '4', '1', '44', '16', '6', '12', '22', '23', '31', '27', '14', '18', '55', '10', '87', '7', '5', '30']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Bahrain Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,045 laps | 26 pit stops (PitInTime-verified) | 56 events embedded\n", "\n", "\ud83d\udce1 2024 Bahrain...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '16', '63', '55', '11', '14', '4', '81', '44', '27', '22', '18', '23', '3', '20', '77', '24', '2', '31', '10']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Saudi Arabian Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,107 laps | 43 pit stops (PitInTime-verified) | 69 events embedded\n", "\n", "\ud83d\udce1 2024 Saudi Arabia...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '16', '11', '14', '81', '4', '63', '44', '22', '18', '38', '23', '20', '3', '27', '77', '31', '10', '2', '24']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Japanese Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 853 laps | 20 pit stops (PitInTime-verified) | 66 events embedded\n", "\n", "\ud83d\udce1 2024 Japan...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '11', '4', '55', '14', '81', '44', '16', '63', '22', '3', '27', '77', '23', '31', '18', '10', '20', '2', '24']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Chinese Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 858 laps | 55 pit stops (PitInTime-verified) | 66 events embedded\n", "\n", "\ud83d\udce1 2024 China...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '11', '14', '4', '81', '16', '55', '63', '27', '77', '18', '3', '31', '23', '10', '24', '20', '44', '22', '2']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Monaco Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 987 laps | 41 pit stops (PitInTime-verified) | 56 events embedded\n", "\n", "\ud83d\udce1 2024 Monaco...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['16', '81', '55', '4', '63', '1', '44', '22', '23', '10', '31', '3', '18', '14', '2', '11', '77', '24', '27', '20']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Canadian Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,210 laps | 23 pit stops (PitInTime-verified) | 118 events embedded\n", "\n", "\ud83d\udce1 2024 Canada...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['63', '1', '4', '81', '3', '14', '44', '22', '18', '23', '16', '55', '2', '20', '10', '11', '27', '31', '77', '24']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Spanish Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,247 laps | 45 pit stops (PitInTime-verified) | 128 events embedded\n", "\n", "\ud83d\udce1 2024 Spain...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['4', '1', '44', '63', '16', '55', '10', '31', '81', '14', '11', '77', '27', '18', '24', '20', '22', '3', '2', '23']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Austrian Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,290 laps | 42 pit stops (PitInTime-verified) | 105 events embedded\n", "\n", "\ud83d\udce1 2024 Austria...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '4', '63', '55', '44', '16', '81', '11', '27', '31', '3', '20', '10', '22', '14', '23', '18', '77', '2', '24']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Las Vegas Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,385 laps | 46 pit stops (PitInTime-verified) | 124 events embedded\n", "\n", "\ud83d\udce1 2023 Las Vegas...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['16', '1', '63', '10', '23', '2', '77', '20', '14', '44', '11', '55', '27', '3', '4', '31', '24', '81', '18', '22']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Monaco Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 841 laps | 31 pit stops (PitInTime-verified) | 73 events embedded\n", "\n", "\ud83d\udce1 2023 Monaco...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '14', '31', '55', '44', '16', '10', '63', '22', '4', '81', '21', '23', '18', '77', '2', '20', '27', '24', '11']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Singapore Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,492 laps | 38 pit stops (PitInTime-verified) | 242 events embedded\n", "\n", "\ud83d\udce1 2023 Singapore...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 19 drivers: ['55', '63', '16', '4', '44', '20', '14', '31', '27', '40', '1', '10', '11', '23', '22', '77', '81', '2', '24']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Abu Dhabi Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,044 laps | 25 pit stops (PitInTime-verified) | 74 events embedded\n", "\n", "\ud83d\udce1 2023 Abu Dhabi...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '16', '81', '63', '4', '22', '14', '27', '11', '10', '44', '31', '18', '23', '3', '55', '20', '77', '24', '2']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Monaco Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,137 laps | 38 pit stops (PitInTime-verified) | 65 events embedded\n", "\n", "\ud83d\udce1 2022 Monaco...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['16', '55', '11', '1', '4', '63', '14', '44', '5', '31', '22', '77', '20', '3', '47', '23', '10', '18', '6', '24']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Abu Dhabi Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,133 laps | 55 pit stops (PitInTime-verified) | 168 events embedded\n", "\n", "\ud83d\udce1 2022 Abu Dhabi...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 20 drivers: ['1', '11', '16', '55', '44', '63', '4', '31', '5', '14', '22', '47', '3', '18', '24', '20', '10', '77', '23', '6']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tLoading data for Abu Dhabi Grand Prix - Race [v3.4.0]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_info\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for driver_info\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 1,097 laps | 34 pit stops (PitInTime-verified) | 90 events embedded\n", "\n", "\ud83d\udce1 2021 Abu Dhabi...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to load result data from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core WARNING \tNo result data for this session available on Ergast! (This is expected for recent sessions)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for session_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for lap_count\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for track_status_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for _extended_timing_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for timing_app_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tProcessing timing data...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "logger WARNING \tFailed to add first lap time from Ergast!\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for weather_data\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "req INFO \tUsing cached data for race_control_messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "core INFO \tFinished loading data for 19 drivers: ['33', '44', '4', '11', '55', '77', '16', '22', '31', '3', '14', '10', '18', '99', '5', '6', '63', '7', '47']\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \u2705 970 laps | 32 pit stops (PitInTime-verified) | 74 events embedded\n", "\n", "\ud83c\udfc1 Done: 24,860 laps | 774 pit stops | 2019 race events embedded as VECTOR(384, FLOAT32)\n" ] } ], "source": [ "# Load races into Oracle AI Database 26ai\n", "# Miami 2022-2025: four years of history for the 2026 prediction\n", "# 2025 season races: builds current team baselines for overperformance\n", "# Cross-season circuits: enriches hybrid search comparison pool\n", "\n", "RACES = [\n", " # Miami \u2014 four years of history for the 2026 strategy prediction\n", " (2022, \"Miami\"),\n", " (2023, \"Miami\"),\n", " (2024, \"Miami\"),\n", " (2025, \"Miami\"),\n", " # 2025 season races \u2014 critical for current team baselines\n", " (2025, \"Bahrain\"),\n", " (2025, \"Saudi Arabia\"),\n", " (2025, \"Japan\"),\n", " (2025, \"China\"),\n", " # 2024 season races \u2014 additional baseline depth\n", " (2024, \"Bahrain\"),\n", " (2024, \"Saudi Arabia\"),\n", " (2024, \"Japan\"),\n", " (2024, \"China\"),\n", " (2024, \"Monaco\"),\n", " (2024, \"Canada\"),\n", " (2024, \"Spain\"),\n", " (2024, \"Austria\"),\n", " # Comparison circuits for hybrid search\n", " (2023, \"Las Vegas\"),\n", " (2023, \"Monaco\"),\n", " (2023, \"Singapore\"),\n", " (2023, \"Abu Dhabi\"),\n", " (2022, \"Monaco\"),\n", " (2022, \"Abu Dhabi\"),\n", " (2021, \"Abu Dhabi\"),\n", "]\n", "\n", "total = {\"laps\": 0, \"pits\": 0, \"events\": 0}\n", "for season, race in RACES:\n", " counts = ingest_race(season, race, CONN)\n", " for k in total:\n", " total[k] += counts.get(k, 0)\n", "\n", "print(f\"\\n\ud83c\udfc1 Done: {total['laps']:,} laps | {total['pits']} pit stops | \"\n", " f\"{total['events']} race events embedded as VECTOR(384, FLOAT32)\")\n" ] }, { "cell_type": "markdown", "id": "md_13", "metadata": {}, "source": [ "---\n", "## Paradigm 1: Relational SQL \u2014 Tyre Strategy at Miami (2022\u20132025)\n", "\n", "**Oracle features:** CTEs, `ROW_NUMBER() OVER`, `PARTITION BY`, aggregation\n", "\n", "Standard SQL \u2014 but demonstrating that Oracle 26ai doesn't sacrifice relational\n", "capability to gain vector and graph features. One engine, all paradigms.\n", "\n", "The `query_winner_strategy` function uses a CTE to identify the race winner\n", "(driver in P1 on the final lap) then reconstructs their full stint sequence.\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "code_14", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:20.122766Z", "iopub.status.busy": "2026-04-15T10:43:20.122570Z", "iopub.status.idle": "2026-04-15T10:43:20.184359Z", "shell.execute_reply": "2026-04-15T10:43:20.183854Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\ud83c\udfce\ufe0f Miami GP \u2014 Winner Strategies (2022\u20132025)\n", "=======================================================\n", "\n", "2022: VER (Red Bull Racing) \u2014 MEDIUM \u2192 HARD\n", " MEDIUM laps 2\u201326 (25 laps, avg 94.156s)\n", " HARD laps 27\u201357 (27 laps, avg 95.525s)\n", "\n", "2023: VER (Red Bull Racing) \u2014 HARD \u2192 MEDIUM\n", " HARD laps 2\u201345 (44 laps, avg 92.144s)\n", " MEDIUM laps 46\u201357 (12 laps, avg 91.847s)\n", "\n", "2024: NOR (McLaren) \u2014 MEDIUM \u2192 HARD\n", " MEDIUM laps 2\u201329 (28 laps, avg 94.954s)\n", " HARD laps 30\u201357 (27 laps, avg 93.911s)\n", "\n", "2025: PIA (McLaren) \u2014 MEDIUM \u2192 HARD\n", " MEDIUM laps 25\u201332 (8 laps, avg 97.414s)\n", " HARD laps 33\u201357 (25 laps, avg 91.436s)\n" ] } ], "source": [ "import pandas as pd\n", "import plotly.graph_objects as go\n", "\n", "COMPOUND_COLORS = {\n", " \"SOFT\":\"#E8002D\", \"MEDIUM\":\"#FFD700\", \"HARD\":\"#F0F0F0\",\n", " \"INTERMEDIATE\":\"#39B54A\", \"WET\":\"#0067FF\", \"UNKNOWN\":\"#888888\",\n", "}\n", "\n", "def query_tire_strategy(conn, season: int, race_name: str) -> pd.DataFrame:\n", " \"\"\"Stint-by-stint tyre allocation for all classified drivers.\"\"\"\n", " return pd.read_sql(\"\"\"\n", " SELECT\n", " driver, team, compound,\n", " MIN(lap_number) AS stint_start,\n", " MAX(lap_number) AS stint_end,\n", " COUNT(*) AS stint_laps,\n", " ROUND(AVG(lap_time_s), 3) AS avg_lap_s,\n", " MIN(position) AS best_pos\n", " FROM race_laps\n", " WHERE season = :season\n", " AND race_name = :race\n", " AND lap_time_s BETWEEN 60 AND 250\n", " AND compound != 'UNKNOWN'\n", " GROUP BY driver, team, compound\n", " ORDER BY best_pos NULLS LAST, stint_start\n", " \"\"\", conn, params={\"season\": season, \"race\": race_name})\n", "\n", "def query_winner_strategy(conn, season: int, race_name: str) -> pd.DataFrame:\n", " \"\"\"Race winner's full stint breakdown using a CTE to isolate P1 finisher.\"\"\"\n", " return pd.read_sql(\"\"\"\n", " WITH winner AS (\n", " SELECT driver FROM (\n", " SELECT driver, position,\n", " ROW_NUMBER() OVER (ORDER BY lap_number DESC) rn\n", " FROM race_laps\n", " WHERE season=:season AND race_name=:race AND position=1\n", " ) WHERE rn = 1\n", " )\n", " SELECT rl.driver, rl.team, rl.compound,\n", " MIN(rl.lap_number) AS stint_start,\n", " MAX(rl.lap_number) AS stint_end,\n", " COUNT(*) AS stint_laps,\n", " ROUND(AVG(rl.lap_time_s),3) AS avg_lap_s\n", " FROM race_laps rl JOIN winner w ON rl.driver=w.driver\n", " WHERE rl.season=:season AND rl.race_name=:race\n", " AND rl.lap_time_s BETWEEN 60 AND 250\n", " AND rl.compound != 'UNKNOWN'\n", " GROUP BY rl.driver, rl.team, rl.compound\n", " ORDER BY stint_start\n", " \"\"\", conn, params={\"season\": season, \"race\": race_name})\n", "\n", "# Show winner strategies across all 4 Miami GPs\n", "print(\"\ud83c\udfce\ufe0f Miami GP \u2014 Winner Strategies (2022\u20132025)\")\n", "print(\"=\" * 55)\n", "for yr in [2022, 2023, 2024, 2025]:\n", " df_w = query_winner_strategy(CONN, yr, \"Miami\")\n", " if len(df_w) > 0:\n", " winner = df_w[\"DRIVER\"].iloc[0]\n", " team = df_w[\"TEAM\"].iloc[0]\n", " stints = \" \u2192 \".join(df_w[\"COMPOUND\"])\n", " print(f\"\\n{yr}: {winner} ({team}) \u2014 {stints}\")\n", " for _, s in df_w.iterrows():\n", " print(f\" {s['COMPOUND']:8s} laps {int(s['STINT_START'])}\u2013{int(s['STINT_END'])} \"\n", " f\"({int(s['STINT_LAPS'])} laps, avg {s['AVG_LAP_S']:.3f}s)\")\n", "\n", "# Use 2025 for the detailed strategy chart\n", "df_strategy = query_tire_strategy(CONN, 2025, \"Miami\")\n", "df_winner = query_winner_strategy(CONN, 2025, \"Miami\")\n" ] }, { "cell_type": "code", "execution_count": 9, "id": "code_15", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:20.186016Z", "iopub.status.busy": "2026-04-15T10:43:20.185880Z", "iopub.status.idle": "2026-04-15T10:43:21.233039Z", "shell.execute_reply": "2026-04-15T10:43:21.232611Z" } }, "outputs": [ { "data": { "text/html": [ " \n", " " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.plotly.v1+json": { "config": { "plotlyServerURL": "https://plot.ly" }, "data": [ { "base": 25, "hovertemplate": "PIA
Compound: MEDIUM
Laps 25\u201332 (8 laps)
Avg lap: 97.414s", "insidetextanchor": "middle", "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": false, "text": "M(8L)", "textposition": "inside", "type": "bar", "x": [ 8 ], "y": [ "PIA" ] }, { "base": 33, "hovertemplate": "PIA
Compound: HARD
Laps 33\u201357 (25 laps)
Avg lap: 91.436s", "insidetextanchor": "middle", "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": false, "text": "H(25L)", "textposition": "inside", "type": "bar", "x": [ 25 ], "y": [ "PIA" ] }, { "base": 25, "hovertemplate": "NOR
Compound: MEDIUM
Laps 25\u201332 (8 laps)
Avg lap: 97.262s", "insidetextanchor": "middle", "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": false, "text": "M(8L)", "textposition": "inside", "type": "bar", "x": [ 8 ], "y": [ "NOR" ] }, { "base": 33, "hovertemplate": "NOR
Compound: HARD
Laps 33\u201357 (25 laps)
Avg lap: 91.324s", "insidetextanchor": "middle", "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": false, "text": "H(25L)", "textposition": "inside", "type": "bar", "x": [ 25 ], "y": [ "NOR" ] }, { "base": 25, "hovertemplate": "VER
Compound: MEDIUM
Laps 25\u201332 (8 laps)
Avg lap: 98.874s", "insidetextanchor": "middle", "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": false, "text": "M(8L)", "textposition": "inside", "type": "bar", "x": [ 8 ], "y": [ "VER" ] }, { "base": 33, "hovertemplate": "VER
Compound: HARD
Laps 33\u201357 (25 laps)
Avg lap: 91.934s", "insidetextanchor": "middle", "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": false, "text": "H(25L)", "textposition": "inside", "type": "bar", "x": [ 25 ], "y": [ "VER" ] }, { "base": 25, "hovertemplate": "RUS
Compound: HARD
Laps 25\u201332 (8 laps)
Avg lap: 98.085s", "insidetextanchor": "middle", "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": false, "text": "H(8L)", "textposition": "inside", "type": "bar", "x": [ 8 ], "y": [ "RUS" ] }, { "base": 33, "hovertemplate": "RUS
Compound: MEDIUM
Laps 33\u201357 (25 laps)
Avg lap: 91.956s", "insidetextanchor": "middle", "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": false, "text": "M(25L)", "textposition": "inside", "type": "bar", "x": [ 25 ], "y": [ "RUS" ] }, { "base": 25, "hovertemplate": "LEC
Compound: MEDIUM
Laps 25\u201332 (8 laps)
Avg lap: 99.113s", "insidetextanchor": "middle", "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": false, "text": "M(8L)", "textposition": "inside", "type": "bar", "x": [ 8 ], "y": [ "LEC" ] }, { "base": 33, "hovertemplate": "LEC
Compound: HARD
Laps 33\u201357 (25 laps)
Avg lap: 92.155s", "insidetextanchor": "middle", "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": false, "text": "H(25L)", "textposition": "inside", "type": "bar", "x": [ 25 ], "y": [ "LEC" ] }, { "base": 25, "hovertemplate": "ALB
Compound: MEDIUM
Laps 25\u201332 (8 laps)
Avg lap: 98.866s", "insidetextanchor": "middle", "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": false, "text": "M(8L)", "textposition": "inside", "type": "bar", "x": [ 8 ], "y": [ "ALB" ] }, { "base": 33, "hovertemplate": "ALB
Compound: HARD
Laps 33\u201357 (25 laps)
Avg lap: 92.082s", "insidetextanchor": "middle", "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": false, "text": "H(25L)", "textposition": "inside", "type": "bar", "x": [ 25 ], "y": [ "ALB" ] }, { "base": 25, "hovertemplate": "ANT
Compound: MEDIUM
Laps 25\u201332 (8 laps)
Avg lap: 99.404s", "insidetextanchor": "middle", "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": false, "text": "M(8L)", "textposition": "inside", "type": "bar", "x": [ 8 ], "y": [ "ANT" ] }, { "base": 33, "hovertemplate": "ANT
Compound: HARD
Laps 33\u201357 (25 laps)
Avg lap: 92.301s", "insidetextanchor": "middle", "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": false, "text": "H(25L)", "textposition": "inside", "type": "bar", "x": [ 25 ], "y": [ "ANT" ] }, { "base": 24, "hovertemplate": "HAM
Compound: HARD
Laps 24\u201332 (9 laps)
Avg lap: 97.617s", "insidetextanchor": "middle", "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": false, "text": "H(9L)", "textposition": "inside", "type": "bar", "x": [ 9 ], "y": [ "HAM" ] }, { "base": 33, "hovertemplate": "HAM
Compound: MEDIUM
Laps 33\u201357 (25 laps)
Avg lap: 92.240s", "insidetextanchor": "middle", "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": false, "text": "M(25L)", "textposition": "inside", "type": "bar", "x": [ 25 ], "y": [ "HAM" ] }, { "base": 25, "hovertemplate": "TSU
Compound: MEDIUM
Laps 25\u201332 (8 laps)
Avg lap: 99.578s", "insidetextanchor": "middle", "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": false, "text": "M(8L)", "textposition": "inside", "type": "bar", "x": [ 8 ], "y": [ "TSU" ] }, { "base": 33, "hovertemplate": "TSU
Compound: HARD
Laps 33\u201357 (25 laps)
Avg lap: 92.380s", "insidetextanchor": "middle", "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": false, "text": "H(25L)", "textposition": "inside", "type": "bar", "x": [ 25 ], "y": [ "TSU" ] }, { "base": 25, "hovertemplate": "SAI
Compound: MEDIUM
Laps 25\u201332 (8 laps)
Avg lap: 99.202s", "insidetextanchor": "middle", "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": false, "text": "M(8L)", "textposition": "inside", "type": "bar", "x": [ 8 ], "y": [ "SAI" ] }, { "base": 33, "hovertemplate": "SAI
Compound: HARD
Laps 33\u201357 (25 laps)
Avg lap: 92.324s", "insidetextanchor": "middle", "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": false, "text": "H(25L)", "textposition": "inside", "type": "bar", "x": [ 25 ], "y": [ "SAI" ] }, { "marker": { "color": "#FFD700", "line": { "color": "#111", "width": 0.8 } }, "name": "MEDIUM", "orientation": "h", "showlegend": true, "type": "bar", "x": [ 0 ], "y": [ "" ] }, { "marker": { "color": "#F0F0F0", "line": { "color": "#111", "width": 0.8 } }, "name": "HARD", "orientation": "h", "showlegend": true, "type": "bar", "x": [ 0 ], "y": [ "" ] } ], "layout": { "barmode": "stack", "font": { "color": "white", "size": 11 }, "height": 420, "legend": { "orientation": "h", "title": { "text": "Compound" }, "y": -0.2 }, "margin": { "b": 60, "l": 80, "r": 40, "t": 60 }, "paper_bgcolor": "#0a0a14", "plot_bgcolor": "#0f0f1a", "template": { "data": { "bar": [ { "error_x": { "color": "#2a3f5f" }, "error_y": { "color": "#2a3f5f" }, "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "bar" } ], "barpolar": [ { "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "barpolar" } ], "carpet": [ { "aaxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "baxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "type": "carpet" } ], "choropleth": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "choropleth" } ], "contour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "contour" } ], "contourcarpet": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "contourcarpet" } ], "heatmap": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "heatmap" } ], "heatmapgl": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "heatmapgl" } ], "histogram": [ { "marker": { "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "histogram" } ], "histogram2d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "histogram2d" } ], "histogram2dcontour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "histogram2dcontour" } ], "mesh3d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "mesh3d" } ], "parcoords": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "parcoords" } ], "pie": [ { "automargin": true, "type": "pie" } ], "scatter": [ { "fillpattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 }, "type": "scatter" } ], "scatter3d": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatter3d" } ], "scattercarpet": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattercarpet" } ], "scattergeo": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergeo" } ], "scattergl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergl" } ], "scattermapbox": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattermapbox" } ], "scatterpolar": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolar" } ], "scatterpolargl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolargl" } ], "scatterternary": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterternary" } ], "surface": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "surface" } ], "table": [ { "cells": { "fill": { "color": "#EBF0F8" }, "line": { "color": "white" } }, "header": { "fill": { "color": "#C8D4E3" }, "line": { "color": "white" } }, "type": "table" } ] }, "layout": { "annotationdefaults": { "arrowcolor": "#2a3f5f", "arrowhead": 0, "arrowwidth": 1 }, "autotypenumbers": "strict", "coloraxis": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "colorscale": { "diverging": [ [ 0, "#8e0152" ], [ 0.1, "#c51b7d" ], [ 0.2, "#de77ae" ], [ 0.3, "#f1b6da" ], [ 0.4, "#fde0ef" ], [ 0.5, "#f7f7f7" ], [ 0.6, "#e6f5d0" ], [ 0.7, "#b8e186" ], [ 0.8, "#7fbc41" ], [ 0.9, "#4d9221" ], [ 1, "#276419" ] ], "sequential": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "sequentialminus": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ] }, "colorway": [ "#636efa", "#EF553B", "#00cc96", "#ab63fa", "#FFA15A", "#19d3f3", "#FF6692", "#B6E880", "#FF97FF", "#FECB52" ], "font": { "color": "#2a3f5f" }, "geo": { "bgcolor": "white", "lakecolor": "white", "landcolor": "#E5ECF6", "showlakes": true, "showland": true, "subunitcolor": "white" }, "hoverlabel": { "align": "left" }, "hovermode": "closest", "mapbox": { "style": "light" }, "paper_bgcolor": "white", "plot_bgcolor": "#E5ECF6", "polar": { "angularaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "radialaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "scene": { "xaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "yaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "zaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" } }, "shapedefaults": { "line": { "color": "#2a3f5f" } }, "ternary": { "aaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "baxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "caxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "title": { "x": 0.05 }, "xaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 }, "yaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 } } }, "title": { "font": { "size": 15 }, "text": "\ud83c\udfce\ufe0f Miami GP 2025 \u2014 Tyre Strategy by Driver
Oracle AI Database 26ai | Paradigm 1: Relational SQL | Source: FastF1" }, "xaxis": { "gridcolor": "#2a2a3e", "title": { "text": "Lap Number" } } } }, "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def plot_strategy(df: pd.DataFrame, title: str) -> go.Figure:\n", " \"\"\"Gantt-style horizontal bar chart of tyre stints per driver.\"\"\"\n", " drivers = df.sort_values(\"BEST_POS\")[\"DRIVER\"].unique()[:10]\n", " fig = go.Figure()\n", " for driver in drivers:\n", " stints = df[df[\"DRIVER\"] == driver].sort_values(\"STINT_START\")\n", " for _, s in stints.iterrows():\n", " c = str(s[\"COMPOUND\"]).upper()\n", " color = COMPOUND_COLORS.get(c, \"#888\")\n", " fig.add_trace(go.Bar(\n", " name=c, x=[s[\"STINT_LAPS\"]],\n", " y=[f\"{driver}\"],\n", " orientation=\"h\", base=s[\"STINT_START\"],\n", " marker_color=color,\n", " marker_line=dict(color=\"#111\", width=0.8),\n", " text=f\"{c[0]}({int(s['STINT_LAPS'])}L)\",\n", " textposition=\"inside\", insidetextanchor=\"middle\",\n", " hovertemplate=(\n", " f\"{driver}
Compound: {c}
\"\n", " f\"Laps {int(s['STINT_START'])}\u2013{int(s['STINT_END'])} \"\n", " f\"({int(s['STINT_LAPS'])} laps)
\"\n", " f\"Avg lap: {s['AVG_LAP_S']:.3f}s\"\n", " ),\n", " showlegend=False,\n", " ))\n", " for c, color in COMPOUND_COLORS.items():\n", " if c in df[\"COMPOUND\"].values:\n", " fig.add_trace(go.Bar(\n", " name=c, x=[0], y=[\"\"], orientation=\"h\",\n", " marker_color=color,\n", " marker_line=dict(color=\"#111\", width=0.8),\n", " showlegend=True,\n", " ))\n", " fig.update_layout(\n", " title=dict(text=title, font=dict(size=15)),\n", " xaxis=dict(title=\"Lap Number\", gridcolor=\"#2a2a3e\"),\n", " barmode=\"stack\",\n", " plot_bgcolor=\"#0f0f1a\", paper_bgcolor=\"#0a0a14\",\n", " font=dict(color=\"white\", size=11),\n", " height=420, margin=dict(l=80, r=40, t=60, b=60),\n", " legend=dict(title=\"Compound\", orientation=\"h\", y=-0.2),\n", " )\n", " return fig\n", "\n", "fig1 = plot_strategy(\n", " df_strategy,\n", " \"\ud83c\udfce\ufe0f Miami GP 2025 \u2014 Tyre Strategy by Driver
\"\n", " \"Oracle AI Database 26ai | Paradigm 1: Relational SQL | Source: FastF1\",\n", ")\n", "fig1.show()\n" ] }, { "cell_type": "markdown", "id": "md_16", "metadata": {}, "source": [ "### Biggest Position Gains \u2014 Miami GP 2024\n", "\n", "Who made the most ground from lap 1 to the chequered flag?" ] }, { "cell_type": "code", "execution_count": 10, "id": "code_17", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:21.256234Z", "iopub.status.busy": "2026-04-15T10:43:21.256082Z", "iopub.status.idle": "2026-04-15T10:43:21.291727Z", "shell.execute_reply": "2026-04-15T10:43:21.291246Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "DRIVER TEAM START_POS FINAL_POS POSITIONS_GAINED FIN_COMPOUND\n", " GAS Alpine 18 13 5 MEDIUM\n", " HAM Ferrari 12 8 4 MEDIUM\n", " NOR McLaren 6 2 4 HARD\n", " BEA Haas F1 Team 16 13 3 HARD\n", " ALO Aston Martin 17 15 2 MEDIUM\n", " PIA McLaren 3 1 2 HARD\n", " LAW Racing Bulls 19 17 2 MEDIUM\n", " LEC Ferrari 8 7 1 HARD\n", " RUS Mercedes 4 3 1 MEDIUM\n" ] }, { "data": { "application/vnd.plotly.v1+json": { "config": { "plotlyServerURL": "https://plot.ly" }, "data": [ { "hovertemplate": "%{y}
Gained: %{x} positions", "marker": { "color": [ "#FFD700", "#FFD700", "#F0F0F0", "#F0F0F0", "#FFD700", "#F0F0F0", "#FFD700", "#F0F0F0", "#FFD700" ], "line": { "color": "#111", "width": 0.8 } }, "orientation": "h", "text": [ "+5", "+4", "+4", "+3", "+2", "+2", "+2", "+1", "+1" ], "textposition": "outside", "type": "bar", "x": [ 5, 4, 4, 3, 2, 2, 2, 1, 1 ], "y": [ "GAS (P13 finish)", "HAM (P8 finish)", "NOR (P2 finish)", "BEA (P13 finish)", "ALO (P15 finish)", "PIA (P1 finish)", "LAW (P17 finish)", "LEC (P7 finish)", "RUS (P3 finish)" ] } ], "layout": { "font": { "color": "white", "size": 11 }, "height": 380, "margin": { "b": 50, "l": 200, "r": 80, "t": 60 }, "paper_bgcolor": "#0a0a14", "plot_bgcolor": "#0f0f1a", "template": { "data": { "bar": [ { "error_x": { "color": "#2a3f5f" }, "error_y": { "color": "#2a3f5f" }, "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "bar" } ], "barpolar": [ { "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "barpolar" } ], "carpet": [ { "aaxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "baxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "type": "carpet" } ], "choropleth": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "choropleth" } ], "contour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "contour" } ], "contourcarpet": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "contourcarpet" } ], "heatmap": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "heatmap" } ], "heatmapgl": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "heatmapgl" } ], "histogram": [ { "marker": { "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "histogram" } ], "histogram2d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "histogram2d" } ], "histogram2dcontour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "histogram2dcontour" } ], "mesh3d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "mesh3d" } ], "parcoords": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "parcoords" } ], "pie": [ { "automargin": true, "type": "pie" } ], "scatter": [ { "fillpattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 }, "type": "scatter" } ], "scatter3d": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatter3d" } ], "scattercarpet": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattercarpet" } ], "scattergeo": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergeo" } ], "scattergl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergl" } ], "scattermapbox": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattermapbox" } ], "scatterpolar": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolar" } ], "scatterpolargl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolargl" } ], "scatterternary": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterternary" } ], "surface": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "surface" } ], "table": [ { "cells": { "fill": { "color": "#EBF0F8" }, "line": { "color": "white" } }, "header": { "fill": { "color": "#C8D4E3" }, "line": { "color": "white" } }, "type": "table" } ] }, "layout": { "annotationdefaults": { "arrowcolor": "#2a3f5f", "arrowhead": 0, "arrowwidth": 1 }, "autotypenumbers": "strict", "coloraxis": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "colorscale": { "diverging": [ [ 0, "#8e0152" ], [ 0.1, "#c51b7d" ], [ 0.2, "#de77ae" ], [ 0.3, "#f1b6da" ], [ 0.4, "#fde0ef" ], [ 0.5, "#f7f7f7" ], [ 0.6, "#e6f5d0" ], [ 0.7, "#b8e186" ], [ 0.8, "#7fbc41" ], [ 0.9, "#4d9221" ], [ 1, "#276419" ] ], "sequential": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "sequentialminus": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ] }, "colorway": [ "#636efa", "#EF553B", "#00cc96", "#ab63fa", "#FFA15A", "#19d3f3", "#FF6692", "#B6E880", "#FF97FF", "#FECB52" ], "font": { "color": "#2a3f5f" }, "geo": { "bgcolor": "white", "lakecolor": "white", "landcolor": "#E5ECF6", "showlakes": true, "showland": true, "subunitcolor": "white" }, "hoverlabel": { "align": "left" }, "hovermode": "closest", "mapbox": { "style": "light" }, "paper_bgcolor": "white", "plot_bgcolor": "#E5ECF6", "polar": { "angularaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "radialaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "scene": { "xaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "yaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "zaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" } }, "shapedefaults": { "line": { "color": "#2a3f5f" } }, "ternary": { "aaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "baxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "caxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "title": { "x": 0.05 }, "xaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 }, "yaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 } } }, "title": { "text": "\ud83d\ude80 Position Gains \u2014 Miami GP 2025
Oracle AI Database 26ai | Paradigm 1: SQL | Bar colour = final compound" }, "xaxis": { "gridcolor": "#2a2a3e", "title": { "text": "Positions Gained" } } } }, "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def query_position_changes(conn, season: int, race_name: str) -> pd.DataFrame:\n", " return pd.read_sql(\"\"\"\n", " WITH ranked AS (\n", " SELECT driver, team, lap_number, position, compound,\n", " MAX(lap_number) OVER (PARTITION BY driver) AS max_lap,\n", " MIN(lap_number) OVER (PARTITION BY driver) AS min_lap\n", " FROM race_laps\n", " WHERE season=:season AND race_name=:race\n", " AND position BETWEEN 1 AND 20\n", " ),\n", " first_p AS (SELECT driver, team, position AS start_pos\n", " FROM ranked WHERE lap_number = min_lap),\n", " final_p AS (SELECT driver, position AS fin_pos, compound AS fin_compound\n", " FROM ranked WHERE lap_number = max_lap)\n", " SELECT f.driver, fp.team, fp.start_pos, f.fin_pos AS final_pos,\n", " (fp.start_pos - f.fin_pos) AS positions_gained, f.fin_compound\n", " FROM final_p f JOIN first_p fp ON fp.driver=f.driver\n", " WHERE (fp.start_pos - f.fin_pos) > 0\n", " ORDER BY positions_gained DESC\n", " FETCH FIRST 10 ROWS ONLY\n", " \"\"\", conn, params={\"season\": season, \"race\": race_name})\n", "\n", "df_pos = query_position_changes(CONN, 2025, \"Miami\")\n", "print(df_pos.to_string(index=False))\n", "\n", "fig2 = go.Figure(go.Bar(\n", " x=df_pos[\"POSITIONS_GAINED\"],\n", " y=df_pos[\"DRIVER\"] + \" (P\" + df_pos[\"FINAL_POS\"].astype(str) + \" finish)\",\n", " orientation=\"h\",\n", " marker_color=[COMPOUND_COLORS.get(str(c).upper(),\"#888\") for c in df_pos[\"FIN_COMPOUND\"]],\n", " marker_line=dict(color=\"#111\", width=0.8),\n", " text=[\"+\" + str(int(v)) for v in df_pos[\"POSITIONS_GAINED\"]],\n", " textposition=\"outside\",\n", " hovertemplate=\"%{y}
Gained: %{x} positions\",\n", "))\n", "fig2.update_layout(\n", " title=\"\ud83d\ude80 Position Gains \u2014 Miami GP 2025
\"\n", " \"Oracle AI Database 26ai | Paradigm 1: SQL | Bar colour = final compound\",\n", " xaxis=dict(title=\"Positions Gained\", gridcolor=\"#2a2a3e\"),\n", " plot_bgcolor=\"#0f0f1a\", paper_bgcolor=\"#0a0a14\",\n", " font=dict(color=\"white\", size=11),\n", " height=380, margin=dict(l=200, r=80, t=60, b=50),\n", ")\n", "fig2.show()\n" ] }, { "cell_type": "markdown", "id": "md_18", "metadata": {}, "source": [ "### Verified Pit Stop Summary\n", "\n", "This table uses the corrected pit stop data \u2014 detected via `PitInTime` timestamps,\n", "not compound transitions. Stop duration is the actual stationary time in the pit box." ] }, { "cell_type": "code", "execution_count": 11, "id": "code_19", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:21.293228Z", "iopub.status.busy": "2026-04-15T10:43:21.293117Z", "iopub.status.idle": "2026-04-15T10:43:21.304236Z", "shell.execute_reply": "2026-04-15T10:43:21.303719Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Miami 2025 \u2014 19 pit stops detected via PitInTime timestamps:\n", "DRIVER TEAM STOP_NUMBER LAP_IN TYRE_REMOVED TYRE_FITTED STOP_DURATION_S\n", " ALB Williams 1 26 MEDIUM MEDIUM None\n", " ALO Aston Martin 1 28 HARD HARD None\n", " ANT Mercedes 1 25 MEDIUM MEDIUM None\n", " BOR Kick Sauber 1 19 None None None\n", " GAS Alpine 1 32 HARD MEDIUM None\n", " HAD Racing Bulls 1 22 None None None\n", " HAM Ferrari 1 28 HARD HARD None\n", " HUL Kick Sauber 1 36 HARD MEDIUM None\n", " LAW Racing Bulls 1 28 HARD HARD None\n", " LAW Racing Bulls 2 36 MEDIUM None None\n", " LEC Ferrari 1 29 MEDIUM MEDIUM None\n", " NOR McLaren 1 29 MEDIUM MEDIUM None\n", " OCO Haas F1 Team 1 23 None HARD None\n", " PIA McLaren 1 29 MEDIUM MEDIUM None\n", " RUS Mercedes 1 29 HARD HARD None\n", " SAI Williams 1 25 MEDIUM MEDIUM None\n", " STR Aston Martin 1 20 None None None\n", " TSU Red Bull Racing 1 27 MEDIUM MEDIUM None\n", " VER Red Bull Racing 1 26 MEDIUM MEDIUM None\n" ] } ], "source": [ "def query_pit_stops(conn, season: int, race_name: str) -> pd.DataFrame:\n", " return pd.read_sql(\"\"\"\n", " SELECT driver, team, stop_number, lap_in,\n", " compound_out AS tyre_removed,\n", " compound_in AS tyre_fitted,\n", " ROUND(stop_duration_s, 2) AS stop_duration_s\n", " FROM pit_stops\n", " WHERE season=:season AND race_name=:race\n", " ORDER BY driver, stop_number\n", " \"\"\", conn, params={\"season\": season, \"race\": race_name})\n", "\n", "df_pits = query_pit_stops(CONN, 2025, \"Miami\")\n", "print(f\"Miami 2025 \u2014 {len(df_pits)} pit stops detected via PitInTime timestamps:\")\n", "print(df_pits.to_string(index=False))\n" ] }, { "cell_type": "markdown", "id": "md_20", "metadata": {}, "source": [ "---\n", "## Paradigm 2: Hybrid Search \u2014 Which Races Felt Like Miami?\n", "\n", "**Oracle features:** `VECTOR_DISTANCE()`, `CONTAINS()`, HNSW vector index,\n", "`CTXSYS.CONTEXT` full-text index, Reciprocal Rank Fusion (RRF)\n", "\n", "This is the feature I found most interesting building this \u2014 and the one that\n", "demonstrates Oracle 26ai's core architectural advantage.\n", "\n", "### The problem\n", "\n", "\"Find past races that felt like Miami \u2014 late incidents, track limits chaos, compressed field\"\n", "is not a SQL question. There's no `chaos_level` column.\n", "\n", "But if we can find races with similar late-race patterns, we can study what\n", "strategies worked in those conditions \u2014 and use that to inform Miami.\n", "\n", "### The solution: hybrid search in a single SQL query\n", "\n", "1. **Build a race fingerprint** \u2014 embed all late-race control messages as one vector\n", "2. **Vector search** \u2014 `VECTOR_DISTANCE()` finds races whose event patterns are semantically similar\n", "3. **Keyword search** \u2014 `CONTAINS()` boosts races with literal safety car / incident mentions\n", "4. **RRF** \u2014 merges both ranked lists: `score = 1/(vec_rank+60) + 1/(text_rank+60)`\n", "\n", "In a fragmented stack you'd need Pinecone for step 2 and Elasticsearch for step 3,\n", "with Python glue code to merge the results. Here it's one cursor execute.\n" ] }, { "cell_type": "code", "execution_count": 12, "id": "code_21", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:21.305586Z", "iopub.status.busy": "2026-04-15T10:43:21.305466Z", "iopub.status.idle": "2026-04-15T10:43:21.442459Z", "shell.execute_reply": "2026-04-15T10:43:21.441711Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Building Miami 2025 race fingerprint...\n", "Building fingerprint from 15 messages (lap 35+):\n", " Lap 35 [Flag ] WAVED BLUE FLAG FOR CAR 30 (LAW) TIMED AT 16:58:38\n", " Lap 37 [Flag ] WAVED BLUE FLAG FOR CAR 30 (LAW) TIMED AT 17:01:25\n", " Lap 42 [Other ] INCIDENT INVOLVING CAR 55 (SAI) NOTED - YELLOW FLAG INFRINGEMENT\n", " Lap 42 [Other ] INCIDENT INVOLVING CAR 10 (GAS) NOTED - YELLOW FLAG INFRINGEMENT\n", " Lap 45 [Other ] FIA STEWARDS: INCIDENT INVOLVING CAR 55 (SAI) WILL BE INVESTIGATE\n", " Lap 45 [Other ] FIA STEWARDS: INCIDENT INVOLVING CAR 10 (GAS) WILL BE INVESTIGATE\n", " Lap 46 [Flag ] WAVED BLUE FLAG FOR CAR 18 (STR) TIMED AT 17:15:02\n", " Lap 47 [Flag ] WAVED BLUE FLAG FOR CAR 18 (STR) TIMED AT 17:16:34\n", " Lap 48 [Flag ] WAVED BLUE FLAG FOR CAR 14 (ALO) TIMED AT 17:18:03\n", " Lap 49 [Flag ] WAVED BLUE FLAG FOR CAR 14 (ALO) TIMED AT 17:20:13\n", " Lap 49 [Flag ] WAVED BLUE FLAG FOR CAR 14 (ALO) TIMED AT 17:20:04\n", " Lap 57 [Other ] TURN 17 INCIDENT INVOLVING CARS 44 (HAM) AND 55 (SAI) NOTED - CAU\n", " Lap 57 [Flag ] WAVED BLUE FLAG FOR CAR 27 (HUL) TIMED AT 17:32:03\n", " Lap 57 [Flag ] CHEQUERED FLAG\n", " Lap 57 [Other ] FIA STEWARDS: TURN 17 INCIDENT INVOLVING CARS 44 (HAM) AND 55 (SA\n", "\n", "\u2705 Fingerprint: 384-dim vector\n" ] } ], "source": [ "def build_race_fingerprint(conn, season: int, race_name: str,\n", " from_lap: int = 35) -> array.array:\n", " \"\"\"\n", " Embed all late-race control messages as a single vector fingerprint.\n", " This represents the overall strategic 'character' of a race's final phase.\n", " \"\"\"\n", " cur = conn.cursor()\n", " cur.execute(\n", " \"\"\"SELECT event_type, message, lap_number\n", " FROM race_events\n", " WHERE season=:s AND race_name=:r AND lap_number >= :l\n", " ORDER BY lap_number\"\"\",\n", " s=season, r=race_name, l=from_lap)\n", " rows = cur.fetchall()\n", " cur.close()\n", "\n", " if not rows:\n", " raise ValueError(f\"No events found for {season} {race_name} from lap {from_lap}\")\n", "\n", " narrative = \" | \".join(f\"{r[0]}: {r[1]}\" for r in rows)\n", " print(f\"Building fingerprint from {len(rows)} messages (lap {from_lap}+):\")\n", " for r in rows:\n", " print(f\" Lap {r[2]:3d} [{r[0]:15s}] {r[1][:65]}\")\n", " return to_vec(narrative)\n", "\n", "print(\"Building Miami 2025 race fingerprint...\")\n", "QUERY_VEC = build_race_fingerprint(CONN, 2025, \"Miami\")\n", "print(f\"\\n\u2705 Fingerprint: {len(QUERY_VEC)}-dim vector\")\n" ] }, { "cell_type": "code", "execution_count": 13, "id": "code_22", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:21.444933Z", "iopub.status.busy": "2026-04-15T10:43:21.444764Z", "iopub.status.idle": "2026-04-15T10:43:21.562128Z", "shell.execute_reply": "2026-04-15T10:43:21.561558Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Races most similar to Miami 2025 (hybrid search \u2014 RRF):\n", " season race_name circuit rrf_score vec_score text_score cosine_sim\n", " 2024 Austria Spielberg 31.01 15.38 15.63 0.705\n", " 2024 Canada Montr\u00e9al 30.68 14.29 16.39 0.673\n", " 2023 Monaco Monaco 30.58 15.87 14.71 0.712\n", " 2022 Monaco Monaco 29.83 16.13 13.70 0.715\n", " 2024 Spain Barcelona 29.71 15.63 14.08 0.707\n", " 2024 Monaco Monaco 29.38 16.39 12.99 0.717\n" ] } ], "source": [ "def hybrid_search(conn, query_vec: array.array,\n", " exclude_season: int, exclude_race: str,\n", " top_k: int = 6) -> pd.DataFrame:\n", " \"\"\"\n", " Hybrid search: VECTOR_DISTANCE() + CONTAINS() combined via RRF.\n", "\n", " This is a single Oracle SQL query using two different indexes simultaneously:\n", " - HNSW vector index on race_events.embedding \u2192 semantic similarity\n", " - CTXSYS.CONTEXT full-text index on race_events.message \u2192 keyword relevance\n", "\n", " Results are aggregated at the race level (GROUP BY season, race_name)\n", " so each race appears exactly once \u2014 ranked by its best-matching event.\n", "\n", " RRF constant (60): prevents top-ranked results from dominating.\n", " Standard value from the original RRF paper (Cormack et al. 2009).\n", " \"\"\"\n", " sql = \"\"\"\n", " WITH\n", " vec_per_event AS (\n", " SELECT season, race_name, circuit,\n", " VECTOR_DISTANCE(embedding, :qvec, COSINE) AS vec_dist\n", " FROM race_events\n", " WHERE NOT (season=:ex_s AND race_name=:ex_r)\n", " ),\n", " vec_ranked AS (\n", " SELECT season, race_name, circuit,\n", " MIN(vec_dist) AS vec_dist,\n", " ROW_NUMBER() OVER (ORDER BY MIN(vec_dist)) AS vec_rank\n", " FROM vec_per_event\n", " GROUP BY season, race_name, circuit\n", " FETCH FIRST 50 ROWS ONLY\n", " ),\n", " text_per_event AS (\n", " SELECT season, race_name, circuit,\n", " CONTAINS(message,\n", " 'SAFETY CAR OR VSC OR RESTART OR INCIDENT OR COLLISION'\n", " ) AS text_rel\n", " FROM race_events\n", " WHERE NOT (season=:ex_s AND race_name=:ex_r)\n", " AND CONTAINS(message,\n", " 'SAFETY CAR OR VSC OR RESTART OR INCIDENT OR COLLISION'\n", " ) > 0\n", " ),\n", " text_ranked AS (\n", " SELECT season, race_name, circuit,\n", " SUM(text_rel) AS total_relevance,\n", " ROW_NUMBER() OVER (ORDER BY SUM(text_rel) DESC) AS text_rank\n", " FROM text_per_event\n", " GROUP BY season, race_name, circuit\n", " ),\n", " rrf AS (\n", " SELECT\n", " COALESCE(v.season, t.season) AS season,\n", " COALESCE(v.race_name, t.race_name) AS race_name,\n", " COALESCE(v.circuit, t.circuit) AS circuit,\n", " NVL(1.0/(v.vec_rank + 60), 0)\n", " + NVL(1.0/(t.text_rank + 60), 0) AS rrf_score,\n", " NVL(1.0/(v.vec_rank + 60), 0) AS vec_contribution,\n", " NVL(1.0/(t.text_rank + 60), 0) AS text_contribution,\n", " v.vec_dist\n", " FROM vec_ranked v\n", " FULL OUTER JOIN text_ranked t\n", " ON v.season=t.season AND v.race_name=t.race_name\n", " )\n", " SELECT season, race_name, circuit,\n", " ROUND(rrf_score * 1000, 2) AS rrf_score,\n", " ROUND(vec_contribution * 1000, 2) AS vec_score,\n", " ROUND(text_contribution * 1000, 2) AS text_score,\n", " ROUND(1 - NVL(vec_dist, 1), 3) AS cosine_sim\n", " FROM rrf\n", " ORDER BY rrf_score DESC\n", " FETCH FIRST :k ROWS ONLY\n", " \"\"\"\n", " cur = conn.cursor()\n", " cur.execute(sql, qvec=query_vec, ex_s=exclude_season, ex_r=exclude_race, k=top_k)\n", " cols = [d[0].lower() for d in cur.description]\n", " df = pd.DataFrame(cur.fetchall(), columns=cols)\n", " cur.close()\n", " return df\n", "\n", "df_similar = hybrid_search(CONN, QUERY_VEC, 2025, \"Miami\")\n", "print(\"Races most similar to Miami 2025 (hybrid search \u2014 RRF):\")\n", "print(df_similar.to_string(index=False))\n" ] }, { "cell_type": "code", "execution_count": 14, "id": "code_23", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:21.564552Z", "iopub.status.busy": "2026-04-15T10:43:21.564379Z", "iopub.status.idle": "2026-04-15T10:43:21.584207Z", "shell.execute_reply": "2026-04-15T10:43:21.583713Z" } }, "outputs": [ { "data": { "application/vnd.plotly.v1+json": { "config": { "plotlyServerURL": "https://plot.ly" }, "data": [ { "hovertemplate": "%{y}
Semantic contribution: %{x}", "marker": { "color": "#7C4DFF" }, "name": "Semantic (VECTOR_DISTANCE)", "orientation": "h", "type": "bar", "x": [ 15.38, 14.29, 15.87, 16.13, 15.63, 16.39 ], "y": [ "Austria 2024", "Canada 2024", "Monaco 2023", "Monaco 2022", "Spain 2024", "Monaco 2024" ] }, { "hovertemplate": "%{y}
Keyword contribution: %{x}", "marker": { "color": "#4FC3F7" }, "name": "Keyword (Oracle Text CONTAINS)", "orientation": "h", "type": "bar", "x": [ 15.63, 16.39, 14.71, 13.7, 14.08, 12.99 ], "y": [ "Austria 2024", "Canada 2024", "Monaco 2023", "Monaco 2022", "Spain 2024", "Monaco 2024" ] } ], "layout": { "annotations": [ { "font": { "color": "#888", "size": 10 }, "showarrow": false, "text": "Purple = semantic match \u00b7 Blue = safety car / incident keywords", "x": 0, "xref": "paper", "y": -0.18, "yref": "paper" } ], "barmode": "stack", "font": { "color": "white", "size": 11 }, "height": 380, "legend": { "x": 0.5, "y": 0.05 }, "margin": { "b": 50, "l": 170, "r": 60, "t": 70 }, "paper_bgcolor": "#0a0a14", "plot_bgcolor": "#0f0f1a", "template": { "data": { "bar": [ { "error_x": { "color": "#2a3f5f" }, "error_y": { "color": "#2a3f5f" }, "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "bar" } ], "barpolar": [ { "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "barpolar" } ], "carpet": [ { "aaxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "baxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "type": "carpet" } ], "choropleth": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "choropleth" } ], "contour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "contour" } ], "contourcarpet": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "contourcarpet" } ], "heatmap": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "heatmap" } ], "heatmapgl": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "heatmapgl" } ], "histogram": [ { "marker": { "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "histogram" } ], "histogram2d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "histogram2d" } ], "histogram2dcontour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "histogram2dcontour" } ], "mesh3d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "mesh3d" } ], "parcoords": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "parcoords" } ], "pie": [ { "automargin": true, "type": "pie" } ], "scatter": [ { "fillpattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 }, "type": "scatter" } ], "scatter3d": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatter3d" } ], "scattercarpet": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattercarpet" } ], "scattergeo": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergeo" } ], "scattergl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergl" } ], "scattermapbox": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattermapbox" } ], "scatterpolar": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolar" } ], "scatterpolargl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolargl" } ], "scatterternary": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterternary" } ], "surface": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "surface" } ], "table": [ { "cells": { "fill": { "color": "#EBF0F8" }, "line": { "color": "white" } }, "header": { "fill": { "color": "#C8D4E3" }, "line": { "color": "white" } }, "type": "table" } ] }, "layout": { "annotationdefaults": { "arrowcolor": "#2a3f5f", "arrowhead": 0, "arrowwidth": 1 }, "autotypenumbers": "strict", "coloraxis": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "colorscale": { "diverging": [ [ 0, "#8e0152" ], [ 0.1, "#c51b7d" ], [ 0.2, "#de77ae" ], [ 0.3, "#f1b6da" ], [ 0.4, "#fde0ef" ], [ 0.5, "#f7f7f7" ], [ 0.6, "#e6f5d0" ], [ 0.7, "#b8e186" ], [ 0.8, "#7fbc41" ], [ 0.9, "#4d9221" ], [ 1, "#276419" ] ], "sequential": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "sequentialminus": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ] }, "colorway": [ "#636efa", "#EF553B", "#00cc96", "#ab63fa", "#FFA15A", "#19d3f3", "#FF6692", "#B6E880", "#FF97FF", "#FECB52" ], "font": { "color": "#2a3f5f" }, "geo": { "bgcolor": "white", "lakecolor": "white", "landcolor": "#E5ECF6", "showlakes": true, "showland": true, "subunitcolor": "white" }, "hoverlabel": { "align": "left" }, "hovermode": "closest", "mapbox": { "style": "light" }, "paper_bgcolor": "white", "plot_bgcolor": "#E5ECF6", "polar": { "angularaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "radialaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "scene": { "xaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "yaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "zaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" } }, "shapedefaults": { "line": { "color": "#2a3f5f" } }, "ternary": { "aaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "baxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "caxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "title": { "x": 0.05 }, "xaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 }, "yaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 } } }, "title": { "text": "\ud83e\udde0 Hybrid Search \u2014 Races Most Similar to Miami 2025
Oracle AI Database 26ai | Paradigm 2: VECTOR_DISTANCE() + CONTAINS() + RRF" }, "xaxis": { "gridcolor": "#2a2a3e", "title": { "text": "RRF Score \u00d7 1000" } } } }, "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Visualise: stacked bar showing vector vs keyword contribution per race\n", "df_similar[\"label\"] = df_similar[\"race_name\"] + \" \" + df_similar[\"season\"].astype(str)\n", "\n", "fig3 = go.Figure()\n", "fig3.add_trace(go.Bar(\n", " name=\"Semantic (VECTOR_DISTANCE)\", x=df_similar[\"vec_score\"],\n", " y=df_similar[\"label\"], orientation=\"h\", marker_color=\"#7C4DFF\",\n", " hovertemplate=\"%{y}
Semantic contribution: %{x}\",\n", "))\n", "fig3.add_trace(go.Bar(\n", " name=\"Keyword (Oracle Text CONTAINS)\", x=df_similar[\"text_score\"],\n", " y=df_similar[\"label\"], orientation=\"h\", marker_color=\"#4FC3F7\",\n", " hovertemplate=\"%{y}
Keyword contribution: %{x}\",\n", "))\n", "fig3.update_layout(\n", " title=\"\ud83e\udde0 Hybrid Search \u2014 Races Most Similar to Miami 2025
\"\n", " \"Oracle AI Database 26ai | Paradigm 2: VECTOR_DISTANCE() + CONTAINS() + RRF\",\n", " xaxis=dict(title=\"RRF Score \u00d7 1000\", gridcolor=\"#2a2a3e\"),\n", " barmode=\"stack\",\n", " plot_bgcolor=\"#0f0f1a\", paper_bgcolor=\"#0a0a14\",\n", " font=dict(color=\"white\", size=11),\n", " height=380, margin=dict(l=170, r=60, t=70, b=50),\n", " legend=dict(x=0.5, y=0.05),\n", " annotations=[dict(\n", " text=\"Purple = semantic match \u00b7 Blue = safety car / incident keywords\",\n", " x=0, y=-0.18, xref=\"paper\", yref=\"paper\",\n", " showarrow=False, font=dict(size=10, color=\"#888\"),\n", " )],\n", ")\n", "fig3.show()\n" ] }, { "cell_type": "markdown", "id": "md_24", "metadata": {}, "source": [ "---\n", "## Paradigm 3: JSON Document \u2014 Race Conditions at Miami\n", "\n", "**Oracle features:** `JSON_VALUE()`, `CLOB IS JSON` constraint\n", "\n", "Weather and session metadata that doesn't fit neatly into typed columns\n", "is stored as a JSON document in a `CLOB IS JSON` column.\n", "Oracle queries it natively with `JSON_VALUE()` \u2014 no parsing in Python required.\n", "\n", "This replaces what MongoDB is typically used for in a fragmented AI stack." ] }, { "cell_type": "code", "execution_count": 15, "id": "code_25", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:21.586187Z", "iopub.status.busy": "2026-04-15T10:43:21.586028Z", "iopub.status.idle": "2026-04-15T10:43:21.595247Z", "shell.execute_reply": "2026-04-15T10:43:21.594713Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Miami 2025 \u2014 Race conditions (from JSON document store):\n", " circuit : Miami Gardens\n", " total_laps : 57\n", " drivers : 20\n", " air_temp_c : 26.6\n", " track_temp_c : 38.7\n", " rainfall : true\n", " humidity_pct : 63\n", " rcm_count : 77\n" ] } ], "source": [ "def query_race_metadata(conn, season: int, race_name: str) -> dict:\n", " \"\"\"Extract race conditions from JSON document using JSON_VALUE().\"\"\"\n", " cur = conn.cursor()\n", " cur.execute(\"\"\"\n", " SELECT\n", " JSON_VALUE(race_context, '$.circuit') AS circuit,\n", " JSON_VALUE(race_context, '$.total_laps') AS total_laps,\n", " JSON_VALUE(race_context, '$.drivers') AS drivers,\n", " JSON_VALUE(race_context, '$.weather.avg_air_temp_c') AS air_temp_c,\n", " JSON_VALUE(race_context, '$.weather.avg_track_temp_c') AS track_temp_c,\n", " JSON_VALUE(race_context, '$.weather.rainfall') AS rainfall,\n", " JSON_VALUE(race_context, '$.weather.avg_humidity_pct') AS humidity_pct,\n", " JSON_VALUE(race_context, '$.rcm_count') AS rcm_count\n", " FROM race_metadata WHERE season=:s AND race_name=:r\n", " \"\"\", s=season, r=race_name)\n", " row = cur.fetchone()\n", " cur.close()\n", " if not row:\n", " return {}\n", " cols = [\"circuit\",\"total_laps\",\"drivers\",\"air_temp_c\",\"track_temp_c\",\n", " \"rainfall\",\"humidity_pct\",\"rcm_count\"]\n", " return dict(zip(cols, row))\n", "\n", "meta = query_race_metadata(CONN, 2025, \"Miami\")\n", "print(\"Miami 2025 \u2014 Race conditions (from JSON document store):\")\n", "for k, v in meta.items():\n", " print(f\" {k:22s}: {v}\")\n" ] }, { "cell_type": "markdown", "id": "md_26", "metadata": {}, "source": [ "---\n", "## Paradigm 4: Property Graph \u2014 Driver Career Network\n", "\n", "**Oracle features:** `GRAPH_TABLE()`, `SQL Property Graph`, `MATCH` pattern syntax\n", "\n", "Which drivers who competed at Miami share team history with the race winner?\n", "\n", "In standard SQL this requires self-joins across `driver_teams`.\n", "With Oracle's SQL Property Graph you describe a path pattern and Oracle walks the graph:\n", "\n", "```sql\n", "(driver) -[DROVE_FOR]-> (team) <-[DROVE_FOR]- (teammate)\n", "```\n", "\n", "No Neo4j. No Cypher to learn. Plain SQL \u2014 with graph traversal built in.\n", "A fallback to SQL JOINs is included for environments where `GRAPH_TABLE()`\n", "is not yet available." ] }, { "cell_type": "code", "execution_count": 16, "id": "code_27", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:21.596812Z", "iopub.status.busy": "2026-04-15T10:43:21.596696Z", "iopub.status.idle": "2026-04-15T10:43:21.616500Z", "shell.execute_reply": "2026-04-15T10:43:21.615883Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Miami 2025 winner: PIA\n", " \u2139\ufe0f GRAPH_TABLE() not available \u2014 using SQL JOIN equivalent\n", "\n", "PIA career network (teams \u2192 circuits):\n", " TEAM CIRCUIT SEASON BEST_FINISH WINS PODIUMS\n", "McLaren Shanghai 2025 1 4 5\n", "McLaren Jeddah 2025 1 4 5\n", "McLaren Miami Gardens 2025 1 4 5\n", "McLaren Sakhir 2025 1 4 5\n", "McLaren Suzuka 2025 2 4 5\n", "McLaren Miami 2024 1 0 2\n", "McLaren Monaco 2024 2 0 2\n", "McLaren Spielberg 2024 2 0 2\n", "McLaren Barcelona 2024 2 0 2\n", "McLaren Montr\u00e9al 2024 2 0 2\n", "McLaren Shanghai 2024 2 0 2\n", "McLaren Jeddah 2024 4 0 2\n", "McLaren Suzuka 2024 5 0 2\n", "McLaren Sakhir 2024 6 0 2\n", "McLaren Marina Bay 2023 2 0 0\n", "McLaren Yas Island 2023 5 0 0\n", "McLaren Monaco 2023 9 0 0\n", "McLaren Las Vegas 2023 10 0 0\n", "McLaren Miami 2023 17 0 0\n", "\n", "PIA 2024 teammates:\n", "TEAMMATE TEAM WINS PODIUMS\n", " NOR McLaren 1 4\n" ] } ], "source": [ "def query_driver_network(conn, driver: str) -> pd.DataFrame:\n", " \"\"\"\n", " Walk the driver career graph: Driver \u2192 Teams \u2192 Circuits.\n", " Uses GRAPH_TABLE() with a fallback to SQL JOINs.\n", " \"\"\"\n", " graph_sql = \"\"\"\n", " SELECT gt.team, gt.circuit, gt.season, gt.best_finish\n", " FROM GRAPH_TABLE(driver_career\n", " MATCH (d IS Driver)\n", " -[e1 IS DROVE_FOR]-> (t IS Team)\n", " -[e2 IS RACED_AT]-> (c IS Circuit)\n", " WHERE d.driver = :drv\n", " COLUMNS (t.team AS team, c.circuit AS circuit,\n", " e1.season AS season, e2.best_finish AS best_finish)\n", " ) gt\n", " ORDER BY season DESC, best_finish\n", " \"\"\"\n", " fallback_sql = \"\"\"\n", " SELECT dt.team, tc.circuit, dt.season, tc.best_finish,\n", " dt.wins, dt.podiums\n", " FROM driver_teams dt\n", " JOIN team_circuits tc ON dt.team=tc.team AND dt.season=tc.season\n", " WHERE UPPER(dt.driver) = UPPER(:drv)\n", " ORDER BY dt.season DESC, tc.best_finish\n", " \"\"\"\n", " try:\n", " return pd.read_sql(graph_sql, conn, params={\"drv\": driver.upper()})\n", " except Exception:\n", " print(\" \u2139\ufe0f GRAPH_TABLE() not available \u2014 using SQL JOIN equivalent\")\n", " return pd.read_sql(fallback_sql, conn, params={\"drv\": driver})\n", "\n", "def query_teammates(conn, driver: str, season: int) -> pd.DataFrame:\n", " \"\"\"Drivers who shared a constructor with 'driver' in 'season'.\"\"\"\n", " return pd.read_sql(\"\"\"\n", " SELECT dt2.driver AS teammate, dt2.team, dt2.wins, dt2.podiums\n", " FROM driver_teams dt1\n", " JOIN driver_teams dt2\n", " ON dt1.team=dt2.team AND dt1.season=dt2.season\n", " AND UPPER(dt2.driver) <> UPPER(dt1.driver)\n", " WHERE UPPER(dt1.driver)=UPPER(:drv) AND dt1.season=:s\n", " ORDER BY dt2.wins DESC\n", " \"\"\", conn, params={\"drv\": driver, \"s\": season})\n", "\n", "# Safely get the winner from the earlier query\n", "try:\n", " WINNER = str(df_winner[\"DRIVER\"].iloc[0])\n", "except Exception:\n", " WINNER = \"VER\"\n", " print(\"\u26a0\ufe0f Could not determine winner \u2014 defaulting to VER. Run Section 4 first.\")\n", "\n", "print(f\"Miami 2025 winner: {WINNER}\")\n", "df_network = query_driver_network(CONN, WINNER)\n", "df_teammates = query_teammates(CONN, WINNER, 2024)\n", "\n", "print(f\"\\n{WINNER} career network (teams \u2192 circuits):\")\n", "print(df_network.to_string(index=False))\n", "print(f\"\\n{WINNER} 2024 teammates:\")\n", "print(df_teammates.to_string(index=False))\n" ] }, { "cell_type": "markdown", "id": "md_overperf", "metadata": {}, "source": [ "### Driver Overperformance \u2014 Who Beat Their Car?\n", "\n", "This is the question the LinkedIn post refers to: *\"which drivers tend to overperform their strategy?\"*\n", "\n", "We answer it with a **convergent query** that joins two paradigms simultaneously:\n", "\n", "- **Property Graph** (`driver_teams`) \u2014 a driver's season podium count tells us how strong their machinery was\n", "- **Relational SQL** (`race_laps`) \u2014 their actual finishing positions tell us what they achieved\n", "\n", "A driver who consistently finishes *ahead* of where their team's machinery should put them\n", "is overperforming relative to their car. This is the metric race engineers use to evaluate\n", "driver quality independent of car performance.\n", "\n", "> **Why this needs the graph:** The podium baseline comes from traversing the `DRIVER_CAREER`\n", "> graph to find a driver's team, then aggregating that team's results across all races.\n", "> Without the graph, you'd need multiple self-joins and a subquery to establish the baseline.\n", "> Oracle 26ai makes this a single query." ] }, { "cell_type": "code", "execution_count": 17, "id": "code_overperf", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:21.618449Z", "iopub.status.busy": "2026-04-15T10:43:21.618220Z", "iopub.status.idle": "2026-04-15T10:43:22.017409Z", "shell.execute_reply": "2026-04-15T10:43:22.016585Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Miami 2025 \u2014 Driver overperformance vs. team machinery baseline:\n", "(Positive = finished better than their car typically allows)\n", "\n", "DRIVER TEAM ACTUAL_FINISH TEAM_EXPECTED_FINISH OVERPERFORMANCE TEAM_PODIUMS\n", " HUL Kick Sauber 9 16.8 7.8 0\n", " VER Red Bull Racing 1 7.1 6.1 18\n", " ALB Williams 5 10.8 5.8 0\n", " SAI Williams 6 10.8 4.8 0\n", " BOR Kick Sauber 13 16.8 3.8 0\n", " ANT Mercedes 2 5.6 3.6 30\n", " HAD Racing Bulls 11 13.7 2.7 0\n", " RUS Mercedes 3 5.6 2.6 30\n", " GAS Alpine 11 13.0 2.0 0\n", " ALO Aston Martin 14 15.7 1.7 0\n", " STR Aston Martin 14 15.7 1.7 0\n", " LEC Ferrari 4 5.6 1.6 10\n", " OCO Haas F1 Team 10 11.5 1.5 0\n", " TSU Red Bull Racing 6 7.1 1.1 18\n", " PIA McLaren 1 1.9 0.9 90\n", " NOR McLaren 2 1.9 -0.1 90\n", " HAM Ferrari 6 5.6 -0.4 10\n", " BEA Haas F1 Team 12 11.5 -0.5 0\n", " LAW Racing Bulls 17 13.7 -3.3 0\n", "\n", "\u26a0\ufe0f Baseline = team avg finishing position across all loaded 2024 races.\n" ] }, { "data": { "application/vnd.plotly.v1+json": { "config": { "plotlyServerURL": "https://plot.ly" }, "data": [ { "customdata": [ [ "9", "16.8" ], [ "1", "7.1" ], [ "5", "10.8" ], [ "6", "10.8" ], [ "13", "16.8" ], [ "2", "5.6" ], [ "11", "13.7" ], [ "3", "5.6" ], [ "11", "13.0" ], [ "14", "15.7" ], [ "14", "15.7" ], [ "4", "5.6" ], [ "10", "11.5" ], [ "6", "7.1" ], [ "1", "1.9" ], [ "2", "1.9" ], [ "6", "5.6" ], [ "12", "11.5" ], [ "17", "13.7" ] ], "hovertemplate": "%{y}
Actual finish: P%{customdata[0]}
Team expected: P%{customdata[1]}
Overperformance: %{x:.1f} positions", "marker": { "color": [ "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#00E676", "#E8002D", "#E8002D", "#E8002D", "#E8002D" ], "line": { "color": "#111", "width": 0.8 } }, "orientation": "h", "text": [ "+7.8", "+6.1", "+5.8", "+4.8", "+3.8", "+3.6", "+2.7", "+2.6", "+2.0", "+1.7", "+1.7", "+1.6", "+1.5", "+1.1", "+0.9", "-0.1", "-0.4", "-0.5", "-3.3" ], "textposition": "outside", "type": "bar", "x": [ 7.8, 6.1, 5.8, 4.8, 3.8, 3.6, 2.7, 2.6, 2.0, 1.7, 1.7, 1.6, 1.5, 1.1, 0.9, -0.1, -0.4, -0.5, -3.3 ], "y": [ "HUL (Kick Sauber)", "VER (Red Bull Racing)", "ALB (Williams)", "SAI (Williams)", "BOR (Kick Sauber)", "ANT (Mercedes)", "HAD (Racing Bulls)", "RUS (Mercedes)", "GAS (Alpine)", "ALO (Aston Martin)", "STR (Aston Martin)", "LEC (Ferrari)", "OCO (Haas F1 Team)", "TSU (Red Bull Racing)", "PIA (McLaren)", "NOR (McLaren)", "HAM (Ferrari)", "BEA (Haas F1 Team)", "LAW (Racing Bulls)" ] } ], "layout": { "font": { "color": "white", "size": 11 }, "height": 420, "margin": { "b": 50, "l": 220, "r": 80, "t": 80 }, "paper_bgcolor": "#0a0a14", "plot_bgcolor": "#0f0f1a", "shapes": [ { "line": { "color": "#555", "width": 1.5 }, "type": "line", "x0": 0, "x1": 0, "xref": "x", "y0": 0, "y1": 1, "yref": "y domain" } ], "template": { "data": { "bar": [ { "error_x": { "color": "#2a3f5f" }, "error_y": { "color": "#2a3f5f" }, "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "bar" } ], "barpolar": [ { "marker": { "line": { "color": "#E5ECF6", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "barpolar" } ], "carpet": [ { "aaxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "baxis": { "endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f" }, "type": "carpet" } ], "choropleth": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "choropleth" } ], "contour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "contour" } ], "contourcarpet": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "contourcarpet" } ], "heatmap": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "heatmap" } ], "heatmapgl": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "heatmapgl" } ], "histogram": [ { "marker": { "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "histogram" } ], "histogram2d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "histogram2d" } ], "histogram2dcontour": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "histogram2dcontour" } ], "mesh3d": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "mesh3d" } ], "parcoords": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "parcoords" } ], "pie": [ { "automargin": true, "type": "pie" } ], "scatter": [ { "fillpattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 }, "type": "scatter" } ], "scatter3d": [ { "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatter3d" } ], "scattercarpet": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattercarpet" } ], "scattergeo": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergeo" } ], "scattergl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergl" } ], "scattermapbox": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattermapbox" } ], "scatterpolar": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolar" } ], "scatterpolargl": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolargl" } ], "scatterternary": [ { "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterternary" } ], "surface": [ { "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "type": "surface" } ], "table": [ { "cells": { "fill": { "color": "#EBF0F8" }, "line": { "color": "white" } }, "header": { "fill": { "color": "#C8D4E3" }, "line": { "color": "white" } }, "type": "table" } ] }, "layout": { "annotationdefaults": { "arrowcolor": "#2a3f5f", "arrowhead": 0, "arrowwidth": 1 }, "autotypenumbers": "strict", "coloraxis": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "colorscale": { "diverging": [ [ 0, "#8e0152" ], [ 0.1, "#c51b7d" ], [ 0.2, "#de77ae" ], [ 0.3, "#f1b6da" ], [ 0.4, "#fde0ef" ], [ 0.5, "#f7f7f7" ], [ 0.6, "#e6f5d0" ], [ 0.7, "#b8e186" ], [ 0.8, "#7fbc41" ], [ 0.9, "#4d9221" ], [ 1, "#276419" ] ], "sequential": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ], "sequentialminus": [ [ 0.0, "#0d0887" ], [ 0.1111111111111111, "#46039f" ], [ 0.2222222222222222, "#7201a8" ], [ 0.3333333333333333, "#9c179e" ], [ 0.4444444444444444, "#bd3786" ], [ 0.5555555555555556, "#d8576b" ], [ 0.6666666666666666, "#ed7953" ], [ 0.7777777777777778, "#fb9f3a" ], [ 0.8888888888888888, "#fdca26" ], [ 1.0, "#f0f921" ] ] }, "colorway": [ "#636efa", "#EF553B", "#00cc96", "#ab63fa", "#FFA15A", "#19d3f3", "#FF6692", "#B6E880", "#FF97FF", "#FECB52" ], "font": { "color": "#2a3f5f" }, "geo": { "bgcolor": "white", "lakecolor": "white", "landcolor": "#E5ECF6", "showlakes": true, "showland": true, "subunitcolor": "white" }, "hoverlabel": { "align": "left" }, "hovermode": "closest", "mapbox": { "style": "light" }, "paper_bgcolor": "white", "plot_bgcolor": "#E5ECF6", "polar": { "angularaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "radialaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "scene": { "xaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "yaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" }, "zaxis": { "backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white" } }, "shapedefaults": { "line": { "color": "#2a3f5f" } }, "ternary": { "aaxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "baxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" }, "bgcolor": "#E5ECF6", "caxis": { "gridcolor": "white", "linecolor": "white", "ticks": "" } }, "title": { "x": 0.05 }, "xaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 }, "yaxis": { "automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "white", "zerolinewidth": 2 } } }, "title": { "text": "\ud83d\udcc8 Driver Overperformance vs. Team Baseline \u2014 Miami GP 2025
Oracle AI Database 26ai | Paradigm 4: Property Graph + Relational SQL | Green = beat their car \u00b7 Red = underperformed" }, "xaxis": { "gridcolor": "#2a2a3e", "title": { "text": "Positions gained vs. team baseline" }, "zeroline": false } } }, "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "\ud83d\udd0d Key findings:\n", " Best: HUL overperformed by +7.8 positions\n", " Finished P9 \u2014 car baseline is ~P16.8\n", " Worst: LAW underperformed by -3.3 positions\n", " Finished P17 \u2014 car baseline is ~P13.7\n" ] } ], "source": [ "def query_driver_overperformance(conn, season: int, race_name: str) -> pd.DataFrame:\n", " \"\"\"\n", " Find drivers who finished ABOVE their team's baseline finishing position.\n", "\n", " Convergent query combining:\n", " - Property Graph (driver_teams): identifies which team each driver belongs to\n", " - Relational SQL (race_laps): actual finishing positions across all loaded races\n", "\n", " Team baseline = average finishing position of all team drivers across loaded races\n", " that season. This is the \"where does this car normally finish?\" metric.\n", "\n", " Overperformance = team_avg_finish - driver_actual_finish\n", " Positive = driver finished AHEAD of where their car typically puts them.\n", " Negative = driver finished BEHIND where their car should be.\n", "\n", " This is the 'which drivers beat their car' metric from the LinkedIn post.\n", " \"\"\"\n", " sql = \"\"\"\n", " WITH\n", " -- Step 1: Use driver_teams (Property Graph edge table) to link drivers to teams\n", " -- Then compute each team's average finishing position across all loaded races\n", " team_baseline AS (\n", " SELECT\n", " dt.team,\n", " dt.season,\n", " SUM(dt.podiums) AS team_podiums,\n", " -- Compute real team baseline from actual race finishing positions\n", " ROUND(AVG(rl.position), 1) AS expected_finish\n", " FROM driver_teams dt\n", " JOIN race_laps rl\n", " ON UPPER(dt.team) = UPPER(rl.team)\n", " AND dt.season = rl.season\n", " WHERE dt.season = :season\n", " AND rl.position BETWEEN 1 AND 20\n", " -- Use final-lap positions only (best approximation of race result)\n", " AND rl.lap_number = (\n", " SELECT MAX(rl2.lap_number)\n", " FROM race_laps rl2\n", " WHERE rl2.season = rl.season\n", " AND rl2.race_name = rl.race_name\n", " AND rl2.driver = rl.driver\n", " )\n", " GROUP BY dt.team, dt.season\n", " ),\n", " -- Step 2: Relational SQL \u2014 actual final position per driver at this race\n", " driver_results AS (\n", " SELECT driver, team,\n", " MIN(position) AS actual_finish\n", " FROM race_laps\n", " WHERE season = :season\n", " AND race_name = :race\n", " AND position BETWEEN 1 AND 20\n", " GROUP BY driver, team\n", " )\n", " -- Step 3: Join and compute overperformance\n", " SELECT\n", " dr.driver,\n", " dr.team,\n", " dr.actual_finish,\n", " tb.expected_finish AS team_expected_finish,\n", " ROUND(tb.expected_finish - dr.actual_finish, 1) AS overperformance,\n", " tb.team_podiums\n", " FROM driver_results dr\n", " JOIN team_baseline tb\n", " ON UPPER(dr.team) = UPPER(tb.team)\n", " AND tb.season = :season\n", " WHERE dr.actual_finish IS NOT NULL\n", " ORDER BY overperformance DESC\n", " \"\"\"\n", " return pd.read_sql(sql, conn, params={\"season\": season, \"race\": race_name})\n", "\n", "df_overperf = query_driver_overperformance(CONN, 2025, \"Miami\")\n", "\n", "print(\"Miami 2025 \u2014 Driver overperformance vs. team machinery baseline:\")\n", "print(\"(Positive = finished better than their car typically allows)\\n\")\n", "print(df_overperf.to_string(index=False))\n", "print(\"\\n\u26a0\ufe0f Baseline = team avg finishing position across all loaded 2024 races.\")\n", "\n", "# Visualise\n", "colors = [\"#00E676\" if v > 0 else \"#E8002D\" for v in df_overperf[\"OVERPERFORMANCE\"]]\n", "\n", "fig_op = go.Figure(go.Bar(\n", " x=df_overperf[\"OVERPERFORMANCE\"],\n", " y=df_overperf[\"DRIVER\"] + \" (\" + df_overperf[\"TEAM\"].str[:15] + \")\",\n", " orientation=\"h\",\n", " marker_color=colors,\n", " marker_line=dict(color=\"#111\", width=0.8),\n", " text=[f\"+{v:.1f}\" if v > 0 else f\"{v:.1f}\" for v in df_overperf[\"OVERPERFORMANCE\"]],\n", " textposition=\"outside\",\n", " customdata=list(zip(\n", " df_overperf[\"ACTUAL_FINISH\"].astype(str),\n", " df_overperf[\"TEAM_EXPECTED_FINISH\"].astype(str),\n", " )),\n", " hovertemplate=(\n", " \"%{y}
\"\n", " \"Actual finish: P%{customdata[0]}
\"\n", " \"Team expected: P%{customdata[1]}
\"\n", " \"Overperformance: %{x:.1f} positions\"\n", " ),\n", "))\n", "\n", "fig_op.add_vline(x=0, line_color=\"#555\", line_width=1.5)\n", "\n", "fig_op.update_layout(\n", " title=(\n", " \"\ud83d\udcc8 Driver Overperformance vs. Team Baseline \u2014 Miami GP 2025
\"\n", " \"Oracle AI Database 26ai | Paradigm 4: Property Graph + Relational SQL | \"\n", " \"Green = beat their car \u00b7 Red = underperformed\"\n", " ),\n", " xaxis=dict(title=\"Positions gained vs. team baseline\", gridcolor=\"#2a2a3e\",\n", " zeroline=False),\n", " plot_bgcolor=\"#0f0f1a\", paper_bgcolor=\"#0a0a14\",\n", " font=dict(color=\"white\", size=11),\n", " height=420, margin=dict(l=220, r=80, t=80, b=50),\n", ")\n", "fig_op.show()\n", "\n", "# Surface the most interesting finding\n", "if len(df_overperf) > 0:\n", " top = df_overperf.iloc[0]\n", " bottom = df_overperf.iloc[-1]\n", " print(f\"\\n\ud83d\udd0d Key findings:\")\n", " print(f\" Best: {top['DRIVER']} overperformed by +{top['OVERPERFORMANCE']:.1f} positions\")\n", " print(f\" Finished P{int(top['ACTUAL_FINISH'])} \u2014 car baseline is ~P{top['TEAM_EXPECTED_FINISH']}\")\n", " print(f\" Worst: {bottom['DRIVER']} underperformed by {bottom['OVERPERFORMANCE']:.1f} positions\")\n", " print(f\" Finished P{int(bottom['ACTUAL_FINISH'])} \u2014 car baseline is ~P{bottom['TEAM_EXPECTED_FINISH']}\")\n" ] }, { "cell_type": "markdown", "id": "md_28", "metadata": {}, "source": [ "---\n", "## \u26a1 Convergent \u2014 All Four Paradigms Combined\n", "\n", "One function call returning a complete race intelligence brief.\n", "\n", "In a Postgres + Pinecone + Neo4j + MongoDB stack this would be four separate\n", "API calls, four network round-trips, and Python code to join the results.\n", "\n", "Here it's one Oracle connection, one Python function, four paradigms." ] }, { "cell_type": "code", "execution_count": 18, "id": "code_29", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:22.019617Z", "iopub.status.busy": "2026-04-15T10:43:22.019408Z", "iopub.status.idle": "2026-04-15T10:43:22.340566Z", "shell.execute_reply": "2026-04-15T10:43:22.338774Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " \u2139\ufe0f GRAPH_TABLE() not available \u2014 using SQL JOIN equivalent\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "============================================================\n", "MIAMI GP 2025 \u2014 STRATEGY INTEL FOR 2026 \u2014 RACE INTELLIGENCE BRIEF\n", "============================================================\n", "\n", "\ud83c\udfc6 Winner: PIA\n", "\n", "Tyre strategy:\n", " MEDIUM laps 25\u201332 (8 laps | avg 97.414s)\n", " HARD laps 33\u201357 (25 laps | avg 91.436s)\n", "\n", "Race conditions (JSON store):\n", " Track: 38.7\u00b0C | Air: 26.6\u00b0C | Rain: true | Humidity: 63%\n", "\n", "Most similar races (hybrid vector + keyword search):\n", " Austria 2024 | RRF: 31.01 (semantic: 15.38, keyword: 15.63)\n", " Canada 2024 | RRF: 30.68 (semantic: 14.29, keyword: 16.39)\n", " Monaco 2023 | RRF: 30.58 (semantic: 15.87, keyword: 14.71)\n", "\n", "Winner career network (Property Graph):\n", " McLaren \u2192 Shanghai\n", " McLaren \u2192 Jeddah\n", " McLaren \u2192 Miami Gardens\n", " McLaren \u2192 Sakhir\n", "\n", "Driver overperformance (Property Graph + SQL):\n", " HUL: P9 finish, +7.8 vs car baseline\n", " VER: P1 finish, +6.1 vs car baseline\n", " ALB: P5 finish, +5.8 vs car baseline\n", "\n", "\u2705 All four Oracle AI Database 26ai paradigms \u2014 one database\n" ] } ], "source": [ "def race_brief(conn, season: int, race_name: str, query_vec: array.array) -> dict:\n", " \"\"\"\n", " Full race intelligence brief using all four Oracle AI Database 26ai paradigms.\n", " Returns a structured dict \u2014 print it, pass it to an LLM, or export it.\n", " \"\"\"\n", " winner_df = query_winner_strategy(conn, season, race_name)\n", " winner = str(winner_df[\"DRIVER\"].iloc[0]) if len(winner_df) > 0 else \"Unknown\"\n", " return {\n", " \"race\": {\"season\": season, \"name\": race_name},\n", " \"winner\": winner,\n", " \"strategy\": winner_df.to_dict(\"records\"), # Paradigm 1\n", " \"positions\": query_position_changes(conn, season, race_name).head(5).to_dict(\"records\"), # Paradigm 1\n", " \"metadata\": query_race_metadata(conn, season, race_name), # Paradigm 3\n", " \"similar\": hybrid_search(conn, query_vec, season, race_name, top_k=3).to_dict(\"records\"), # Paradigm 2\n", " \"network\": query_driver_network(conn, winner).head(6).to_dict(\"records\"), # Paradigm 4\n", " \"overperformance\": query_driver_overperformance(conn, season, race_name).head(5).to_dict(\"records\"), # Paradigm 4 + 1\n", " }\n", "\n", "brief = race_brief(CONN, 2025, \"Miami\", QUERY_VEC)\n", "\n", "print(\"=\" * 60)\n", "print(\"MIAMI GP 2025 \u2014 STRATEGY INTEL FOR 2026 \u2014 RACE INTELLIGENCE BRIEF\")\n", "print(\"=\" * 60)\n", "\n", "print(f\"\\n\ud83c\udfc6 Winner: {brief['winner']}\")\n", "print(\"\\nTyre strategy:\")\n", "for s in brief[\"strategy\"]:\n", " print(f\" {s.get('COMPOUND','?'):12s} laps {s.get('STINT_START','?')}\u2013\"\n", " f\"{s.get('STINT_END','?')} ({s.get('STINT_LAPS','?')} laps | \"\n", " f\"avg {s.get('AVG_LAP_S','?')}s)\")\n", "\n", "print(\"\\nRace conditions (JSON store):\")\n", "m = brief[\"metadata\"]\n", "print(f\" Track: {m.get('track_temp_c','?')}\u00b0C | \"\n", " f\"Air: {m.get('air_temp_c','?')}\u00b0C | \"\n", " f\"Rain: {m.get('rainfall','?')} | \"\n", " f\"Humidity: {m.get('humidity_pct','?')}%\")\n", "\n", "print(\"\\nMost similar races (hybrid vector + keyword search):\")\n", "for r in brief[\"similar\"]:\n", " print(f\" {r.get('race_name','?'):15s} {r.get('season','?')} | \"\n", " f\"RRF: {r.get('rrf_score','?')} \"\n", " f\"(semantic: {r.get('vec_score','?')}, keyword: {r.get('text_score','?')})\")\n", "\n", "print(f\"\\nWinner career network (Property Graph):\")\n", "for n in brief[\"network\"][:4]:\n", " print(f\" {n.get('TEAM',n.get('team','?')):30s} \u2192 \"\n", " f\"{n.get('CIRCUIT',n.get('circuit','?'))}\")\n", "\n", "print(\"\\nDriver overperformance (Property Graph + SQL):\")\n", "for o in brief.get(\"overperformance\", [])[:3]:\n", " gained = o.get(\"OVERPERFORMANCE\", o.get(\"overperformance\", 0))\n", " driver = o.get(\"DRIVER\", o.get(\"driver\", \"?\"))\n", " actual = o.get(\"ACTUAL_FINISH\", o.get(\"actual_finish\", \"?\"))\n", " print(f\" {driver}: P{actual} finish, {gained:+.1f} vs car baseline\")\n", "\n", "print(\"\\n\u2705 All four Oracle AI Database 26ai paradigms \u2014 one database\")\n" ] }, { "cell_type": "markdown", "id": "md_30", "metadata": {}, "source": [ "---\n", "## Summary\n", "\n", "### What we built\n", "\n", "A race strategy intelligence tool for the **upcoming Miami GP 2026** powered by all four\n", "Oracle AI Database 26ai paradigms using real FastF1 data.\n", "\n", "| Paradigm | Oracle Feature | Question |\n", "|---|---|---|\n", "| Relational SQL | CTEs, `ROW_NUMBER() OVER` | What tyre strategy wins at Miami? (2022\u20132025) |\n", "| Hybrid Search | `VECTOR_DISTANCE()` + `CONTAINS()` + RRF | Which past races felt like Miami 2025? |\n", "| JSON Document | `CLOB IS JSON` + `JSON_VALUE()` | What were the conditions? |\n", "| Property Graph | `GRAPH_TABLE()` + `MATCH` | Which drivers beat their car? (overperformance vs. team baseline) |\n", "\n", "### The fragmented stack this replaces\n", "\n", "```\n", "PostgreSQL \u2192 race_laps, pit_stops\n", "Pinecone \u2192 race_events embeddings + HNSW search\n", "Elasticsearch \u2192 race control message keyword search\n", "Neo4j \u2192 driver career graph\n", "MongoDB \u2192 race_metadata JSON documents\n", "```\n", "\n", "Oracle AI Database 26ai replaces all five. One connection string.\n", "Same query language. No glue code.\n", "\n", "### Extend this notebook\n", "\n", "- Add qualifying data: `fastf1.get_session(season, race, 'Q')`\n", "- Load team radio transcripts via [OpenF1 API](https://openf1.org/) for richer semantic fingerprints\n", "- Use `DBMS_VECTOR` to generate embeddings inside Oracle \u2014 eliminating the Python embedding step entirely\n", "- Deploy on [Oracle Autonomous Database Free Tier](https://www.oracle.com/autonomous-database/free-trial/) for cloud-hosted persistent access\n", "\n", "### Resources\n", "\n", "- [Oracle AI Vector Search Docs](https://docs.oracle.com/en/database/oracle/oracle-database/26/vecse/)\n", "- [Oracle SQL Property Graph](https://docs.oracle.com/en/database/oracle/oracle-database/23/spgdg/)\n", "- [FastF1 Documentation](https://docs.fastf1.dev/)\n", "- [\u2b50 oracle-ai-developer-hub](https://github.com/oracle-devrel/oracle-ai-developer-hub) \u2014 star the repo if this was useful!\n", "\n", "---\n", "*Built by [Mariana Antaya](https://www.instagram.com/mar_antaya/) \u00b7\n", "Contributed to [oracle-devrel/oracle-ai-developer-hub](https://github.com/oracle-devrel/oracle-ai-developer-hub)*\n" ] }, { "cell_type": "code", "execution_count": 19, "id": "code_31", "metadata": { "execution": { "iopub.execute_input": "2026-04-15T10:43:22.344750Z", "iopub.status.busy": "2026-04-15T10:43:22.344423Z", "iopub.status.idle": "2026-04-15T10:43:22.350558Z", "shell.execute_reply": "2026-04-15T10:43:22.349318Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u2705 Notebook complete.\n", " Oracle container 'f1_oracle' still running.\n", " Reconnect any time: CONN = connect_oracle()\n", "\n", "\u2b50 Star the repo: https://github.com/oracle-devrel/oracle-ai-developer-hub\n" ] } ], "source": [ "# Cleanup \u2014 uncomment to stop the Oracle container when done\n", "# import subprocess\n", "# subprocess.run([\"docker\", \"stop\", CONTAINER])\n", "# print(f\"\ud83d\uded1 Container '{CONTAINER}' stopped. Data persisted in Docker volume.\")\n", "\n", "print(\"\u2705 Notebook complete.\")\n", "print(f\" Oracle container '{CONTAINER}' still running.\")\n", "print(f\" Reconnect any time: CONN = connect_oracle()\")\n", "print(f\"\\n\u2b50 Star the repo: https://github.com/oracle-devrel/oracle-ai-developer-hub\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.12.13" } }, "nbformat": 4, "nbformat_minor": 5 }