{ "cells": [ { "cell_type": "markdown", "id": "4756194f", "metadata": {}, "source": [ "# 📍 Recipe: Touch Location Heatmap for a Player\n", "\n", "In this example, we'll generate a heatmap of where a single player touched the ball during a match using StatsBomb open data.\n", "\n", "We'll use Flow to filter events, extract coordinates, and then visualize the result using mplsoccer.\n", "\n", "## 🧰 What You'll Learn\n", "\n", "- How to access StatsBomb data using `Flow.statsbomb` methods\n", "- How to filter events to those involving a given player\n", "- How to extract location data using or `.split_array()`\n", "- How to use `.pipe()` to apply a custom function to the Flow\n", "\n", "## Imports" ] }, { "cell_type": "code", "execution_count": 1, "id": "4248fe6e", "metadata": {}, "outputs": [], "source": [ "from IPython.display import HTML\n", "from penaltyblog.matchflow import Flow, where_equals\n", "from penaltyblog.viz import Pitch\n", "import plotly.io as pio" ] }, { "cell_type": "markdown", "id": "087075c2", "metadata": {}, "source": [ "## Load the Data" ] }, { "cell_type": "code", "execution_count": 2, "id": "ee5a686d", "metadata": {}, "outputs": [], "source": [ "# Load events for a StatsBomb match\n", "match_id = 22912 # Champions League Final 2018/2019\n", "\n", "flow = Flow.statsbomb.events(match_id)" ] }, { "cell_type": "markdown", "id": "4f53ac10", "metadata": {}, "source": [ "## Filter Events to Just Those by Trent Alexander-Arnold" ] }, { "cell_type": "code", "execution_count": 13, "id": "350cf117", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'x': 43.5, 'y': 65.7}\n", "{'x': 21.9, 'y': 59.6}\n", "{'x': 14.7, 'y': 65.9}\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/Users/martin/repos/penaltyblog/venv/lib/python3.13/site-packages/statsbombpy/api_client.py:21: NoAuthWarning:\n", "\n", "credentials were not supplied. open data access only\n", "\n" ] } ], "source": [ "touches = (\n", " flow.filter(where_equals(\"player.name\", \"Trent Alexander-Arnold\"))\n", " .split_array(\"location\", into=[\"x\", \"y\"])\n", " .select(\"x\", \"y\")\n", ")\n", "\n", "for touch in touches.head(3):\n", " print(touch)" ] }, { "cell_type": "markdown", "id": "d7680381", "metadata": {}, "source": [ "## Plot Heatmap of Touches" ] }, { "cell_type": "code", "execution_count": 14, "id": "8d2236c6", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ], "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pitch = Pitch(\n", " provider=\"statsbomb\",\n", " orientation=\"horizontal\",\n", " view=\"full\",\n", " theme=\"night\",\n", " show_axis=False,\n", " show_legend=False,\n", " width=400,\n", " height=400,\n", " title=\"Trent Alexander-Arnold – Touch Map\",\n", ")\n", "\n", "pitch.plot_kde(\n", " touches,\n", " x=\"x\",\n", " y=\"y\",\n", " colorscale=\"Viridis\",\n", ")\n", "\n", "# NOTE: normally we'd just call `pitch.show()` here, but since\n", "# we're exporting to HTML docs, we need to use `HTML` to export\n", "# the plot\n", "# pitch.show()\n", "HTML(pio.to_html(pitch.fig, include_plotlyjs=\"cdn\"))" ] } ], "metadata": { "kernelspec": { "display_name": "venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.1" } }, "nbformat": 4, "nbformat_minor": 5 }