1. Introduction

Clearances are an important part of Australian Football League (AFL) performance, allowing teams to gain possession following centre bounces and stoppages. But does winning more clearances than the opposition relate to a greater winning margin?

This tutorial explores the relationship between clearance differential and match margin using data from the 2026 AFL men’s season. Simple linear regression in R is used to examine this relationship through data preparation, visualisation, statistical modelling and interpretation.

The analysis focuses on two key variables:

  • Clearance differential: The difference between the home team’s total clearances and the away team’s total clearances.
  • Match margin: The difference between the home team’s final score and the away team’s final score.

Positive values indicate an advantage to the home team, while negative values indicate an advantage to the away team.

2. Loading the Data

The fitzRoy package is used to retrieve official AFL statistics and match results, while tidyverse is used for data preparation and visualisation, and knitr for presenting tables.

If the packages are not already installed, they can be installed by running the following code once in the RStudio Console:

install.packages(c("fitzRoy", "tidyverse", "knitr"))
# Load required packages
library(fitzRoy)
library(tidyverse)
library(knitr)
# Retrieve AFL player statistics and match results for the 2026 season
afl_stats <- fetch_player_stats(season = 2026, comp = "AFLM", source = "AFL")
match_results <- fetch_results(season = 2026, comp = "AFLM", source = "AFL")

3. Preparing the Data

To compare teams, we first add individual player clearances together to calculate each team’s total clearances per match.

