The 2025 Australian Federal Election featured tight races for a number of key seats.
Small variations in the percentage of votes cast had a big impact on the result.
This report examines how changes in voter support across the country influenced the final results.
Seats are classified as safe or marginal according on how close the result was.
A marginal seat is one where the winning party received less than 6% of the vote, increasing the likelihood of a change in power.
This graph depicts how many seats fell into each category in the 2025 election.
This graph depicts the divisions with the greatest shifts in voter choice.
A larger shift indicates that voters have shifted their support significantly from the last election.
In several of these divisions, a single digit difference was enough to determine who won the seat.
These top ten divisions experienced the most significant shifts, demonstrating how dynamic the 2025 election truly was.
These are the divisions that changed hands in 2025.
The table shows which party lost and which party gained the seat.
Marginal seats had a significant influence on the 2025 result.
Small swings changed the political landscape.
Data visualisation helps make sense of how electoral power shifts.
Australian Electoral Commission. (2025). Tally Room 2025 Federal Election Results. https://tallyroom.aec.gov.au
Australian Electoral Commission. (2022). Two-party-preferred results by division – House of Representatives. https://results.aec.gov.au/27966/website/HouseTppByDivision-27966-NAT.htm
RStudio. (n.d.). Using flexdashboard Appearance Options. Retrieved from https://rstudio.github.io/flexdashboard/articles/using.html#appearance
---
title: "Changing Votes, Changing Outcomes: The 2025 Election Story"
author: "Anjalika Fernando"
output:
flexdashboard::flex_dashboard:
orientation: columns
storyboard: true
social: menu
source_code: embed
---
```{r setup, include=FALSE}
library(tidyverse)
library(flexdashboard)
library(readr)
library(ggplot2)
library(DT)
library(scales)
library(forcats)
```
```{r data-load, include=FALSE}
# Load datasets
prefsParty <- read_csv("data/HouseFirstPrefsByPartyDownload-31496.csv", skip = 1)
changedHands <- read_csv("data/HouseSeatsWhichChangedHandsDownload-31496.csv", skip = 1)
tppDivision <- read_csv("data/HouseTppByDivisionDownload-31496.csv", skip = 1)
tppState <- read_csv("data/HouseTppByStateDownload-31496.csv", skip = 1)
tpp_2022 <- read_csv("data/HouseTppByDivisionDownload-27966.csv", skip = 1)
tpp_2025 <- read_csv("data/HouseTppByDivisionDownload-31496.csv", skip = 1)
```
### Introduction {.storyboard}
- The 2025 Australian Federal Election featured tight races for a number of key seats.
- Small variations in the percentage of votes cast had a big impact on the result.
- This report examines how changes in voter support across the country influenced the final results.
### Understanding Seat Margins
```{r}
tppDivision <- tppDivision %>%
mutate(Margin = abs(as.numeric(Swing))) %>%
mutate(MarginType = ifelse(Margin < 6, "Marginal", "Safe"))
margin_summary <- tppDivision %>%
count(MarginType)
ggplot(margin_summary, aes(x = fct_rev(MarginType), y = n, fill = MarginType)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = n), vjust = -0.3) +
scale_fill_manual(values = c("Safe" = "#008ac5", "Marginal" = "#00c698")) +
labs(title = "2025 Seat Margin Breakdown", y = "Number of Seats", x = NULL) +
theme_minimal()
```
***
- Seats are classified as safe or marginal according on how close the result was.
- A marginal seat is one where the winning party received less than 6% of the vote, increasing the likelihood of a change in power.
- This graph depicts how many seats fell into each category in the 2025 election.
### National Shift Overview
```{r}
topVotes <- tppDivision %>%
select(DivisionNm, Swing) %>%
mutate(Swing = as.numeric(Swing)) %>%
slice_max(order_by = abs(Swing), n = 10)
ggplot(topVotes, aes(x = reorder(DivisionNm, Swing), y = Swing, fill = Swing)) +
geom_col() +
scale_fill_gradient2(low = "red", mid = "white", high = "blue", midpoint = 0) +
coord_flip() +
labs(title = "Top 10 Divisions by Vote Shift (%)", y = "% Vote Shift", x = "Division") +
theme_minimal()
```
***
- This graph depicts the divisions with the greatest shifts in voter choice.
- A larger shift indicates that voters have shifted their support significantly from the last election.
- In several of these divisions, a single digit difference was enough to determine who won the seat.
- These top ten divisions experienced the most significant shifts, demonstrating how dynamic the 2025 election truly was.
### Seats That Flipped
```{r}
datatable(changedHands %>%
select(StateAb, DivisionNm, PreviousPartyNm, PartyNm),
colnames = c("State", "Division", "From Party", "To Party"))
```
***
- These are the divisions that changed hands in 2025.
- The table shows which party lost and which party gained the seat.
### State by State Breakdown
```{r}
tppState <- tppState %>% mutate(VoteShift = as.numeric(Swing))
ggplot(tppState, aes(x = StateNm, y = VoteShift, fill = VoteShift)) +
geom_col(width = 0.6) +
scale_fill_gradient2(low = "#e6550d", mid = "white", high = "#31a354", midpoint = 0) +
geom_text(aes(label = round(Swing, 1)),
vjust = ifelse(tppState$VoteShift >= 0, -0.5, 1.5),
size = 3.5) +
labs(title = "Vote Shift by State (%)", y = "% Vote Shift", x = NULL) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 30, hjust = 1, size = 6),
plot.margin = margin(t = 10, r = 15, b = 35, l = 15)
)
```
***
- The graph represents the variance in voter support for the major parties among states in 2025.
### Minor Parties and Independents
```{r}
minor_wins <- prefsParty %>%
filter(!PartyAb %in% c("ALP", "LP", "LNP")) %>%
select(PartyNm, Elected) %>%
filter(Elected > 0) %>%
arrange(desc(Elected)) %>%
slice_max(order_by = Elected, n = 10)
ggplot(minor_wins, aes(x = reorder(PartyNm, Elected), y = Elected, fill = PartyNm)) +
geom_col(show.legend = FALSE, width = 0.6) +
geom_text(aes(label = Elected), vjust = -0.3, size = 4) +
labs(title = "Minor Parties Seat Gains (2025)", y = "Seats Won", x = NULL) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 30, hjust = 1),
plot.margin = margin(t = 10, r = 10, b = 35, l = 10)
)
```
***
- Minor parties increased their seat count in 2025.
- This chart shows how many seats each non major party won.
### Voter Turnout
```{r}
library(scales)
ggplot(tppState, aes(x = reorder(StateNm, TotalVotes), y = TotalVotes)) +
geom_col(fill = "#00c698", width = 0.6) +
geom_text(aes(label = comma(TotalVotes)), vjust = -0.5, size = 3.5) +
labs(title = "Voter Turnout by State", y = "Total Votes", x = NULL) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 30, hjust = 1),
plot.margin = margin(t = 10, r = 10, b = 40, l = 10)
)
```
***
- This chart provides the total number of votes given in each state.
### What’s Changed Since 2022?
```{r}
# Prepare and join
voteCompare <- inner_join(
tpp_2022 %>% select(DivisionNm, Swing) %>% mutate(Vote_2022 = as.numeric(Swing)) %>% select(-Swing),
tpp_2025 %>% select(DivisionNm, Swing) %>% mutate(Vote_2025 = as.numeric(Swing)) %>% select(-Swing),
by = "DivisionNm"
) %>%
filter(abs(Vote_2025) < 20) %>%
mutate(SwingDiff = abs(Vote_2025 - Vote_2022)) %>%
slice_max(SwingDiff, n = 6) %>%
pivot_longer(c(Vote_2022, Vote_2025), names_to = "Year", values_to = "Swing")
# Plot
ggplot(voteCompare, aes(x = DivisionNm, y = Swing, fill = Year)) +
geom_col(position = "dodge") +
labs(title = "Swing Comparison: 2022 vs 2025", y = "% Swing", x = NULL) +
scale_fill_manual(values = c("Vote_2022" = "#bdbdbd", "Vote_2025" = "#756bb1")) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 30, hjust = 1))
```
***
- This graph compares vote shifts in critical divisions from the 2022 and 2025 elections.
### Conclusion
- Marginal seats had a significant influence on the 2025 result.
- Small swings changed the political landscape.
- Data visualisation helps make sense of how electoral power shifts.
### References {.storyboard}
- Australian Electoral Commission. (2025). *Tally Room 2025 Federal Election Results*. https://tallyroom.aec.gov.au
- Australian Electoral Commission. (2022). *Two-party-preferred results by division – House of Representatives*. https://results.aec.gov.au/27966/website/HouseTppByDivision-27966-NAT.htm
- RStudio. (n.d.). *Using flexdashboard Appearance Options*. Retrieved from https://rstudio.github.io/flexdashboard/articles/using.html#appearance