1 Introduction

How consistent are AFL players’ disposal counts across a season?

This tutorial examines Bailey Smith’s and Max Holmes’s disposal counts across their 2026 AFL appearances, including regular-season matches and finals, using statistical process control (SPC).

Statistical process control (SPC) is a method for monitoring data over time. It uses control charts to show typical variation and highlight unusual values or patterns. In sport, SPC can help coaches and analysts identify changes in performance that may need closer investigation.

2 Install Packages

install.packages(c("fitzRoy", 
                   "tidyverse", 
                   "janitor", 
                   "qicharts2"))

3 Load Packages

library(fitzRoy)
## Warning: package 'fitzRoy' was built under R version 4.6.1
library(tidyverse)
library(janitor)
library(qicharts2)

4 Import and Check Data

4.1 Download player statistics

# Download player statistics for the 2026 season
player_stats <- fetch_player_stats_afltables(season = 2026) |>
  clean_names()

4.2 Check data structure

# Check the columns we need
player_stats |>
  select(date, round, first_name, surname,
         playing_for, disposals) |>
  glimpse()
## Rows: 10,028
## Columns: 6
## $ date        <date> 2026-03-05, 2026-03-05, 2026-03-05, 2026-03-05, 2026-03-0…
## $ round       <chr> "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"…
## $ first_name  <chr> "Joel", "Riley", "Nick", "Charlie", "Brodie", "Errol", "Is…
## $ surname     <chr> "Amartey", "Bice", "Blakey", "Curnow", "Grundy", "Gulden",…
## $ playing_for <chr> "Sydney", "Sydney", "Sydney", "Sydney", "Sydney", "Sydney"…
## $ disposals   <int> 7, 16, 21, 10, 16, 27, 20, 16, 18, 16, 11, 32, 7, 25, 9, 1…

5 Prepare the Data

5.1 Select players for comparison

# Create full player names and select our two players
player_disposals <- player_stats |>
  mutate(player_name = paste(first_name, surname)) |>
  filter(player_name %in% c("Bailey Smith", "Max Holmes")) |>
  select(player_name, date, round, playing_for, disposals) |>
  arrange(player_name, date)

# Preview the selected data
head(player_disposals)
## # A data frame: 6 × 5
##   player_name  date       round playing_for disposals
## * <chr>        <date>     <chr> <chr>           <int>
## 1 Bailey Smith 2026-03-06 1     Geelong            23
## 2 Bailey Smith 2026-03-14 2     Geelong            31
## 3 Bailey Smith 2026-03-26 4     Geelong            40
## 4 Bailey Smith 2026-04-06 5     Geelong            33
## 5 Bailey Smith 2026-04-12 6     Geelong            34
## 6 Bailey Smith 2026-04-17 7     Geelong            33

5.2 Check appearances and missing disposals

player_disposals |>
  group_by(player_name) |>
  summarise(
    appearances = n(),
    missing_disposals = sum(is.na(disposals)),
    .groups = "drop")
## # A tibble: 2 × 3
##   player_name  appearances missing_disposals
##   <chr>              <int>             <int>
## 1 Bailey Smith          24                 0
## 2 Max Holmes            20                 0

6 Create a Separate Dataset for Each Player

# Bailey Smith's appearances in chronological order
smith_disposals <- player_disposals |>
  filter(player_name == "Bailey Smith") |>
  arrange(date)

# Max Holmes' appearances in chronological order
holmes_disposals <- player_disposals |>
  filter(player_name == "Max Holmes") |>
  arrange(date)

7 Create Control Charts

7.1 Individual charts

The individual charts show each player’s disposals in match order. The centre line represents their average, while the control limits help identify unusually high or low disposal counts.

qic(smith_disposals$disposals,
    chart = "i",
    title = "Bailey Smith: Disposals per Match",
    xlab = "Appearance number")

qic(holmes_disposals$disposals,
    chart = "i",
    title = "Max Holmes: Disposals per Match",
    xlab = "Appearance number")

7.2 Moving Range Charts

The moving range charts show the absolute difference in disposals between consecutive appearances. For example, changing from 25 to 30 disposals produces a moving range of 5. Larger values indicate bigger changes between matches, regardless of whether disposals increased or decreased.

qic(smith_disposals$disposals,
    chart = "mr",
    title = "Bailey Smith: Moving Range",
    xlab = "Appearance number")

qic(holmes_disposals$disposals,
    chart = "mr",
    title = "Max Holmes: Moving Range",
    xlab = "Appearance number")

8 Interpret Results

8.1 Individual Charts

Bailey Smith averaged approximately 32.2 disposals per appearance. All observations fall within the control limits of approximately 18.8 and 45.7 disposals. Although his disposal counts fluctuate, no appearance is flagged as unusually high or low by these limits.

Max Holmes averaged approximately 27.9 disposals per appearance. All observations fall within the control limits of approximately 14.7 and 41.2 disposals. His disposal counts decrease towards the end of the sequence, but these observations remain within the limits.

8.2 Moving Range Charts

Smith’s average absolute change between consecutive appearances was approximately five disposals. Some changes were much larger than others, but all remained below the upper control limit of approximately 16.5. The chart therefore does not flag any change as unusually large using this limit.

Holmes’s average absolute change between consecutive appearances was also approximately five disposals. All changes remained below the upper control limit of approximately 16.3. This suggests that the observed changes were within the range estimated by the chart.

9 Conclusion

This tutorial demonstrated how to use individual and moving range charts to examine AFL players’ disposal counts over time.

Smith averaged more disposals per appearance than Holmes, but both players had an average absolute change of approximately five disposals between consecutive appearances. Neither player had observations outside the control limits on either chart.

These results describe variation in disposal counts rather than overall playing performance. Playing time, role and opposition can affect disposals, so the charts should provide a starting point for further investigation.