Crossing and Wide Play
Crossing and Wide Play
Crossing and wide play are fundamental tactical elements in modern football that involve utilizing the width of the pitch to create goal-scoring opportunities. This analytical approach examines the effectiveness of crosses, the quality of wide play, delivery accuracy, and the strategic value of attacking through the flanks. Understanding crossing metrics helps teams optimize their attacking patterns, identify key creative players, and exploit defensive weaknesses in opposition setups.
Key Concepts
Analyzing crossing and wide play requires understanding multiple interconnected metrics that measure both the quantity and quality of wide attacks:
- Cross Completion Rate: The percentage of crosses that successfully reach a teammate, indicating delivery accuracy and attacking coordination.
- Cross xA (Expected Assists): The expected goal value generated from crosses, measuring the quality of crossing opportunities created.
- Wide Zone Progression: Metrics tracking ball advancement through wide channels, typically defined as the outer thirds of the pitch width.
- Cross Types: Classification of crosses including low/driven crosses, lofted crosses, cutbacks, pull-backs, and switch passes.
- Delivery Zones: Analysis of crossing origin points, including deep crosses from near the byline, half-space crosses, and switches from opposite flanks.
- Box Entry via Crosses: Tracking successful ball entries into the penalty area through aerial delivery.
- Aerial Duel Success: Winning percentage of aerial challenges following crosses, relevant for both attackers and defenders.
- Wide Overload Frequency: Instances where attacking teams create numerical superiority in wide areas.
Mathematical Foundation
Cross Completion Rate:
Cross Completion % = (Successful Crosses / Total Crosses Attempted) × 100
Cross Expected Assists (xA):
Cross xA = Σ xG(shot) for all shots resulting from crosses
Wide Play Efficiency:
Wide Efficiency = (Goals from Wide Play / Wide Play Sequences) × 100
Cross Danger Index:
CDI = (Cross xA + Box Entries via Cross × 0.05 + Key Passes from Cross × 0.08) / Total Crosses
Positional Width:
Average Width = Mean horizontal distance of attacking players from center line during possession phases
Cross Target Area Success:
Target Area % = (Crosses landing in optimal zones / Total Crosses) × 100
Where optimal zones are typically 6-yard box (high value) and penalty spot area (medium-high value)
Python Implementation
import pandas as pd
import numpy as np
from statsbombpy import sb
from mplsoccer import Pitch, VerticalPitch
import matplotlib.pyplot as plt
# Load match event data
matches = sb.matches(competition_id=2, season_id=44) # Premier League
events = sb.events(match_id=3788741)
# Filter crossing events
crosses = events[events['type'].str.contains('Cross', na=False) |
((events['type'] == 'Pass') & (events['pass_cross'] == True))]
# Analyze cross completion
def analyze_cross_completion(crosses_df):
"""Calculate cross completion rates and patterns"""
total_crosses = len(crosses_df)
successful_crosses = len(crosses_df[crosses_df['pass_outcome'].isna()])
completion_rate = (successful_crosses / total_crosses * 100) if total_crosses > 0 else 0
return {
'total_crosses': total_crosses,
'successful': successful_crosses,
'completion_rate': completion_rate
}
# Calculate cross xA
def calculate_cross_xa(events_df):
"""Calculate expected assists from crosses"""
# Identify shots that came from crosses
crosses_with_xa = events_df[
(events_df['type'] == 'Pass') &
(events_df['pass_cross'] == True) &
(events_df['pass_shot_assist'] == True)
]
total_xa = crosses_with_xa['pass_goal_assist'].sum() if 'pass_goal_assist' in crosses_with_xa.columns else 0
return total_xa
# Analyze crossing zones
def analyze_crossing_zones(crosses_df):
"""Categorize crosses by origin zone"""
crosses_df['x_zone'] = pd.cut(
crosses_df['location'].apply(lambda x: x[0] if isinstance(x, list) else 0),
bins=[0, 80, 102, 120],
labels=['midfield', 'attacking_third', 'final_third']
)
crosses_df['y_zone'] = pd.cut(
crosses_df['location'].apply(lambda x: x[1] if isinstance(x, list) else 40),
bins=[0, 20, 60, 80],
labels=['left_wing', 'central', 'right_wing']
)
zone_analysis = crosses_df.groupby(['x_zone', 'y_zone']).agg({
'id': 'count',
'pass_outcome': lambda x: (x.isna()).sum()
}).rename(columns={'id': 'total', 'pass_outcome': 'completed'})
zone_analysis['completion_rate'] = (zone_analysis['completed'] / zone_analysis['total'] * 100).round(2)
return zone_analysis
# Visualize crossing patterns
def plot_crossing_heatmap(crosses_df):
"""Create heatmap of crossing origin points"""
pitch = Pitch(pitch_type='statsbomb', pitch_color='#22312b', line_color='white')
fig, ax = pitch.draw(figsize=(12, 8))
# Extract coordinates
x_coords = crosses_df['location'].apply(lambda x: x[0] if isinstance(x, list) else 60)
y_coords = crosses_df['location'].apply(lambda x: x[1] if isinstance(x, list) else 40)
# Create heatmap
bin_statistic = pitch.bin_statistic(x_coords, y_coords, statistic='count', bins=(12, 8))
pitch.heatmap(bin_statistic, ax=ax, cmap='hot', edgecolors='#22312b')
plt.title('Crossing Origin Heatmap', fontsize=16, color='white')
plt.tight_layout()
return fig
# Advanced: Cross danger assessment
def assess_cross_danger(events_df):
"""Evaluate danger created by crosses using multiple metrics"""
crosses = events_df[
(events_df['type'] == 'Pass') &
(events_df['pass_cross'] == True)
]
danger_scores = []
for idx, cross in crosses.iterrows():
score = 0
# Height of cross (low crosses more dangerous)
if 'pass_height' in cross and cross['pass_height'] == 'Ground Pass':
score += 3
elif 'pass_height' in cross and cross['pass_height'] == 'Low Pass':
score += 2
# End location (penalty area = higher danger)
if 'pass_end_location' in cross and isinstance(cross['pass_end_location'], list):
end_x, end_y = cross['pass_end_location'][0], cross['pass_end_location'][1]
if end_x >= 102 and 18 <= end_y <= 62: # Penalty box
score += 5
if end_x >= 114 and 30 <= end_y <= 50: # Six-yard box
score += 3
# Assist or key pass
if cross.get('pass_shot_assist', False):
score += 10
if cross.get('pass_goal_assist', False):
score += 20
danger_scores.append(score)
crosses['danger_score'] = danger_scores
return crosses
# Team-level wide play analysis
def analyze_team_wide_play(events_df, team_name):
"""Comprehensive wide play metrics for a team"""
team_events = events_df[events_df['team'] == team_name]
# Crosses
team_crosses = team_events[
(team_events['type'] == 'Pass') &
(team_events['pass_cross'] == True)
]
# Wide passes (outer channels)
wide_passes = team_events[
(team_events['type'] == 'Pass') &
(team_events['location'].apply(lambda x: x[1] if isinstance(x, list) else 40) < 20) |
(team_events['location'].apply(lambda x: x[1] if isinstance(x, list) else 40) > 60)
]
metrics = {
'total_crosses': len(team_crosses),
'cross_completion': (team_crosses['pass_outcome'].isna().sum() / len(team_crosses) * 100) if len(team_crosses) > 0 else 0,
'wide_passes': len(wide_passes),
'wide_pass_completion': (wide_passes['pass_outcome'].isna().sum() / len(wide_passes) * 100) if len(wide_passes) > 0 else 0,
'crosses_per_90': (len(team_crosses) / 90) * 90,
'key_crosses': len(team_crosses[team_crosses.get('pass_shot_assist', False) == True])
}
return metrics
# Example usage
cross_stats = analyze_cross_completion(crosses)
print(f"Cross Completion Rate: {cross_stats['completion_rate']:.2f}%")
zone_stats = analyze_crossing_zones(crosses)
print("
Crossing by Zone:")
print(zone_stats)
team_wide_metrics = analyze_team_wide_play(events, 'Arsenal')
print(f"
Team Wide Play Metrics: {team_wide_metrics}")
R Implementation
library(tidyverse)
library(worldfootballR)
library(ggsoccer)
library(StatsBombR)
# Load match data
competitions <- FreeCompetitions()
matches <- FreeMatches(competitions %>% filter(competition_name == "Premier League"))
events <- get.matchFree(matches$match_id[1])
# Filter crossing events
crosses <- events %>%
filter(type.name == "Pass" & pass.cross == TRUE)
# Analyze cross completion
analyze_cross_completion <- function(crosses_data) {
crosses_data %>%
summarise(
total_crosses = n(),
successful_crosses = sum(is.na(pass.outcome.name)),
completion_rate = (successful_crosses / total_crosses) * 100,
avg_length = mean(pass.length, na.rm = TRUE),
avg_angle = mean(abs(pass.angle), na.rm = TRUE)
)
}
# Cross completion by zone
analyze_crossing_zones <- function(crosses_data) {
crosses_data %>%
mutate(
x_zone = cut(location.x,
breaks = c(0, 80, 102, 120),
labels = c("midfield", "attacking_third", "final_third")),
y_zone = cut(location.y,
breaks = c(0, 20, 60, 80),
labels = c("left_wing", "central", "right_wing"))
) %>%
group_by(x_zone, y_zone) %>%
summarise(
total = n(),
completed = sum(is.na(pass.outcome.name)),
completion_rate = (completed / total) * 100,
.groups = "drop"
)
}
# Calculate cross xA
calculate_cross_xa <- function(events_data) {
events_data %>%
filter(type.name == "Pass" & pass.cross == TRUE &
!is.na(pass.shot_assist)) %>%
summarise(
total_xa = sum(pass.goal_assist, na.rm = TRUE),
key_crosses = n()
)
}
# Visualize crossing patterns
plot_crossing_map <- function(crosses_data) {
ggplot(crosses_data) +
annotate_pitch(dimensions = pitch_statsbomb) +
theme_pitch() +
geom_segment(
aes(x = location.x, y = location.y,
xend = pass.end_location.x, yend = pass.end_location.y,
color = is.na(pass.outcome.name)),
arrow = arrow(length = unit(0.2, "cm")),
alpha = 0.6
) +
scale_color_manual(
values = c("TRUE" = "#00FF00", "FALSE" = "#FF0000"),
labels = c("Completed", "Incomplete"),
name = "Cross Outcome"
) +
labs(title = "Crossing Patterns",
subtitle = "Green = Completed, Red = Incomplete") +
theme(
plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
plot.subtitle = element_text(hjust = 0.5, size = 12)
)
}
# Cross danger scoring
assess_cross_danger <- function(crosses_data) {
crosses_data %>%
mutate(
danger_score = case_when(
pass.height.name == "Ground Pass" ~ 3,
pass.height.name == "Low Pass" ~ 2,
TRUE ~ 0
) +
case_when(
pass.end_location.x >= 102 &
pass.end_location.y >= 18 &
pass.end_location.y <= 62 ~ 5,
pass.end_location.x >= 114 &
pass.end_location.y >= 30 &
pass.end_location.y <= 50 ~ 8,
TRUE ~ 0
) +
case_when(
!is.na(pass.shot_assist) ~ 10,
!is.na(pass.goal_assist) ~ 20,
TRUE ~ 0
)
) %>%
select(player.name, location.x, location.y, danger_score,
pass.outcome.name, pass.height.name)
}
# Team-level wide play analysis
analyze_team_wide_play <- function(events_data, team_name) {
team_events <- events_data %>% filter(team.name == team_name)
# Crosses analysis
team_crosses <- team_events %>%
filter(type.name == "Pass" & pass.cross == TRUE)
# Wide passes (outer channels)
wide_passes <- team_events %>%
filter(type.name == "Pass" & (location.y < 20 | location.y > 60))
list(
total_crosses = nrow(team_crosses),
cross_completion = mean(is.na(team_crosses$pass.outcome.name)) * 100,
wide_passes = nrow(wide_passes),
wide_pass_completion = mean(is.na(wide_passes$pass.outcome.name)) * 100,
crosses_per_match = nrow(team_crosses),
key_crosses = sum(!is.na(team_crosses$pass.shot_assist))
)
}
# Player crossing profiles
player_crossing_profile <- function(events_data) {
events_data %>%
filter(type.name == "Pass" & pass.cross == TRUE) %>%
group_by(player.name, team.name) %>%
summarise(
crosses = n(),
completed = sum(is.na(pass.outcome.name)),
completion_rate = (completed / crosses) * 100,
key_crosses = sum(!is.na(pass.shot_assist)),
assists = sum(!is.na(pass.goal_assist)),
avg_length = mean(pass.length, na.rm = TRUE),
.groups = "drop"
) %>%
arrange(desc(crosses)) %>%
filter(crosses >= 5) # Minimum threshold
}
# Execute analysis
cross_completion_stats <- analyze_cross_completion(crosses)
print(cross_completion_stats)
zone_analysis <- analyze_crossing_zones(crosses)
print(zone_analysis)
cross_xa_stats <- calculate_cross_xa(events)
print(cross_xa_stats)
# Generate visualizations
crossing_map <- plot_crossing_map(crosses)
print(crossing_map)
# Team analysis
team_wide_stats <- analyze_team_wide_play(events, "Arsenal")
print(team_wide_stats)
# Player profiles
player_profiles <- player_crossing_profile(events)
print(player_profiles)
Practical Applications
Tactical Analysis and Match Preparation: Coaches use crossing analytics to identify opposition weaknesses in defending wide areas. By analyzing which zones opponents struggle to defend crosses from, teams can design attacking patterns that exploit these vulnerabilities. For instance, if data shows a team concedes more from low crosses originating from the byline, the attacking team can instruct wingers to get beyond defenders before delivering.
Player Recruitment and Scouting: Crossing metrics are essential for identifying wide players and fullbacks who can consistently create chances. Scouts look for high cross completion rates combined with strong xA values, indicating not just accuracy but also the ability to find dangerous areas. Metrics like crosses per 90 minutes, key crosses, and assist conversion rates help differentiate between volume crossers and quality crossers.
Performance Evaluation: Individual player assessments benefit from crossing analytics by providing objective measures of wide play contribution. Instead of relying solely on assists (which depend on teammates finishing), cross xA and danger scores offer more complete pictures of creative output from wide areas.
Opposition Analysis: Defensive coaches study opponent crossing patterns to prepare defensive schemes. Understanding whether teams prefer early crosses, cutbacks, or deep crosses allows defenders to position appropriately and goalkeepers to adjust their starting positions.
Style of Play Development: Crossing data informs broader tactical decisions about team style. Teams can determine whether they're more effective with possession-based buildup leading to crosses or quick transitions to wide areas. Comparing crossing success rates from different tactical approaches helps refine overall playing philosophy.
Key Takeaways
- Crossing effectiveness requires measuring both quantity (crosses attempted) and quality (completion rate, xA, danger zones)
- Location analysis reveals that crosses from the byline and half-spaces often generate higher quality chances than crosses from deeper positions
- Low and driven crosses typically have higher conversion rates than lofted crosses, though this varies by team personnel and aerial ability
- Cross completion rates vary significantly by zone, with crosses from the final third generally having lower completion but higher danger
- Expected assists (xA) from crosses provides better insight into creative contribution than raw assist numbers
- Wide play analysis should include not just crosses but also progressive passes through wide channels and box entries via dribbling
- Player crossing profiles should consider match context, opposition quality, and tactical instructions when evaluating performance
- Modern analytics increasingly favor cutbacks and pull-backs over traditional lofted crosses due to higher conversion rates