{ "cells": [ { "cell_type": "markdown", "id": "66874ad8", "metadata": {}, "source": [ "# 🧭 More sources with `sportsdataverse-py`\n", "\n", "Four smaller surfaces, one notebook:\n", "\n", "| Surface | Module | Provider |\n", "|---|---|---|\n", "| NBA G League | `sportsdataverse.nbagl` | ESPN |\n", "| Formula 1 | `sportsdataverse.f1` | Jolpica (the Ergast API successor) |\n", "| TheSportsDB | `sportsdataverse.thesportsdb` | TheSportsDB (free tier) |\n", "| ESPN content | `sportsdataverse.espn_content` | ESPN news API |\n", "\n", "No API keys needed." ] }, { "cell_type": "markdown", "id": "aa952643", "metadata": {}, "source": [ "## 🧰 The toolbox\n", "\n", "| Function | What it gives you |\n", "|---|---|\n", "| [`espn_nbagl_standings`](../nbagl/reference/site.md#espn_nbagl_standings) | G League standings |\n", "| [`espn_nbagl_scoreboard`](../nbagl/reference/site.md#espn_nbagl_scoreboard) | G League scoreboard |\n", "| [`f1_schedule`](../f1/reference/f1.md#f1_schedule) | Race calendar of a season |\n", "| [`f1_driver_standings`](../f1/reference/f1.md#f1_driver_standings) | Drivers' championship |\n", "| [`f1_results`](../f1/reference/f1.md#f1_results) | Race classification |\n", "| [`f1_laps`](../f1/reference/additional.md#f1_laps) | Every lap of a race, all pages |\n", "| [`thesportsdb_leagues`](../thesportsdb/reference/thesportsdb.md#thesportsdb_leagues) | Leagues TheSportsDB knows |\n", "| [`thesportsdb_table`](../thesportsdb/reference/thesportsdb.md#thesportsdb_table) | A league table |\n", "| [`espn_content_news`](../espn_content/reference/espn_content.md#espn_content_news) | Cross-sport headlines |\n", "| [`espn_content_league_news`](../espn_content/reference/espn_content.md#espn_content_league_news) | Headlines for one league |" ] }, { "cell_type": "markdown", "id": "4d139182", "metadata": {}, "source": [ "## 🔌 Setup\n", "\n", "```sh\n", "pip install \"sportsdataverse[sdvplot]\"\n", "```" ] }, { "cell_type": "code", "execution_count": null, "id": "506cf27e", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import polars as pl\n", "\n", "from sportsdataverse import espn_content, f1, nbagl, thesportsdb\n", "\n", "pl.Config.set_tbl_rows(10)\n", "pl.Config.set_fmt_str_lengths(60)" ] }, { "cell_type": "markdown", "id": "4291f46b", "metadata": {}, "source": [ "Provider feeds are seasonal and occasionally rate-limited, so a tiny `safe()` helper runs each call\n", "defensively: you get the frame when the feed is up, and a one-line skip when it isn't, naming whether\n", "the answer was *nothing there* (`NoDataError`) or *the fetch failed* (`AssetFetchError`). 🛟" ] }, { "cell_type": "code", "execution_count": null, "id": "302328f1", "metadata": {}, "outputs": [], "source": [ "from sportsdataverse.errors import AssetFetchError, NoDataError\n", "\n", "def safe(label, thunk):\n", " try:\n", " out = thunk()\n", " print(f\"✅ {label}\")\n", " return out\n", " except (NoDataError, AssetFetchError) as e:\n", " print(f\"\\u23ed\\ufe0f {label}: {type(e).__name__}: {e}\")\n", " return None" ] }, { "cell_type": "markdown", "id": "8b43bb6a", "metadata": {}, "source": [ "## 🏀 NBA G League\n", "\n", "The G League rides the cross-league ESPN wrappers (`espn_nbagl_*`).\n", "[`espn_nbagl_standings`](../nbagl/reference/site.md#espn_nbagl_standings) takes a `season` (END year),\n", "and sdvplot's `team_colors(\"nbagl\", ...)` resolves ESPN team ids to each team's color.\n", "\n", "The `nbagl_enhanced_pbp` / `nbagl_possessions` / `nbagl_rapm_from_games` helpers in the same module\n", "read stats.nba.com, which blocks datacenter IPs, so this notebook does not call them." ] }, { "cell_type": "code", "execution_count": null, "id": "1565fb2a", "metadata": {}, "outputs": [], "source": [ "import sdvplot\n", "\n", "gst = safe(\"G League standings 2026\", lambda: nbagl.espn_nbagl_standings(season=2026))\n", "if gst is not None and gst.height:\n", " top = gst.sort(\"win_percent\", \"point_differential\")\n", " colors = [c or \"#999999\" for c in sdvplot.team_colors(\"nbagl\", [str(i) for i in top[\"team_id\"]])]\n", " fig, ax = plt.subplots(figsize=(7, 8))\n", " ax.barh(top[\"team_display_name\"], top[\"win_percent\"], color=colors)\n", " ax.set_xlabel(\"win %\")\n", " ax.set_title(\"G League 2025-26 standings\")\n", " plt.tight_layout()\n", " plt.show()\n", "else:\n", " print(\"standings unavailable\")" ] }, { "cell_type": "code", "execution_count": null, "id": "e548045a", "metadata": {}, "outputs": [], "source": [ "gsb = safe(\"G League scoreboard\", nbagl.espn_nbagl_scoreboard)\n", "cols = [\"date\", \"short_name\", \"status_type_detail\", \"home_score\", \"away_score\"]\n", "gsb.select([c for c in cols if c in gsb.columns]).head() if gsb is not None and gsb.height else \"no games today\"" ] }, { "cell_type": "markdown", "id": "960b9382", "metadata": {}, "source": [ "## 🏎️ Formula 1\n", "\n", "The F1 helpers take `season` and `round` as strings.\n", "[`f1_driver_standings`](../f1/reference/f1.md#f1_driver_standings) returns the table after a season's\n", "last round; [`f1_laps`](../f1/reference/additional.md#f1_laps) pages through every lap of a race." ] }, { "cell_type": "code", "execution_count": null, "id": "80d83473", "metadata": {}, "outputs": [], "source": [ "F1_SEASON = \"2025\"\n", "dst = safe(f\"F1 driver standings {F1_SEASON}\", lambda: f1.f1_driver_standings(F1_SEASON))\n", "cols = [\"position\", \"driver_given_name\", \"driver_family_name\", \"constructors\", \"points\", \"wins\"]\n", "dst.select([c for c in cols if c in dst.columns]).head(10) if dst is not None else None" ] }, { "cell_type": "code", "execution_count": null, "id": "86881450", "metadata": {}, "outputs": [], "source": [ "laps = safe(f\"F1 laps {F1_SEASON} round 1\", lambda: f1.f1_laps(F1_SEASON, \"1\"))\n", "res = safe(f\"F1 results {F1_SEASON} round 1\", lambda: f1.f1_results(F1_SEASON, \"1\"))\n", "if laps is not None and laps.height and res is not None and res.height:\n", " top5 = res.sort(pl.col(\"position\").cast(pl.Int64, strict=False)).head(5)[\"driver_id\"].to_list()\n", " fig, ax = plt.subplots(figsize=(9, 4))\n", " for d in top5:\n", " s = laps.filter(pl.col(\"driver_id\") == d).sort(\"lap_number\")\n", " ax.plot(s[\"lap_number\"], s[\"position\"].cast(pl.Int64, strict=False), label=d)\n", " ax.invert_yaxis()\n", " ax.set_xlabel(\"lap\")\n", " ax.set_ylabel(\"position\")\n", " ax.set_title(f\"{res['race_name'][0]} {F1_SEASON}: the top five finishers, lap by lap\")\n", " ax.legend(fontsize=8)\n", " plt.show()\n", "else:\n", " print(\"lap data unavailable\")" ] }, { "cell_type": "markdown", "id": "c15ad655", "metadata": {}, "source": [ "## 📚 TheSportsDB\n", "\n", "TheSportsDB's free tier serves a sample of its catalog (a handful of leagues, five rows of a table),\n", "which is enough to see the shape. [`thesportsdb_table`](../thesportsdb/reference/thesportsdb.md#thesportsdb_table)\n", "takes the league id and a season label such as `\"2024-2025\"` (4328 is the English Premier League)." ] }, { "cell_type": "code", "execution_count": null, "id": "b4400b7a", "metadata": {}, "outputs": [], "source": [ "safe(\"TheSportsDB leagues\", thesportsdb.thesportsdb_leagues)" ] }, { "cell_type": "code", "execution_count": null, "id": "8161ea91", "metadata": {}, "outputs": [], "source": [ "epl = safe(\"EPL table 2024-2025\", lambda: thesportsdb.thesportsdb_table(\"4328\", \"2024-2025\"))\n", "cols = [\"int_rank\", \"str_team\", \"int_played\", \"int_win\", \"int_draw\", \"int_loss\", \"int_points\"]\n", "epl.select([c for c in cols if c in epl.columns]) if epl is not None else None" ] }, { "cell_type": "markdown", "id": "5dad3928", "metadata": {}, "source": [ "## 📰 ESPN content\n", "\n", "[`espn_content_news`](../espn_content/reference/espn_content.md#espn_content_news) returns cross-sport\n", "headlines; [`espn_content_league_news`](../espn_content/reference/espn_content.md#espn_content_league_news)\n", "takes ESPN's sport and league slugs (`\"basketball\"`, `\"nba\"`)." ] }, { "cell_type": "code", "execution_count": null, "id": "e3b78139", "metadata": {}, "outputs": [], "source": [ "news = safe(\"ESPN NBA news\", lambda: espn_content.espn_content_league_news(\"basketball\", \"nba\", limit=\"5\"))\n", "news.select(\"published\", \"headline\") if news is not None and news.height else \"no headlines\"" ] }, { "cell_type": "markdown", "id": "092c3b37", "metadata": {}, "source": [ "## 🎉 Where to next\n", "\n", "- The G League's ESPN surface has the same shape as every other ESPN league: see the\n", " **NBA G League** reference pages.\n", "- F1 also has `f1_qualifying`, `f1_sprint`, `f1_pitstops` and the `f1_circuits` / `f1_status`\n", " vocabularies.\n", "- Headlines for any league: pass its ESPN slugs to `espn_content_league_news`." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }