Skip to content

Feature Request: Between-the-Lines Passing Classification & Press Bypass Detection — Arsenal 1-1 Man City (21 Sep 2025) #10

Description

@zhub9006

Feature Request: Between-the-Lines Passing Classification & Press Bypass Detection

This issue proposes a programmatic methodology for detecting "between-the-lines" and "press bypass" passes using StatsBomb event data, motivated by the Arsenal 1-1 Man City (21 Sep 2025) match where the Martinelli equaliser (90+3') origina
ted from a pass that bypassed 2+ pressing lines.


1. The Problem

In the Martinelli match-winning goal, Eze lofted a ball over Manchester City's defensive line into the channel behind Gvardiol — a between-the-lines pass that bypassed both the midfield press and the back line simultaneously. Currently, there is no standard method in publicly available football analytics repositories to:

  1. automatically classify a pass as "between-the-lines" vs. "line-compressing" vs. "line-breaking"
  2. measure how many pressing layers (if any) a pass bypasses
  3. compute a Press Bypass Rate (PBR): the percentage of press actions that result in the opponent playing a pass that bypasses the press rather than being intercepted or forced into a turnover

This gap prevents analysts from programmatically comparing pressing effectiveness across teams and matches — only manual video annotation can currently do this.


2. Proposed Methodology

Using StatsBomb event data fields (pass_start_location, pass_end_location, pass_height, pass_outcome, facing pressures pressure_start_location, pressure_end_location), we can define:

2.1 Between-the-Lines Pass Detection

A pass is "between-the-lines" if:

  1. pass_start_y is in the attacking half (y > 50 in a 0–100 coordinate system)
  2. The pass trajectory crosses at least two defensive player positions (estimated from pressure_start_location events in the 3 seconds before the pass)
  3. The pass is not intercepted (pass_outcome != "Off Target" AND pass_outcome != "Blocked")
  4. The pass end location is in a central channel (x between 30–70 on the pitch) rather than wide

Implementation pseudocode:

def is_between_the_lines_pass(pass_event, player_positions_before_pass, pressure_events):
    # 1. Check start location is in attacking half
    if pass_event["pass_start_y"] < 50:
        return False
    
    # 2. Check trajectory crosses 2+ defensive player positions
    trajectory = compute_trajectory(pass_event)
    defensive_positions = [p for p in player_positions_before_pass 
                           if p["team"] != pass_event["team"] 
                           and p["y"] > pass_event["pass_start_y"] - 10]
    
    crossings = sum(1 for dp in defensive_positions 
                    if line_crosses_point(trajectory, dp["x"], dp["y"]))
    if crossings < 2:
        return False
    
    # 3. Check pass wasn't intercepted/blocked
    if pass_event["pass_outcome"] in ["Off Target", "Blocked", "Intercepted"]:
        return False
    
    # 4. Check end location is in central channel
    if not (30 <= pass_event["pass_end_x"] <= 70):
        return False
    
    return True

2.2 Press Bypass Rate (PBR) — Per Match

def press_bypass_rate(events, player_positions):
    press_actions = [e for e in events if e["type"] == "Pressure" 
                     and e["position"]["y"] > 50]  # In att. half
    bypass_passes = [e for e in events if is_between_the_lines_pass(e, player_positions)]
    return len(bypass_passes) / len(press_actions) if press_actions else 0

2.3 Between-the-Lines Passing Value (BtLV)

Extend this to a transition quality metric: weight each between-the-lines pass by:

  • Distance gained (yards ahead of the line of scrimmage)
  • xG generated downstream (xG of any shot from the resulting sequence within 8 seconds)
  • whether it led to a goal in transition
def between_the_lines_passing_value(events, pass_events):
    btln_passes = [p for p in pass_events if is_between_the_lines_pass(p, ...)]
    return sum(
        downstream_xg(p, events) * distance_factor(p) * goal_factor(p)
        for p in btln_passes
    )

3. Application to Arsenal 1-1 Man City (21 Sep 2025)

Using this methodology, we could quantify:

Metric Arsenal (1st HT) Arsenal (2nd HT) Man City (1st HT) Man City (2nd HT)
PBR (Press Bypass Rate) TBD TBD TBD TBD
BtLV (Between-the-Lines Passing Value) TBD TBD TBD TBD
Number of BtL Passes TBD TBD TBD TBD
Avg Distance Gained per BtL Pass TBD TBD TBD TBD
xG from BtL Pass Sequences TBD TBD TBD TBD

Hypothesis: Arsenal's PBR was very low in the first half (only 1 shot on target despite 67.5% possession) — their passes were either being intercepted by City's press, played into crowded wide areas, or recycled backwards. Martinelli's goal (a BtL pass) represented a late-game increase in BtL passing quality that correlated directly with the transition goal.

Cross-match comparison: In the Man City 2-1 Arsenal (19 Apr 2026) match (see #5 on this repo), the same PBR methodology could reveal whether Arteta adjusted his team's passing through the lines differently in the response match.


4. Integration with Existing Repo Structure

This methodology can be slotted into the Arsenal 1-1 Man City analysis in "03. Analyzing Event Data" and the "Beyond Expected Goals" notebook. Specifically:

Notebook / Module Integration Point
03. Analyzing Event Data Add classify_between_the_lines_passes() function
06. Beyond Expected Goals Extend to calculate xG from BtL pass sequences
07. Passing Networks Extend get_pass_clusters() to filter by pass type (BtL vs. standard)
Metrics/Pressing Add PPDA × PBR cross-matrix
New: Metrics/BetweenTheLines Dedicated module for BtL pass analytics

5. Why This Matters

Currently, pressing effectiveness can only be measured reactively (did we win the ball? yes/no). The PBR metric adds a proactive dimension: how often did our press force the opponent into a bypass rather than a turnover? And BtLV adds a quality dimension: not just did the ball get past the press, but did it create xG?

This is particularly relevant to the recurring question posed across multiple issues in both the penaltyblog and Football-Analytics-With-Python repos: "How do we programmatically flag between-the-lines or line-breaking passes?" — which was raised as an open question in penaltyblog #34 (comment by zhub9006 on 13 Jul 2026).


6. Open Questions

  1. Coordinate system: StatsBomb uses a 0–100 pitch coordinate system. Should we normalize to real-world pitches (100m × 68m) for distance calculations?
  2. Tracking data requirement: The above pseudocode uses ball positions and player pressure events — can we achieve reliable BtL detection using event-level data alone, or do we need tracking data (player x/y positions at every moment)?
  3. Defining "crossing 2+ defensive positions": What is the tolerance for positional uncertainty? If a player is 2m from the pass trajectory, should that count as a "cross"?
  4. Between-the-lines vs. line-breaking vs. line-compressing: Are these distinct or overlapping concepts? Should our classification be ternary (BtL / line-breaking / line-compressing) or more granular?
  5. StatsBomb API fields: The free event data API may not include all pressure_start_location fields needed for this analysis — would the paid API be required?

7. Data Sources

  • StatsBomb Open Data (free match event data available at statsbomb.com)
  • The Analyst / Opta match data (theanalyst.com)
  • Arsenal.com official match report (Arsenal 1-1 Man City, 21 Sep 2025)
  • Coaches' Voice tactical analysis
  • Total Football Analysis tactical breakdown

This feature request is intended to complement the pressing and transition analysis discussions across both the penaltyblog and slothfulwave repositories. A prototype notebook demonstrating BtL pass detection on the Arsenal 1-1 Man City match would be a valuable contribution to the football analytics community.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions