📍 Recipe: Touch Location Heatmap for a Player#

In this example, we’ll generate a heatmap of where a single player touched the ball during a match using StatsBomb open data.

We’ll use Flow to filter events, extract coordinates, and then visualize the result using mplsoccer.

🧰 What You’ll Learn#

  • How to access StatsBomb data using Flow.statsbomb methods

  • How to filter events to those involving a given player

  • How to extract location data using or .split_array()

  • How to use .pipe() to apply a custom function to the Flow

Imports#

[1]:
from IPython.display import HTML
from penaltyblog.matchflow import Flow, where_equals
from penaltyblog.viz import Pitch
import plotly.io as pio

Load the Data#

[2]:
# Load events for a StatsBomb match
match_id = 22912  # Champions League Final 2018/2019

flow = Flow.statsbomb.events(match_id)

Filter Events to Just Those by Trent Alexander-Arnold#

[13]:
touches = (
    flow.filter(where_equals("player.name", "Trent Alexander-Arnold"))
    .split_array("location", into=["x", "y"])
    .select("x", "y")
)

for touch in touches.head(3):
    print(touch)
{'x': 43.5, 'y': 65.7}
{'x': 21.9, 'y': 59.6}
{'x': 14.7, 'y': 65.9}
/Users/martin/repos/penaltyblog/venv/lib/python3.13/site-packages/statsbombpy/api_client.py:21: NoAuthWarning:

credentials were not supplied. open data access only

Plot Heatmap of Touches#

[14]:
pitch = Pitch(
    provider="statsbomb",
    orientation="horizontal",
    view="full",
    theme="night",
    show_axis=False,
    show_legend=False,
    width=400,
    height=400,
    title="Trent Alexander-Arnold – Touch Map",
)

pitch.plot_kde(
    touches,
    x="x",
    y="y",
    colorscale="Viridis",
)

# NOTE: normally we'd just call `pitch.show()` here, but since
# we're exporting to HTML docs, we need to use `HTML` to export
# the plot
# pitch.show()
HTML(pio.to_html(pitch.fig, include_plotlyjs="cdn"))
[14]: