The 2025 Australian Federal Election is here!
Australians head to the polls on May 3rd, 2025, in what promises to be a pivotal moment for the nation. This storyboard explores the emerging trends, key battlegrounds, and potential outcomes as the results unfold.
Our journey through the data will paint a picture of a nation making critical choices about its future leadership and direction.
Use the navigation arrows or your keyboard arrow keys to move through the storyboard.
Narrative: The initial count reveals the overall distribution of seats across the major parties. This provides a high-level view of who is leading the race to form government. A party needs 76 seats for a majority.
Narrative: Elections are often decided in key states. This map highlights which party is currently leading in each state or territory, offering insights into regional voting patterns and critical battlegrounds.
Narrative: The swing reveals the shift in voter sentiment compared to the previous election. Positive swings indicate gains for a party, while negative swings show losses. This visualization breaks down the swing by state and party, highlighting where the political tide is turning.
Narrative: Understanding the present often requires looking at the past. This chart tracks the performance of major parties over recent election cycles, providing context for the 2025 results and highlighting long-term political trends in Australia.
The Story So Far…
The 2025 Australian Federal Election is a dynamic event. The data presented here offers an initial glimpse into the results, highlighting national standings, state-level battles, voter swings, and historical context.
As more data becomes available, a clearer picture will emerge, ultimately determining the next government of Australia.
Data Source:
Data Retrieved From:
Australian Electoral Commission. (2025). Federal Election Results. Retrieved from https://tallyroom.aec.gov.au/HousePartyRepresentationLeading-31496.htm
---
title: "Australia Votes 2025: A Nation at the Crossroads"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: scroll
storyboard: true
social: menu
source_code: embed
theme: cosmo
self_contained: true
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE)
library(flexdashboard)
library(ggplot2)
library(plotly)
library(dplyr)
library(sf)
library(viridis)
library(scales)
library(tidyr) # For pivot_longer
library(leaflet) # Explicitly load leaflet
# Load Actual Election Data
party_rep_data_raw <- read.csv("HousePartyRepresentationLeadingDownload-31496.csv", skip = 1, check.names = FALSE, stringsAsFactors = FALSE, strip.white = TRUE)
seat_summary_data_raw <- read.csv("HouseSeatSummaryDownload-31496.csv", skip = 1, check.names = FALSE, stringsAsFactors = FALSE, strip.white = TRUE)
# Clean column names to remove potential leading/trailing whitespace
names(party_rep_data_raw) <- trimws(names(party_rep_data_raw))
names(seat_summary_data_raw) <- trimws(names(seat_summary_data_raw))
# --- Data Processing ---
# National Summary (for Frame 2)
national_summary_actual <- party_rep_data_raw %>%
mutate(Seats_numeric = suppressWarnings(as.numeric(as.character(National)))) %>% # Ensure Seats is numeric
select(Party_Winning = Party, Seats = Seats_numeric) %>%
filter(!is.na(Seats) & Seats > 0 & Party_Winning != "" & !is.na(Party_Winning)) %>%
arrange(desc(Seats))
# State Summary for Map (for Frame 3)
# Using HouseSeatSummaryDownload-31496.csv
state_summary_seats <- seat_summary_data_raw %>%
filter(StateAb != "" & !is.na(StateAb) & PartyNmLeading != "" & !is.na(PartyNmLeading)) %>%
group_by(State = StateAb, Party_Winning = PartyNmLeading) %>%
summarise(Seats = n(), .groups = 'drop')
# Coordinates for states (expanded to include territories)
states_all <- c("NSW", "VIC", "QLD", "WA", "SA", "TAS", "ACT", "NT")
state_coords <- data.frame(
State = states_all,
lat = c(-33.8688, -37.8136, -27.4698, -31.9505, -34.9285, -41.4545, -35.2809, -12.4634),
lng = c(151.2093, 144.9631, 153.0251, 115.8605, 138.6007, 145.9707, 149.1300, 130.8456)
)
state_map_data_actual <- state_summary_seats %>%
group_by(State) %>%
filter(Seats == max(Seats)) %>%
slice(1) %>% # In case of ties, pick one
ungroup() %>%
left_join(state_coords, by = "State") %>%
filter(!is.na(lat) & !is.na(lng)) # Ensure only states with coords are mapped
# Swing Analysis Data (for Frame 4)
# Using HouseSeatSummaryDownload-31496.csv
swing_data_actual <- seat_summary_data_raw %>%
select(State = StateAb, Party_Winning = PartyNmLeading, Swing_TPP_Raw = Swing) %>%
mutate(Swing_TPP = suppressWarnings(as.numeric(as.character(Swing_TPP_Raw)))) %>%
filter(!is.na(Swing_TPP) & State != "" & !is.na(State) & Party_Winning != "" & !is.na(Party_Winning))
# Historical Trends Data (for Frame 5)
historical_seats_actual <- party_rep_data_raw %>%
mutate(
National_numeric = suppressWarnings(as.numeric(as.character(National))),
LastElection_numeric = suppressWarnings(as.numeric(as.character(LastElection)))
) %>%
select(Party, CurrentSeats = National_numeric, PreviousSeats = LastElection_numeric) %>%
filter(Party != "" & !is.na(Party)) %>%
# Ensure we only proceed if at least one of the seat counts is a valid number for a party
filter(!is.na(CurrentSeats) | !is.na(PreviousSeats)) %>%
tidyr::pivot_longer(cols = c(CurrentSeats, PreviousSeats), names_to = "ElectionTime", values_to = "Seats") %>%
mutate(Year = ifelse(ElectionTime == "CurrentSeats", 2025, 2022)) %>% # Adjust year as needed for 'LastElection'
filter(!is.na(Seats) & Seats >= 0) %>% # Filter out any NAs that might result from pivot or if a party had no seats
select(Year, Party, Seats)
```
### Frame 1: The Nation Decides
**The 2025 Australian Federal Election is here!**
Australians head to the polls on May 3rd, 2025, in what promises to be a pivotal moment for the nation. This storyboard explores the emerging trends, key battlegrounds, and potential outcomes as the results unfold.
*Our journey through the data will paint a picture of a nation making critical choices about its future leadership and direction.*
---
Use the navigation arrows or your keyboard arrow keys to move through the storyboard.
### Frame 2: National Snapshot - The Seat Count
```{r national_seats_chart, fig.height=4, fig.width=10}
plot_ly(national_summary_actual, x = ~Party_Winning, y = ~Seats, type = 'bar',
color = ~Party_Winning, # Removed explicit colors argument, plotly will auto-assign
text = ~Seats, textposition = 'auto',
height = 400) %>%
layout(title = '<b>National Seat Distribution - 2025 Election</b>',
xaxis = list(title = 'Party'),
yaxis = list(title = 'Number of Seats Won'),
showlegend = FALSE,
margin = list(l = 50, r = 50, b = 100, t = 100, pad = 4),
autosize = TRUE)
```
**Narrative:**
The initial count reveals the overall distribution of seats across the major parties. This provides a high-level view of who is leading the race to form government. A party needs 76 seats for a majority.
### Frame 3: State by State - Where the Election is Won and Lost
```{r state_map, fig.height=4, fig.width=10}
if (!is.null(state_map_data_actual) && nrow(state_map_data_actual) > 0 && all(c("lng", "lat") %in% names(state_map_data_actual))){
pal_map <- colorFactor(viridis::viridis(length(unique(state_map_data_actual$Party_Winning))), domain = unique(state_map_data_actual$Party_Winning))
leaflet(state_map_data_actual, width = "100%", height = 400) %>%
addTiles() %>%
addCircleMarkers(
lng = ~lng, lat = ~lat,
radius = 15,
color = ~pal_map(Party_Winning),
popup = ~paste0("<b>", State, "</b><br>Leading Party: ", Party_Winning, "<br>Seats (leading party): ", Seats),
label = ~State
) %>%
addLegend(position = "bottomright",
pal = pal_map,
values = ~Party_Winning, title = "Leading Party by State")
} else {
plot_ly(data.frame(x=1,y=1,text="Map data incomplete for visualization"), x=~x,y=~y,text=~text,type='scatter',mode='text') %>% layout(title='State Map Unavailable')
}
```
**Narrative:**
Elections are often decided in key states. This map highlights which party is currently leading in each state or territory, offering insights into regional voting patterns and critical battlegrounds.
### Frame 4: The Swing States - Voter Sentiment Shifts
```{r swing_analysis, fig.height=4, fig.width=10}
# Filter for states with notable swings or a mix of results for storytelling
plot_ly(swing_data_actual, x = ~State, y = ~Swing_TPP, type = 'violin', split = ~Party_Winning,
box = list(visible = T), meanline = list(visible = T),
height = 400, width = 800
# colors argument can be omitted if relying on plotly's auto-assignment per 'split' group
# or specify a palette if desired, e.g. colors = RColorBrewer::brewer.pal(length(unique(swing_data_actual$Party_Winning)), "Set2")
) %>%
layout(title = '<b>Two-Party Preferred Swing by State and Party</b>',
xaxis = list(title = 'State'),
yaxis = list(title = 'Swing (%)'),
violingap = 0, violingroupgap = 0, violinmode = 'overlay',
margin = list(l = 50, r = 50, b = 100, t = 100, pad = 4),
autosize = TRUE)
```
**Narrative:**
The swing reveals the shift in voter sentiment compared to the previous election. Positive swings indicate gains for a party, while negative swings show losses. This visualization breaks down the swing by state and party, highlighting where the political tide is turning.
### Frame 5: Historical Perspective - Trends Over Time
```{r historical_trends, fig.height=4, fig.width=10}
plot_ly(historical_seats_actual, x = ~Year, y = ~Seats, color = ~Party, type = 'scatter', mode = 'lines+markers',
height = 400, width = 800) %>%
# Removed explicit colors argument, plotly will auto-assign based on ~Party
layout(title = '<b>Party Seat Trends: Past Elections to 2025</b>',
xaxis = list(title = 'Election Year', tickformat = 'd'),
yaxis = list(title = 'Number of Seats'),
margin = list(l = 50, r = 50, b = 100, t = 100, pad = 4),
autosize = TRUE)
```
**Narrative:**
Understanding the present often requires looking at the past. This chart tracks the performance of major parties over recent election cycles, providing context for the 2025 results and highlighting long-term political trends in Australia.
### Frame 6: Conclusion & Data Source
**The Story So Far...**
The 2025 Australian Federal Election is a dynamic event. The data presented here offers an initial glimpse into the results, highlighting national standings, state-level battles, voter swings, and historical context.
As more data becomes available, a clearer picture will emerge, ultimately determining the next government of Australia.
---
**Data Source:**
* The primary data for this analysis is sourced from the Australian Electoral Commission (AEC) website, which publishes official election results and historical data.
* For the 2025 election, results are typically updated live on election night and finalized in the following weeks.
* Specific datasets used: "Party Representation and Seat Summary"
**Data Retrieved From:**
Australian Electoral Commission. (2025). *Federal Election Results*. Retrieved from https://tallyroom.aec.gov.au/HousePartyRepresentationLeading-31496.htm