Skip to content

Feature Request: Built-in patterns for pressing structure and transitional play analysis #573

Description

@zhub9006

Feature Request: Built-in patterns for pressing structure and transitional play analysis

Summary

I'd like to propose adding built-in helper utilities and "pattern" abstractions for analyzing pressing behavior and transitional play to kloppy. These are two of the most analytically rich and tactically meaningful events in football, and first-class support would significantly enhance the value of kloppy as the standard data layer for football analytics.

Background & Motivation

I was analyzing Arsenal vs Manchester City matches from the 2024/25 and 2025/26 Premier League seasons and noticed several recurring patterns that are extremely difficult to capture with only kotpy's current event/tracking API:

1. High Pressing Structure & Sequences

When Arsenal press high against City, they adopt a 4-1-4-1 or 4-2-2-1 pressing shape rather than a flat 4-4-2:

  • Two inverted wingers tuck inward to occupy City's spread-out centre-backs (Stones & Gvardiol), cutting off outward passing lanes.
  • Two attacking midfielders (Odegaard/Rice) press City's double pivot (Silva/Kovacic) man-to-man.
  • A forward (Add datasets loader and bump version #9) acts as a "trigger" — their role is not to win the ball but to force City into predictable side corridors.
  • Only once City is funneled into a narrow corridor does the press escalate to a mechanical close-down sequence.

In the 5-1 match (Feb 2025), this press directly generated the first goal: Havertz forced Stones→Kovacic→Akanji into a hurried pass that Trossard intercepted, leading to a 3-second attack and Ødegaard's finish.

2. Counter-Press / Transition Windows

When possession is won high, the most dangerous moments occur within 5-10 seconds:

  • Partey's goal and Havertz's goal in the 5-1 match both came within 5 seconds of regaining the ball in the opponent's half.
  • Lewis-Skelly's goal came from a transition where City's defensive line was caught 20+ yards up — Rice won the ball in midfield and found Lewis-Skelly in the box in one pass.
  • City's transition threat was equally lethal: their first goal came via Reijnders releasing Haaland directly into a disorganized Arsenal back line.

3. Mid-Block Defensive Transitions (Low Block)

When City drop into a mid-block (4-4-2 / 4-5-1), they compress central zones:

  • Foden and Doku tuck in narrow, reducing passing lanes through the middle.
  • Full-backs stay compact, forcing Arsenal wide.
  • In the 1-1 match (Sep 2025), City's mid-block limited Arsenal to 0.64 xG in 90 minutes — the breakthrough only came when Eze's positional intelligence unlocked a transition.

What I'm Proposing

1. Temporal neighborhood helpers — A method to query events that occur within N seconds of a given event (e.g., event.after(seconds=10), event.before(seconds=5)). This is essential for defining pressing chains and transition windows. Currently, users must manually filter by time difference.

2. Transition detection utility — A function that, given an event dataset, identifies transitions in both directions:

  • "attacking transition" = possession won → shot within N seconds in the opponent's half
  • "defensive transition" = possession lost in own half → recovery in the opponent's half within N seconds
  • Each transition should carry metadata: how it was won (press/intercept/error), the xG added, and the players involved.

3. Pressing pattern analysis helper — Not necessarily a full "Pattern" class (that's codeball's domain), but at minimum:

  • A way to identify chains of consecutive PRESSURE events on the same opponent within a time window
  • The ability to measure "press intensity" as pressures-per-minute in a target zone
  • A method to detect when a press results in a turnover (the "press success rate")

4. Positional context for transitions — When analyzing a transition, it's critical to know:

  • Was it won in the opponent's half, own half, or at the touchline?
  • What was the formation of both teams at the moment of the turnover?
  • How far apart were the nearest defenders from the ball at the moment of recovery?

Why kloppy?

Kloppy is already the standard interface for loading event and tracking data from StatsBomb, Opta, Wyscout, Metrica, and others. All tactical analysis tools (codeball, football-analytics, Reep, etc.) build on top of it. Adding these analytical primitives to kloppy means every downstream tool immediately benefits. The existing filter() regex-based search mechanism is powerful for finding static patterns (like "shot.goal"), but pressing and transitional analysis require temporal relationships between events — something kloppy is uniquely positioned to provide with its time-indexed event model.

Example (what the API could look like)

from kloppy import StatsBomb

dataset = StatsBomb.load_open_event_data(league="E0", season="2024/25")

# Find all transitions where a shot followed within 8 seconds of a recovery
transitions = dataset.transitions(
    max_seconds=8,
    min_xG=0.05,
    half="opponent"  # only transitions that started in the opponent's half
)

for t in transitions:
    print(f"Player: {t.player}, xG: {t.xG}, from_press: {t.from_press}")

Additional Note on Possession-Transition Correlation

I observed a subtle but important trend: the lowest-possession matches are disproportionately the ones where transitional actions decide the game. In the 5-1 (City had 37% possession but 4 of their 5 shots came from transitions), and the 1-1 draw (City had 33% — their lowest ever under Guardiola in the PL — yet created dangerous transitions). A built-in metric correlating possession share with transition threat intensity would be a valuable addition to kloppy's analysis utilities.

I'm happy to collaborate on PRs for any of these features.


Observations sourced from: Arsenal 5-1 Man City (Feb 2025), Arsenal 1-1 Man City (Sep 2025) via Opta, The Analyst, Coaches' Voice, and the Arsenal Official Match Centre.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions