The number of injury crashes in Victoria is in the thousands each year, however, crash counts do not reveal where the highest numbers of fatal and serious outcomes in crash situations are occurring. This visual story explores the increase in the likelihood of recorded injury crashes in Victoria being classified as serious or fatal, when, by road conditions, amongst whom and where.

Analysis is based on full calendar years for the period 2012-25. Severely crashes are defined here as a crash classified in the source data as either a Serious injury accident or a Fatal accident. Percentages are NOT population/ exposure-based measures of crash risk; they are a look at the percentage of recorded injury crashes that are severe.

The information used consists of over 200,000 crash data from several years. The period of years used for this particular project was for the full years from 2012 through 2025 to avoid the distortion of any data from a partial year. Because the primary objective is to gain an understanding of the severity of crash consequences, fatal and serious injury crashes were combined into severe crashes.

I have looked at variables that deal with the time of the crash, its severity, speed zones, lighting conditions, location, and the type of road users involved to explore the story. These variables enabled me to compare the severity of crashes over the course of the day, in different road environments, among the different groups of road users and in the various regions of Victoria.

Data preparation

Data was cleaned prior to creating visualisations. The data was limited to calendar years of the years 2012 to 2025 with the exception of the data from 2026 being an incomplete year. Non-injury crashes were excluded as this study is about the severity of injury crashes.

Fatal and serious injury crashes were combined into a single category (severe), and all other injury crashes remained in their own category. The year, month and hour were extracted from the crash date and time, and where available, numeric values for speed-zone were extracted. Where a value was missing, this was addressed separately for each visualisation, to not lose valuable data from the overall set of data.

data_path <- "C:/Users/aathi/Downloads/victorian_road_crash_data (1).csv"

crashes_raw <- read_csv(
  data_path,
  show_col_types = FALSE
)

# Clean and prepare the crash data
crashes <- crashes_raw %>%
  mutate(
    # Create time variables
    Year = year(ACCIDENT_DATE),
    Month = month(
      ACCIDENT_DATE,
      label = TRUE,
      abbr = TRUE
    ),
    Hour = hour(ACCIDENT_TIME),

    # Convert speed zone into numeric values where possible
    Speed_numeric = as.numeric(
      str_extract(SPEED_ZONE, "\\d+")
    ),

    # Put days of the week in correct order
    DAY_OF_WEEK = factor(
      DAY_OF_WEEK,
      levels = c(
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
        "Sunday"
      )
    )
  ) %>%

  # Keep complete calendar years only
  filter(
    Year >= 2012,
    Year <= 2025
  ) %>%

  # Remove non-injury crashes
  filter(
    SEVERITY != "Non injury accident"
  ) %>%

  # Create simplified severity category
  mutate(
    Severe = case_when(
      SEVERITY %in% c(
        "Fatal accident",
        "Serious injury accident"
      ) ~ "Severe",

      SEVERITY == "Other injury accident" ~ "Other injury",

      TRUE ~ NA_character_
    )
  ) %>%

  # Remove records without a usable severity category
  filter(
    !is.na(Severe)
  )

# Statewide severe-crash percentage
statewide_severe_rate <- mean(
  crashes$Severe == "Severe",
  na.rm = TRUE
) * 100

1. The quietest hours can produce some of the most severe crashes

The volume of the crashes is not the only thing. The percentage of injury serious or fatal crashes is greater in early morning compared to daytime hours and fewer injury crashes are experienced during early morning hours compared with daytime hours.

p1 <- ggplot(
  day_hour_summary,
  aes(
    x = Hour,
    y = DAY_OF_WEEK,
    fill = Severe_rate,
    text = hover
  )
) +
  geom_tile(colour = "white", linewidth = 0.25) +
  scale_fill_viridis_c(
    option = "C",
    direction = -1,
    labels = label_number(accuracy = 1, suffix = "%")
  ) +
  scale_x_continuous(
    breaks = seq(0, 23, by = 2),
    labels = function(x) sprintf("%02d:00", x)
  ) +
  labs(
    x = "Hour of day",
    y = NULL,
    fill = "Serious/fatal\nshare"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    panel.grid = element_blank(),
    legend.position = "right"
  )

plot1 <- ggplotly(
  p1,
  tooltip = "text",
  height = 470
) %>%
  layout(
    margin = list(l = 85, r = 130, t = 20, b = 70),
    xaxis = list(automargin = TRUE),
    yaxis = list(automargin = TRUE)
  ) %>%
  config(
    responsive = TRUE,
    displaylogo = FALSE
  )

