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
- Possession flow chart: Visualize which team dominated at each point
- Momentum analysis: Identify when team gained/lost control
- 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
- Always include scene filter when analyzing possession to exclude replays
- Use per-minute data for trends rather than just totals
- Consider unknown % - high unknown might indicate contested match
- Compare with expected values based on match context