Skip to content

Possession

Understanding possession data in AIB Insight.

Types of Possession

Team Possession

Which team controls the ball.

{
  "team_0": { "seconds": 2750.5, "pct": 52.1 },
  "team_1": { "seconds": 2120.0, "pct": 40.2 },
  "unknown": { "seconds": 410.0, "pct": 7.7 }
}
Category Description
team_0 Left-starting team has ball
team_1 Right-starting team has ball
unknown Contested, transitional, or unclear

Player Possession

Which specific player controls the ball.

Interpreting Possession Percentages

What "Unknown" Means

The unknown category includes:

  • Contested ball: 50/50 challenges
  • Transitions: Ball changing between teams
  • Aerial duels: Ball in the air
  • Out of play: Ball outside pitch
  • Detection issues: Unclear footage

Clean Possession Stats

For team comparison, you can normalize to exclude unknown:

team_0_normalized = team_0_pct / (team_0_pct + team_1_pct) * 100

Realistic Values

Possession Interpretation
60%+ Dominant possession
50-60% Slight advantage
45-55% Balanced match
<45% Opposition dominant

Per-Minute Possession

The possession_per_minute array shows possession over time:

[
  { "minute": 0, "team_0_pct": 58.67, "team_1_pct": 33.5, "unknown_pct": 7.83 },
  { "minute": 1, "team_0_pct": 47.7, "team_1_pct": 44.8, "unknown_pct": 7.5 }
]

Use Cases

  1. Possession flow chart: Visualize which team dominated at each point
  2. Momentum analysis: Identify when team gained/lost control
  3. Tactical changes: See effect of substitutions or formation changes

Visualization Example

import matplotlib.pyplot as plt

# Stacked area chart
minutes = [pm['minute'] for pm in possession_per_minute]
team_0 = [pm['team_0_pct'] for pm in possession_per_minute]
team_1 = [pm['team_1_pct'] for pm in possession_per_minute]

plt.stackplot(minutes, team_0, team_1, labels=['Team A', 'Team B'])
plt.xlabel('Minute')
plt.ylabel('Possession %')
plt.legend()
plt.show()

Possession Events

The Events endpoint returns individual possession changes:

[
  { "start": 10.5, "end": 25.3, "label": "Team A", "event_type": "team_possession" },
  { "start": 25.8, "end": 42.1, "label": "Team B", "event_type": "team_possession" }
]

Gaps Between Events

Gaps indicate:

  • Transition moments
  • Contested ball
  • Ball out of play

Calculating Possession from Events

from collections import defaultdict

possession_time = defaultdict(float)
for event in events:
    if event['event_type'] == 'team_possession':
        duration = event['end'] - event['start']
        possession_time[event['label']] += duration

total = sum(possession_time.values())
for team, seconds in possession_time.items():
    print(f"{team}: {seconds/total*100:.1f}%")

Team Identification

ID Starting Side Notes
team_0 Left Use summary endpoint for team name
team_1 Right Use summary endpoint for team name

Getting Team Names

# From summary
summary = requests.get(f'/api/v1/analyses/{id}/summary', headers=headers).json()
team_0_name = summary['teams']['team_0_name']  # "FC Example"
team_1_name = summary['teams']['team_1_name']  # "Opponent FC"

Best Practices

  1. Always include scene filter when analyzing possession to exclude replays
  2. Use per-minute data for trends rather than just totals
  3. Consider unknown % - high unknown might indicate contested match
  4. Compare with expected values based on match context