Speed & Distance
Understanding player movement metrics in AIB Insight.
Distance Metrics
Total Distance
Total meters traveled during tracked period.
{
"player_id": "0-10",
"total_distance_m": 10542.5,
"tracking_duration_sec": 5280.0
}
Distance by Speed Bucket
Distance broken down by movement intensity:
{
"distance_by_speed_bucket": {
"walking_m": 3200.0,
"jogging_m": 4500.0,
"running_m": 2200.0,
"sprinting_m": 642.5
}
}
Speed Buckets
| Bucket | Speed Range | Description |
|---|---|---|
| Walking | 0-7 km/h | Standing, walking, slow movement |
| Jogging | 7-14 km/h | Light running, recovery |
| Running | 14-21 km/h | Moderate running, active play |
| Sprinting | 21+ km/h | High-intensity sprints |
Speed Thresholds
SPEED_BUCKETS = {
'walking': (0, 7), # km/h
'jogging': (7, 14), # km/h
'running': (14, 21), # km/h
'sprinting': (21, 40), # km/h (40 is max realistic)
}
Speed Statistics
| Metric | Description | Typical Values |
|---|---|---|
avg_speed_kmh |
Average speed while tracked | 6-10 km/h |
max_speed_kmh |
Peak speed reached | 28-35 km/h |
Context for Max Speed
| Max Speed | Interpretation |
|---|---|
| < 25 km/h | Limited sprinting |
| 25-30 km/h | Normal match sprints |
| 30-35 km/h | Fast sprinter |
| > 35 km/h | Elite sprint speed |
Typical Values by Position
| Position | Total Distance | Sprint Distance | Sprint % |
|---|---|---|---|
| Goalkeeper | 5-6 km | 0.1-0.3 km | 2-5% |
| Center Back | 9-11 km | 0.3-0.6 km | 3-6% |
| Full Back | 10-12 km | 0.5-0.9 km | 5-8% |
| Central Mid | 11-13 km | 0.5-0.8 km | 4-7% |
| Winger | 10-12 km | 0.7-1.2 km | 7-10% |
| Striker | 9-11 km | 0.5-0.9 km | 5-8% |
Analyzing Player Performance
High-Intensity Running
Combine running + sprinting for high-intensity distance:
def high_intensity_distance(player):
buckets = player['distance_by_speed_bucket']
return buckets['running_m'] + buckets['sprinting_m']
# Sort by high-intensity
players_by_intensity = sorted(
player_stats,
key=high_intensity_distance,
reverse=True
)
Sprint Percentage
What portion of distance was sprinting:
def sprint_percentage(player):
sprint = player['distance_by_speed_bucket']['sprinting_m']
total = player['total_distance_m']
return (sprint / total * 100) if total > 0 else 0
# Find best sprinters
for player in player_stats:
pct = sprint_percentage(player)
print(f"{player['display_name']}: {pct:.1f}% sprinting")
Work Rate
Distance per minute of play:
def work_rate(player):
distance = player['total_distance_m']
duration_min = player['tracking_duration_sec'] / 60
return distance / duration_min if duration_min > 0 else 0
# Meters per minute
for player in player_stats:
rate = work_rate(player)
print(f"{player['display_name']}: {rate:.0f} m/min")
Tracking Limitations
Coverage
- Not all players are tracked for the full match
tracking_duration_secshows actual tracked time- Compare players with similar tracking duration
Accuracy
- Positions sampled at 25 FPS
- Speed calculated from position changes
- Unrealistic speeds (>40 km/h) are filtered out
Missing Data
- Players may be untracked during:
- Off-camera moments
- Crowded situations
- Substitution periods
Visualization Ideas
Distance Bar Chart
import matplotlib.pyplot as plt
players = sorted(player_stats, key=lambda p: p['total_distance_m'], reverse=True)
names = [p['display_name'] for p in players]
distances = [p['total_distance_m'] / 1000 for p in players] # Convert to km
plt.barh(names, distances)
plt.xlabel('Distance (km)')
plt.title('Player Distances')
plt.tight_layout()
plt.show()
Speed Bucket Stacked Bar
import matplotlib.pyplot as plt
import numpy as np
players = player_stats[:10] # Top 10
names = [p['display_name'] for p in players]
walking = [p['distance_by_speed_bucket']['walking_m'] for p in players]
jogging = [p['distance_by_speed_bucket']['jogging_m'] for p in players]
running = [p['distance_by_speed_bucket']['running_m'] for p in players]
sprinting = [p['distance_by_speed_bucket']['sprinting_m'] for p in players]
x = np.arange(len(names))
plt.bar(x, walking, label='Walking')
plt.bar(x, jogging, bottom=walking, label='Jogging')
plt.bar(x, running, bottom=np.array(walking)+np.array(jogging), label='Running')
plt.bar(x, sprinting, bottom=np.array(walking)+np.array(jogging)+np.array(running), label='Sprinting')
plt.xticks(x, names, rotation=45, ha='right')
plt.ylabel('Distance (m)')
plt.legend()
plt.tight_layout()
plt.show()