Case Study 6.1: Mapping Barcelona's Tiki-Taka Through Spatial Analysis
Background
FC Barcelona under Pep Guardiola (2008-2012) revolutionized modern soccer with their "tiki-taka" style: relentless short passing, positional rotations, and deliberate progression through defined pitch zones. The system was not random ball circulation -- it was a highly structured spatial strategy designed to draw opponents out of position and exploit the half-spaces.
In this case study, we use spatial analysis techniques from Chapter 6 to quantify the spatial signature of Barcelona's passing game. We examine where passes originate, where they terminate, which zones see the highest pass density, and how Barcelona's spatial profile differs from a more direct team.
Objectives
- Load and convert Barcelona's pass event data into a standard coordinate frame.
- Visualize pass origins and destinations using scatter plots and arrow maps.
- Compute zone-based pass statistics using a 6 x 5 grid.
- Generate KDE heat maps of pass origins to identify spatial concentration.
- Compare Barcelona's spatial passing profile to a contrasting team.
Data
We use StatsBomb's open data for a Barcelona match from the 2015-16 La Liga season (the methodology applies to any StatsBomb match). The data includes all pass events with start coordinates $(x, y)$ and end coordinates $(end\_x, end\_y)$ in StatsBomb's 120 x 80 coordinate system.
Step 1: Load and Transform the Data
import json
import pandas as pd
import numpy as np
def load_statsbomb_passes(events_path: str, team_name: str) -> pd.DataFrame:
"""Load pass events for a specific team from a StatsBomb events JSON file."""
with open(events_path, "r", encoding="utf-8") as f:
events = json.load(f)
passes = []
for event in events:
if event["type"]["name"] == "Pass" and event["team"]["name"] == team_name:
loc = event["location"]
end_loc = event["pass"]["end_location"]
outcome = event["pass"].get("outcome", {}).get("name", "Complete")
passes.append({
"x": loc[0],
"y": loc[1],
"end_x": end_loc[0],
"end_y": end_loc[1],
"outcome": outcome,
"player": event["player"]["name"],
"minute": event["minute"],
})
return pd.DataFrame(passes)
After loading, we convert StatsBomb coordinates to standard metres (105 x 68, origin bottom-left, y-up):
def statsbomb_to_metres(df: pd.DataFrame) -> pd.DataFrame:
"""Convert StatsBomb coordinate columns to standard metres."""
df = df.copy()
df["x_m"] = df["x"] * (105.0 / 120.0)
df["y_m"] = 68.0 - df["y"] * (68.0 / 80.0)
df["end_x_m"] = df["end_x"] * (105.0 / 120.0)
df["end_y_m"] = 68.0 - df["end_y"] * (68.0 / 80.0)
return df
Step 2: Pass Origin Scatter Plot
We plot every pass origin on the pitch:
from mplsoccer import Pitch
def plot_pass_origins(df: pd.DataFrame, title: str) -> None:
pitch = Pitch(pitch_type="custom", pitch_length=105, pitch_width=68,
pitch_color="#1a1a2e", line_color="#c7d5cc")
fig, ax = pitch.draw(figsize=(12, 8))
completed = df[df["outcome"] == "Complete"]
incomplete = df[df["outcome"] != "Complete"]
pitch.scatter(completed["x_m"], completed["y_m"], ax=ax,
s=20, color="#2ecc71", alpha=0.5, label="Complete")
pitch.scatter(incomplete["x_m"], incomplete["y_m"], ax=ax,
s=20, color="#e74c3c", alpha=0.5, label="Incomplete")
ax.set_title(title, fontsize=16, color="white", pad=10)
ax.legend(loc="upper left", fontsize=10)
fig.set_facecolor("#1a1a2e")
return fig, ax
Key observation: Barcelona's pass origins are distributed broadly across the pitch, with visible concentration in the middle and attacking thirds. The defensive third shows relatively few passes -- a hallmark of a possession-dominant team that pushes its defensive line high.
Step 3: Zone-Based Pass Statistics
We overlay a 6 x 5 grid (30 zones) and count passes originating in each zone:
def compute_zone_counts(
df: pd.DataFrame,
n_x: int = 6,
n_y: int = 5,
pitch_length: float = 105.0,
pitch_width: float = 68.0,
) -> np.ndarray:
"""Count pass origins per grid zone."""
grid = np.zeros((n_y, n_x), dtype=int)
col = np.clip(np.floor(df["x_m"] / (pitch_length / n_x)).astype(int), 0, n_x - 1)
row = np.clip(np.floor(df["y_m"] / (pitch_width / n_y)).astype(int), 0, n_y - 1)
for c, r in zip(col, row):
grid[r, c] += 1
return grid
For Barcelona, the zone counts reveal:
- The highest pass volumes are in the centre of the middle third (zones corresponding to central midfield).
- The half-spaces in the attacking third show elevated counts relative to the wings, consistent with positional play principles.
- The defensive third has the lowest counts, indicating that Barcelona rarely circulate the ball deep in their own half under normal circumstances.
Step 4: KDE Heat Map of Pass Origins
def plot_pass_heatmap(df: pd.DataFrame, title: str) -> None:
pitch = Pitch(pitch_type="custom", pitch_length=105, pitch_width=68,
pitch_color="#1a1a2e", line_color="#c7d5cc")
fig, ax = pitch.draw(figsize=(12, 8))
pitch.kdeplot(df["x_m"], df["y_m"], ax=ax,
cmap="YlOrRd", fill=True, levels=100, thresh=0.02, alpha=0.8)
ax.set_title(title, fontsize=16, color="white", pad=10)
fig.set_facecolor("#1a1a2e")
return fig, ax
The KDE surface confirms the zone analysis but with finer granularity. The hottest region is a broad area spanning central midfield into the attacking half-spaces -- the "engine room" of tiki-taka. The density drops sharply near the corner flag areas, indicating that Barcelona preferred to create chances through central overloads rather than conventional wing play.
Step 5: Comparative Analysis
To understand what makes Barcelona's spatial profile distinctive, we compare it against a team with a more direct style (e.g., a counter-attacking team from the same dataset).
def compare_pass_profiles(df_barca: pd.DataFrame, df_other: pd.DataFrame,
name_other: str) -> None:
pitch = Pitch(pitch_type="custom", pitch_length=105, pitch_width=68,
pitch_color="#1a1a2e", line_color="#c7d5cc")
fig, axes = pitch.draw(nrows=1, ncols=2, figsize=(20, 8))
pitch.kdeplot(df_barca["x_m"], df_barca["y_m"], ax=axes[0],
cmap="Blues", fill=True, levels=100, thresh=0.02)
axes[0].set_title("Barcelona", fontsize=14, color="white")
pitch.kdeplot(df_other["x_m"], df_other["y_m"], ax=axes[1],
cmap="Reds", fill=True, levels=100, thresh=0.02)
axes[1].set_title(name_other, fontsize=14, color="white")
fig.set_facecolor("#1a1a2e")
fig.suptitle("Pass Origin Density Comparison", fontsize=18, color="white", y=1.02)
return fig, axes
Findings:
| Metric | Barcelona | Counter-attacking team |
|---|---|---|
| Passes in attacking third (%) | ~38% | ~22% |
| Passes in half-spaces (%) | ~35% | ~25% |
| Mean pass origin x (m) | ~58 | ~45 |
| Pass completion rate | ~88% | ~76% |
Barcelona's spatial centroid is shifted roughly 13 m further up the pitch, reflecting their high defensive line and territorial dominance. Their concentration in the half-spaces is 10 percentage points higher, consistent with the positional play philosophy.
Discussion
This case study demonstrates how coordinate systems, zone models, and heat maps transform raw event data into tactical insight. The spatial signature of tiki-taka -- central concentration, half-space emphasis, high territorial line -- is clearly visible once we apply the right analytical tools.
Limitations:
- Single-match analysis may not capture the full range of Barcelona's tactics (e.g., against a low block vs. a high press).
- Pass origin density does not distinguish between progressive passes and sideways recycling.
- The comparison team is from a different match context; controlling for game state (leading, trailing, level) would strengthen the analysis.
Extensions:
- Analyze pass destination heat maps to identify the most targeted zones.
- Weight passes by progressive distance to separate meaningful progression from possession recycling.
- Track the evolution of Barcelona's spatial profile across seasons to identify tactical shifts under different managers.
Key Takeaways
- Coordinate transformation is the essential first step: all analysis begins with converting to a common frame.
- Zone-based statistics provide tactically interpretable summaries; KDE heat maps add spatial nuance.
- Comparative spatial analysis reveals team style differences that are invisible in aggregate statistics like total passes or possession percentage.
- The tools from Chapter 6 -- coordinate conversion, zoning, heat maps -- form a reusable pipeline for any spatial analysis task in soccer analytics.