plot1

Source: Victorian Road Crash Data, Department of Transport and Planning. Complete years 2012–2025.

2. Higher-speed road environments carry a heavier severity burden

The percentage of the total number of injury crashes that are classified as serious or fatal tends to increase with increased speed limits. The posted limit is a description of the roadway environment, and not a measurement of the actual travelling speed of the vehicle at the time of the crash.

p2 <- ggplot(
  speed_summary_final,
  aes(
    x = Speed_numeric,
    y = Severe_rate,
    text = hover
  )
) +
  geom_line(linewidth = 1, colour = "#3C5488") +
  geom_point(
    aes(size = Total_crashes),
    colour = "#3C5488",
    alpha = 0.85
  ) +
  scale_size_continuous(range = c(4, 11), guide = "none") +
  scale_x_continuous(
    breaks = c(40, 50, 60, 70, 80, 90, 100, 110)
  ) +
  scale_y_continuous(
    labels = label_number(accuracy = 1, suffix = "%")
  ) +
  labs(
    x = "Posted speed zone (km/h)",
    y = "Serious/fatal crash share"
  ) +
  theme_minimal(base_size = 12) +
  theme(panel.grid.minor = element_blank())

plot2 <- ggplotly(
  p2,
  tooltip = "text",
  height = 460
) %>%
  layout(
    margin = list(l = 85, r = 30, t = 20, b = 75),
    xaxis = list(automargin = TRUE),
    yaxis = list(automargin = TRUE)
  ) %>%
  config(
    responsive = TRUE,
    displaylogo = FALSE
  )

plot2

Source: Victorian Road Crash Data, Department of Transport and Planning. Percentages are calculated within each posted speed-zone category.

3. The same road environment does not affect every road-user group equally

There are several crashes that are particularly severe: motorcyclist-involved crash; pedestrian-involved crash; driver age 75 or older. The variations are particularly noticeable on higher speed roads.

p3 <- ggplot(
  road_user_speed,
  aes(
    x = Speed_numeric,
    y = Severe_rate,
    colour = Group,
    group = Group,
    text = hover
  )
) +
  geom_line(linewidth = 0.9, alpha = 0.9) +
  geom_point(size = 2.8) +
  scale_colour_viridis_d(option = "D", end = 0.9) +
  scale_x_continuous(
    breaks = c(40, 50, 60, 70, 80, 90, 100, 110)
  ) +
  scale_y_continuous(
    labels = label_number(accuracy = 1, suffix = "%")
  ) +
  labs(
    x = "Posted speed zone (km/h)",
    y = "Serious/fatal crash share",
    colour = "Crash involved"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    panel.grid.minor = element_blank(),
    legend.position = "bottom"
  )

plot3 <- ggplotly(
  p3,
  tooltip = "text",
  height = 540
) %>%
  layout(
    margin = list(l = 85, r = 30, t = 20, b = 150),
    legend = list(
      orientation = "h",
      x = 0,
      y = -0.30,
      xanchor = "left",
      yanchor = "top"
    ),
    xaxis = list(automargin = TRUE),
    yaxis = list(automargin = TRUE)
  ) %>%
  config(
    responsive = TRUE,
    displaylogo = FALSE
  )

plot3

Source: Victorian Road Crash Data, Department of Transport and Planning. Road-user categories can overlap because a single crash can involve more than one road-user type.

4. The places with the most crashes are not always the places with the most severe outcomes

While most recorded injury crashes are in the metro areas, there are a few regional areas with a higher percentage of serious-or-fatal crashes. These are shown by the size of the point and the dashed line represents the number of severe crashes statewide.

p4 <- ggplot(
  region_summary_final,
  aes(
    x = Severe_rate,
    y = DTP_REGION,
    text = hover
  )
) +
  geom_vline(
    xintercept = statewide_severe_rate,
    linetype = "dashed",
    colour = "grey45"
  ) +
  geom_segment(
    aes(
      x = statewide_severe_rate,
      xend = Severe_rate,
      yend = DTP_REGION
    ),
    colour = "grey75",
    linewidth = 1
  ) +
  geom_point(
    aes(
      size = Total_crashes,
      colour = Area_type
    ),
    alpha = 0.9
  ) +
  scale_size_continuous(range = c(5, 16), guide = "none") +
  scale_colour_manual(
    values = c(
      "Metropolitan" = "#0072B2",
      "Regional" = "#D55E00"
    )
  ) +
  scale_x_continuous(
    labels = label_number(accuracy = 1, suffix = "%")
  ) +
  labs(
    x = "Serious/fatal crash share",
    y = NULL,
    colour = NULL
  ) +
  theme_minimal(base_size = 12) +
  theme(
    panel.grid.major.y = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "bottom"
  )