# Groups player statistics by match and team
team_clearances <- afl_stats %>%
  group_by(providerId, utcStartTime, teamStatus,
           `home.team.name`, `away.team.name`) %>%
  
  # Adds individual player clearances to calculate team totals
  summarise(
    clearances = sum(clearances.totalClearances, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  
  # Converts team status to lowercase for consistency
  mutate(teamStatus = str_to_lower(teamStatus)) %>%
  select(providerId, utcStartTime, `home.team.name`,
         `away.team.name`, teamStatus, clearances) %>%
  
  # Creates separate columns for home and away team clearances
  pivot_wider(names_from = teamStatus, values_from = clearances) %>%
  
  # Renames variables and calculates the clearance differential
  transmute(
    providerId,
    match_time = as.POSIXct(substr(as.character(utcStartTime), 1, 19),
                            format = "%Y-%m-%dT%H:%M:%S", tz = "UTC"),
    home_team = `home.team.name`,
    away_team = `away.team.name`,
    home_clearances = home,
    away_clearances = away,
    clearance_diff = home - away
  )

Next, we calculate match margins from the official AFL scores and match them to the clearance totals. We use match identifiers where possible, with match times as a fallback.

# Prepare match results and calculate the match margin
results_clean <- match_results %>%
  transmute(
    result_id = as.character(`match.matchId`),
    match_time = as.POSIXct(`match.date`, tz = "UTC"),
    margin = as.numeric(`homeTeamScore.matchScore.totalScore`) -
             as.numeric(`awayTeamScore.matchScore.totalScore`)
  )

# Convert match IDs to the same data type
team_clearances <- team_clearances %>%
  mutate(
    providerId = as.character(providerId)
  )

# Match clearance statistics with match results using match IDs
id_matches <- team_clearances %>%
  inner_join(results_clean, by = c("providerId" = "result_id"),
             suffix = c("", ".result"))

# Match clearance statistics with match results using match times
time_matches <- team_clearances %>%
  inner_join(results_clean, by = "match_time")

# Use match IDs if all matches are found, otherwise use match times
if (nrow(id_matches) == nrow(match_results)) {
  afl_matches <- id_matches
} else {
  afl_matches <- time_matches
}

# Remove missing values and convert match dates to Melbourne time
afl_matches <- afl_matches %>%
  filter(!is.na(clearance_diff), !is.na(margin)) %>%
  mutate(match_date = as.Date(match_time, tz = "Australia/Melbourne"))

# Check that all matches are included and there are no duplicates
if (nrow(afl_matches) != nrow(match_results) ||
    n_distinct(afl_matches$providerId) != nrow(afl_matches)) {
  stop("Some AFL matches could not be matched uniquely. Check match IDs and start times.")
}

A positive clearance differential means the home team recorded more clearances; a positive match margin means the home team won.

# Select the match details and clearance statistics to display
afl_matches %>%
  select(match_date, home_team, away_team,
         home_clearances, away_clearances, clearance_diff, margin) %>%
  
  # Display the first six matches
  head(6) %>%
  
  # Present the results in a formatted table
  kable(caption = "Table 1. Example matches from the 2026 AFL season")
Table 1. Example matches from the 2026 AFL season
match_date home_team away_team home_clearances away_clearances clearance_diff margin
2026-03-05 Sydney Swans Carlton 40 39 1 63
2026-03-06 Gold Coast SUNS Geelong Cats 34 41 -7 56
2026-03-07 GWS GIANTS Hawthorn 44 37 7 27
2026-03-07 Brisbane Lions Western Bulldogs 45 39 6 -5
2026-03-08 St Kilda Collingwood 31 29 2 -12
2026-03-12 Carlton Richmond 37 29 8 4

4. Exploring and Visualising the Data

4.1 Summary Statistics

Before creating graphs, we can summarise the typical values and variation in clearance differential and match margin.

# Calculate summary statistics for clearance differential and match margin
summary_table <- afl_matches %>%
  summarise(
    `Mean clearance differential` = round(mean(clearance_diff), 1),
    `Median clearance differential` = round(median(clearance_diff), 1),
    `SD clearance differential` = round(sd(clearance_diff), 1),
    `Minimum clearance differential` = min(clearance_diff),
    `Maximum clearance differential` = max(clearance_diff),
    `Mean match margin` = round(mean(margin), 1),
    `SD match margin` = round(sd(margin), 1)
  ) %>%
  
  # Organise the statistics into two columns
  pivot_longer(everything(), names_to = "Statistic", values_to = "Value")

# Display the summary statistics in a formatted table
kable(summary_table, caption = "Table 2. Summary statistics for 2026 AFL matches")
Table 2. Summary statistics for 2026 AFL matches
Statistic Value
Mean clearance differential 0.9
Median clearance differential 0.0
SD clearance differential 9.3
Minimum clearance differential -22.0
Maximum clearance differential 24.0
Mean match margin 7.2
SD match margin 40.3

The mean and median describe typical values, while the standard deviation and range describe variation between matches.

4.2 Distribution of Clearance Differentials

A histogram shows how often different clearance differentials occur. Values near zero indicate similar clearance totals for the two teams.

# Create a histogram showing the distribution of clearance differentials
ggplot(afl_matches, aes(x = clearance_diff)) +
  
  # Group clearance differentials into intervals of five
  geom_histogram(binwidth = 5, fill = "navyblue", colour = "white") +
  
  # Add a dashed line at zero to indicate equal clearances
  geom_vline(xintercept = 0, linetype = "dashed", colour = "red") +
  
  # Add the graph title and axis labels
  labs(title = "Distribution of Clearance Differentials",
       subtitle = "2026 AFL men's season",
       x = "Clearance differential (home - away)",
       y = "Number of matches") +
  
  # Apply a simple theme to the graph
  theme_minimal(base_size = 13)

4.3 Comparing Clearances in Wins and Losses

A boxplot compares clearance differentials for home-team wins and losses. Draws are excluded from this comparison.

# Categorise matches as home wins, losses or draws based on match margin
afl_matches <- afl_matches %>%
  mutate(result = case_when(
    margin > 0 ~ "Home Win",
    margin < 0 ~ "Home Loss",
    TRUE ~ "Draw"
  ))

# Exclude drawn matches and compare clearance differentials by match result
ggplot(afl_matches %>% filter(result != "Draw"),
       aes(x = result, y = clearance_diff, fill = result)) +
  
  # Create a boxplot showing the distribution for wins and losses
  geom_boxplot(alpha = 0.8, width = 0.5) +
  
  # Set the colours for home wins and losses
  scale_fill_manual(values = c("Home Win" = "lightgreen",
                               "Home Loss" = "orange")) +
  
  # Add the graph title and axis labels
  labs(title = "Clearance Differentials in Wins and Losses",
       subtitle = "2026 AFL men's season",
       x = "Match result", y = "Clearance differential (home - away)") +
  
  # Apply a simple theme and remove the legend
  theme_minimal(base_size = 13) +
  theme(legend.position = "none")

The line inside each box is the median. Home-team wins generally had a higher median clearance differential than home-team losses, suggesting a positive relationship between clearance performance and winning matches.

4.4 Clearance Differential and Match Margin

Each dot on this scatterplot represents one match. The fitted line shows the estimated linear relationship between clearance differential and match margin.

# Create a scatterplot comparing clearance differential and match margin
ggplot(afl_matches, aes(x = clearance_diff, y = margin)) +
  
  # Add a point for each match
  geom_point(colour = "navyblue", alpha = 0.6, size = 2.2) +
  
  # Add a linear regression line with a confidence interval
  geom_smooth(method = "lm", se = TRUE, colour = "red") +
  
  # Add dashed lines at zero on both axes
  geom_hline(yintercept = 0, linetype = "dashed", colour = "grey65") +
  geom_vline(xintercept = 0, linetype = "dashed", colour = "grey65") +
  
  # Add the graph title and axis labels
  labs(title = "Clearance Differential and AFL Match Margin",
       subtitle = "2026 AFL men's season",
       x = "Clearance differential (home - away)",
       y = "Match margin (points; home - away)") +
  
  # Apply a simple theme to the graph
  theme_minimal(base_size = 13)

The upward-sloping line indicates a positive relationship, with greater clearance differentials generally associated with higher match margins. However, the wide spread of points suggests that clearances alone do not fully explain match outcomes.

5. Simple Linear Regression

The lm() function is used to estimate how match margin changes with clearance differential.

# Fit a simple linear regression model to predict match margin from clearance differential
model <- lm(margin ~ clearance_diff, data = afl_matches)

# Display the regression results, including coefficients, R-squared and p-values
summary(model)
## 
## Call:
## lm(formula = margin ~ clearance_diff, data = afl_matches)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -120.448  -21.482   -0.893   25.050  105.324 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      5.9583     2.6071   2.285   0.0233 *  
## clearance_diff   1.3586     0.2796   4.860 2.26e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 38.3 on 216 degrees of freedom
## Multiple R-squared:  0.09856,    Adjusted R-squared:  0.09438 
## F-statistic: 23.62 on 1 and 216 DF,  p-value: 2.26e-06

The slope represents the estimated change in match margin for a one-clearance increase in differential. R-squared describes the proportion of variation in match margins explained by the linear regression model, while the p-value indicates whether there is statistical evidence of a relationship.

# Extract the slope, R-squared and p-value from the regression model
slope <- coef(model)[["clearance_diff"]]
r2 <- summary(model)$r.squared
p_value <- coef(summary(model))["clearance_diff", "Pr(>|t|)"]

# Create a formatted table with the regression results
tibble(
  Measure = c("Slope (points per clearance)", "R-squared", "p-value"),
  Value = c(
    sprintf("%.2f", slope),
    sprintf("%.3f", r2),
    ifelse(p_value < 0.001, "< 0.001", sprintf("%.3f", p_value))
  )
) %>%
  knitr::kable(caption = "Linear regression results")
Linear regression results
Measure Value
Slope (points per clearance) 1.36
R-squared 0.099
p-value < 0.001

For these matches, the slope was 1.36 points per clearance, and the model explained approximately 9.9% of the variation in match margins (R-squared = 0.099). The slope’s p-value was 2.26^{-6}.

5.1 Checking the Regression Model

A residual is the difference between the actual match margin and the margin estimated by the model. A residual plot is used to identify patterns that may not be captured by the linear regression model.

# Calculate predicted match margins and residuals from the regression model
afl_matches <- afl_matches %>%
  mutate(predicted_margin = predict(model),
         residual = margin - predicted_margin)

# Create a scatterplot comparing residuals with predicted match margins
ggplot(afl_matches, aes(x = predicted_margin, y = residual)) +
  
  # Add a point for each match
  geom_point(colour = "navyblue", alpha = 0.6, size = 2.2) +
  
  # Add a dashed line at zero to identify positive and negative residuals
  geom_hline(yintercept = 0, linetype = "dashed", colour = "red") +
  
  # Add the graph title and axis labels
  labs(title = "Residuals vs Predicted Match Margins",
       x = "Predicted match margin (points)",
       y = "Residual (points)") +
  
  # Apply a simple theme to the graph
  theme_minimal(base_size = 13)

Ideally, residuals are scattered around zero without a clear curved pattern or major change in spread. Large residuals indicate matches where clearance differential alone did not explain the margin well.

6. Conclusion

This tutorial demonstrated how simple linear regression can be applied to AFL match statistics to investigate the relationship between clearance differential and match margin.

Using 218 matches from the 2026 AFL season, the estimated relationship was positive. Clearance differential accounted for approximately 9.9% of the observed variation in match margins, suggesting that clearance performance has a relatively weak association with match outcomes.

Clearances are only one aspect of AFL performance. Scoring efficiency, inside-50 entries, defensive pressure and opposition strength may also influence results.

6.1 Key Takeaways

  • Clearance performance: A positive relationship was identified between clearance differential and match margin, indicating that greater clearance advantages were generally associated with higher match margins.
  • Model interpretation: The R-squared value of 0.099 indicates that clearance differential explained only 9.9% of the variation in match margins.
  • Practical application: Simple linear regression is useful for investigating relationships between sporting performance indicators, although multiple factors contribute to match outcomes.