plot4 <- ggplotly(
  p4,
  tooltip = "text",
  height = 500
) %>%
  layout(
    margin = list(l = 150, r = 30, t = 20, b = 100),
    legend = list(
      orientation = "h",
      x = 0,
      y = -0.20
    ),
    xaxis = list(automargin = TRUE),
    yaxis = list(automargin = TRUE)
  ) %>%
  config(
    responsive = TRUE,
    displaylogo = FALSE
  )

plot4

Source: Victorian Road Crash Data, Department of Transport and Planning. The statewide reference line is based on all included injury crashes from 2012–2025.

5. High-speed road environments align with more severe outcomes across LGAs

In Victorian LGAs where there were 500 or more recorded injury crashes, areas with a higher proportion of crashes in 80+ km/h areas also have a higher proportion of serious-or-fatal crashes. This correlation is significant (Pearson r ≈ 0.70); it is important to note that this is a correlation – not a cause-and-effect relationship.

p5 <- ggplot(
  lga_environment,
  aes(
    x = High_speed_share,
    y = Severe_rate,
    size = Total_crashes,
    colour = DTP_REGION,
    text = hover
  )
) +
  geom_point(alpha = 0.80) +
  geom_smooth(
    aes(x = High_speed_share, y = Severe_rate),
    inherit.aes = FALSE,
    method = "lm",
    se = FALSE,
    colour = "grey35",
    linewidth = 0.8
  ) +
  scale_size_continuous(
    range = c(4, 13),
    guide = "none"
  ) +
  scale_colour_viridis_d(
    option = "D",
    end = 0.85
  ) +
  scale_x_continuous(
    labels = label_number(accuracy = 1, suffix = "%")
  ) +
  scale_y_continuous(
    labels = label_number(accuracy = 1, suffix = "%")
  ) +
  labs(
    x = "Crashes in 80+ km/h zones",
    y = "Serious/fatal crash share",
    colour = "DTP region"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "right",
    panel.grid.minor = element_blank()
  )

plot5 <- ggplotly(
  p5,
  tooltip = "text",
  height = 520
) %>%
  layout(
    autosize = TRUE,
    margin = list(
      l = 90,
      r = 190,
      t = 25,
      b = 80
    ),
    legend = list(
      orientation = "v",
      x = 1.02,
      y = 1,
      xanchor = "left",
      yanchor = "top",
      font = list(size = 11)
    ),
    xaxis = list(
      automargin = TRUE,
      title = list(
        text = "Crashes in 80+ km/h zones (%)"
      )
    ),
    yaxis = list(
      automargin = TRUE,
      title = list(
        text = "Serious/fatal crash share (%)"
      )
    )
  ) %>%
  config(
    responsive = TRUE,
    displaylogo = FALSE
  )

plot5

Source: Victorian Road Crash Data, Department of Transport and Planning. LGA comparison restricted to LGAs with at least 500 injury crashes and at least 400 crashes with a known numeric speed zone.

What does the pattern indicate?

The distribution of crash severity is not even across the five views. The impact of these consequences is worse and more likely to be fatal in the early morning, on faster speed road environments, amongst certain group of road users, and in certain geographic locations across Victoria. These patterns are associations and they describe the types of crashes that have been recorded, and should not be used as direct measures of driving risk and are not evidence of causation.

Acknowledgements

The use of Generative AI (ChatGPT, OpenAI) for troubleshooting R code, polishing written explanations.

The following packages have been used for importing, cleaning and transforming data, analysing it and visualizing it: R, RStudio, ggplot2 and plotly. Interactive visualisations made in R with the packages ffmpeg, ggplot2 and plotly.

R and RStudio were used to import, clean, transform, analyse and visualise the data. ggplot2 and plotly were used to construct the interactive visualisations.

References

Department of Transport and Planning. (2026). Victoria road crash data [Data set]. Transport Victoria Open Data. https://opendata.transport.vic.gov.au/dataset/victoria-road-crash-data

R Core Team. (2026). R: A language and environment for statistical computing [Computer software]. R Foundation for Statistical Computing. https://www.R-project.org/

Wickham, H. (2016). ggplot2: Elegant graphics for data analysis. Springer. https://doi.org/10.1007/978-3-319-24277-4

Sievert, C. (2020). Interactive web-based data visualization with R, plotly, and shiny. Chapman and Hall/CRC.