This data-driven dashboard explores the 2025 Federal Election using
open Australian Electoral Commission (AEC) datasets.
The media labelled the result a “Labor landslide”, but a closer look at
the data reveals an uneven performance across states and many seats won
by narrow margins.
The visualisations in this storyboard are powered by Australian Electoral Commission (AEC). Two official CSV files are used to explore voting behaviour and seat competitiveness in the 2025 Federal Election.
Both AEC datasets were cleaned and transformed using R packages
tidyverse and janitor to make the results
suitable for visual analysis. The goal was to create summary tables
showing party vote shares and margins of victory at the
division level.
skip = 1).
clean_names() for
consistent formatting.
group_by() and summarise().
mutate().
Example transformation (simplified):
# A tibble: 6 × 3
party_nm total_votes share
<chr> <dbl> <dbl>
1 Animal Justice Party 4451 0.138
2 Australian Labor Party 1043383 32.4
3 Citizens Party 5673 0.176
4 FUSION | Planet Rescue | Whistleblower Protection | Innova… 3282 0.102
5 Family First 66436 2.07
6 Gerard Rennick People First 4315 0.134
This visualisation shows the share of first-preference votes
across political parties in Victoria during the 2025 Federal
Election.
It captures voter loyalty before preference redistribution in
Australia’s preferential voting system.
This chart shows the most competitive Victorian seats based on
the Two-Candidate-Preferred (TCP) results in the AEC data.
Seats with the smallest margins are the ones most likely to change hands
in the next election.
The 2025 Australian Federal Election is often described as a “Labor
landslide.”
Yet, a closer examination of the Victorian results reveals a more
complex political landscape.
Take-home message:
While aggregate results tell a story of dominance, local-level data
shows that electoral competitiveness and voter diversity remain strong
across Victoria. This demonstrates how data storytelling can move
beyond headlines to uncover deeper truths in political behaviour.
Data Sources:
---
title: "S4123208 Assignment 3"
output:
flexdashboard::flex_dashboard:
storyboard: true
theme: united
source_code: embed
---
```{r setup, include=FALSE}
library(tidyverse)
library(flexdashboard)
library(readr)
library(stringr)
library(tidyverse)
library(janitor)
knitr::opts_chunk$set(message = FALSE, warning = FALSE)
```
### Introduction
<div style="text-align:center; padding:40px;">
<h2 style="font-weight:800; color:#7B1FA2;">Beyond the Landslide</h2>
<h3 style="margin-top:-10px;">The Real Story of the 2025 Australian Federal Election</h3>
<p style="font-size:17px; max-width:800px; margin:auto;">
This data-driven dashboard explores the 2025 Federal Election using
<b>open Australian Electoral Commission (AEC)</b> datasets.
The media labelled the result a “Labor landslide”, but a closer look at the data
reveals an uneven performance across states and many seats won by
narrow margins.
</p>
<br>
<div style="text-align:left; display:inline-block; font-size:16px; color:#333;">
<b>Key Questions</b>
<ul>
<li>How did first-preference votes vary by party across Victoria?</li>
<li>Which divisions were truly marginal, and what does that mean for 2028?</li>
</ul>
</div>
<br><br>
<br><b>Assignment 3 – Data Visualisation</b>
<br><b>Student:</b> Sai Nihanth Edara (S4123208)
</p>
</div>
### About the Data
<div style="text-align:center; padding:30px;">
<h2 style="font-weight:700; color:#7B1FA2;">About the Dataset</h2>
<h3>Two Complementary Election Datasets from the AEC</h3>
<p style="font-size:17px; max-width:850px; margin:auto;">
The visualisations in this storyboard are powered by <b>Australian Electoral Commission (AEC)</b></a>.
Two official CSV files are used to explore voting behaviour and seat competitiveness in the
<b>2025 Federal Election</b>.
</p>
<br>
<div style="text-align:left; display:inline-block; font-size:16px; margin-top:10px;">
<ul>
<li><b>HouseTcpByCandidateByPollingPlaceDownload-31496.csv</b><br><br>
Provides <b>Two-Candidate-Preferred (TCP)</b> results for every polling place and division.<br>
It contains details such as state, division name, candidate, party, total votes,
and final TCP percentages used to identify <b>marginal seats</b>.</li><br>
<li><b>HouseStateFirstPrefsByPollingPlaceDownload-31496-VIC.csv</b><br><br>
Contains <b>First-Preference votes</b> for each candidate and party in Victoria.<br>
This reveals initial voter loyalties before preferences were distributed, allowing analysis of
<b>party vote share</b> across the state.</li>
</ul>
</div>
```{r}
tcp <- readr::read_csv("HouseTcpByCandidateByPollingPlaceDownload-31496.csv",
skip = 1) |>
clean_names()
prefs_vic <- readr::read_csv("HouseStateFirstPrefsByPollingPlaceDownload-31496-VIC.csv",
skip = 1) |>
clean_names()
```
### Data Cleaning & Processing
<div style="text-align:center; padding:30px;">
<h2 style="font-weight:700; color:#7B1FA2;">Data Cleaning & Processing</h2>
<h3>Preparing the AEC data for visualisation</h3>
<p style="font-size:17px; max-width:850px; margin:auto;">
Both AEC datasets were cleaned and transformed using <b>R</b> packages
<code>tidyverse</code> and <code>janitor</code> to make the results suitable for visual analysis.
The goal was to create summary tables showing <b>party vote shares</b> and
<b>margins of victory</b> at the division level.
</p>
<br>
<div style="text-align:left; display:inline-block; font-size:16px;">
<ul>
<li><b>Remove metadata rows</b> – Skipped the first line in both CSVs (<code>skip = 1</code>).</li>
<li><b>Standardise column names</b> – Used <code>clean_names()</code> for consistent formatting.</li>
<li><b>Summarise by division & party</b> – Aggregated vote totals using <code>group_by()</code> and <code>summarise()</code>.</li>
<li><b>Compute vote shares (%)</b> – Added proportional calculations with <code>mutate()</code>.</li>
<li><b>Identify marginal seats</b> – Calculated the difference between the top two TCP candidates per division.</li>
</ul>
</div>
<br>
<p style="font-size:16px;">Example transformation (simplified):</p>
```{r}
library(dplyr)
# Summarise first preferences in Victoria
vic_summary_party <- prefs_vic %>%
group_by(party_nm) %>%
summarise(total_votes = sum(ordinary_votes, na.rm = TRUE)) %>%
mutate(share = 100 * total_votes / sum(total_votes))
head(vic_summary_party)
```
### Victoria – First Preferences
<div style="text-align:center; padding:30px;">
<h2 style="font-weight:800; color:#7B1FA2; margin-bottom:5px;">Victoria – First Preference Votes by Party</h2>
<h3>Who did Victorians choose first?</h3>
<p style="font-size:17px; max-width:800px; margin:auto;">
This visualisation shows the share of <b>first-preference votes</b> across political parties in Victoria during the
<b>2025 Federal Election</b>.
It captures voter loyalty before preference redistribution in Australia’s preferential voting system.
</p>
```{r, fig.width=9, fig.height=5}
vic_summary_party <- prefs_vic %>%
group_by(party_nm) %>%
summarise(total_votes = sum(ordinary_votes, na.rm = TRUE), .groups = "drop") %>%
mutate(share = 100 * total_votes / sum(total_votes)) %>%
arrange(desc(share))
ggplot(vic_summary_party, aes(x = reorder(party_nm, share), y = share)) +
geom_col(fill = "#E53935") +
coord_flip() +
geom_text(aes(label = sprintf("%.1f%%", share)), hjust = -0.2, size = 4) +
labs(
title = "First Preference by Party in Victoria, 2025",
subtitle = "Labor leads Victoria’s first preferences",
x = NULL, y = "Vote Share (%)"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#333"),
plot.subtitle = element_text(size = 11, color = "#555"),
axis.text.y = element_text(size = 11),
axis.text.x = element_text(size = 10)
) +
ylim(0, max(vic_summary_party$share) + 5)
```
### Victoria – Marginal Divisions (TCP)
<div style="text-align:center; padding:30px;">
<h2 style="font-weight:800; color:#7B1FA2; margin-bottom:5px;">Victoria – Marginal Divisions (TCP)</h2>
<h3 margin-top:0;">Where the 2025 election was closest</h3>
<p style="font-size:17px; max-width:800px; margin:auto;">
This chart shows the <b>most competitive Victorian seats</b> based on the
<b>Two-Candidate-Preferred (TCP)</b> results in the AEC data.
Seats with the smallest margins are the ones most likely to change hands in the next election.
</p>
```{r, fig.width=9, fig.height=5}
# 1.aggregate TCP votes per division+party, but only for VIC
tcp_vic <- tcp %>%
filter(state_ab == "VIC") %>%
group_by(division_nm, party_nm) %>%
summarise(total_votes = sum(ordinary_votes, na.rm = TRUE), .groups = "drop")
# 2. for each division, sort parties by votes and keep top 2
tcp_vic_top2 <- tcp_vic %>%
group_by(division_nm) %>%
arrange(desc(total_votes), .by_group = TRUE) %>%
slice_head(n = 2) %>%
ungroup()
# 3. turn those 2 rows into wide form so we can compute margin safely
tcp_vic_wide <- tcp_vic_top2 %>%
group_by(division_nm) %>%
mutate(div_total = sum(total_votes, na.rm = TRUE)) %>%
mutate(share = 100 * total_votes / div_total) %>%
arrange(desc(share), .by_group = TRUE) %>%
# now we have exactly 2 rows per division (if there were 2 candidates)
summarise(
top_share = first(share),
second_share = nth(share, 2),
.groups = "drop"
) %>%
# keep only divisions that actually had 2 candidates
filter(!is.na(second_share)) %>%
mutate(margin = top_share - second_share)
# 4. pick the 10 smallest positive margins
marginals_vic <- tcp_vic_wide %>%
arrange(margin) %>%
slice_head(n = 10)
ggplot(marginals_vic, aes(x = reorder(division_nm, margin), y = margin)) +
geom_col(fill = "#1E88E5") +
coord_flip() +
geom_text(aes(label = sprintf("%.1f%%", margin)), hjust = -0.1, size = 4) +
labs(
title = "Most Marginal Divisions in Victoria (2025)",
subtitle = "Ten Victorian divisions with the smallest TCP winning margins.",
x = NULL,
y = "TCP margin (percentage points)"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#333"),
plot.subtitle = element_text(size = 11, color = "#555"),
axis.text.y = element_text(size = 11)
) +
ylim(0, max(marginals_vic$margin) + 2)
```
### Interpretation & Insights
<div style="text-align:center; padding:30px;">
<h2 style="font-weight:800; color:#7B1FA2;">Interpretation & Insights</h2>
<h3 margin-top:0;">Understanding the story behind the numbers</h3>
<p style="font-size:17px; max-width:850px; margin:auto;">
The 2025 Australian Federal Election is often described as a <i>“Labor landslide.”</i>
Yet, a closer examination of the <b>Victorian results</b> reveals a more complex political landscape.
</p>
<br>
<div style="text-align:left; display:inline-block; font-size:16px; max-width:900px;">
<ul>
<li><b> Labor’s strong base:</b> Labor dominated Victoria’s first preferences, particularly in metropolitan areas such as Melbourne and Ballarat.</li>
<li><b> Growing diversity in votes:</b> Greens and Independents continued to strengthen their share, reflecting voter interest in local and environmental issues.</li>
<li><b> Marginal seats remain decisive:</b> Despite Labor’s overall success, many divisions were won by <b>less than 2 %</b>.
This underlines that even a “landslide” can rest on razor-thin margins in key battlegrounds.</li>
<li><b> Outlook for 2028:</b> These marginal divisions, especially those in outer metropolitan and regional areas will be critical in shaping future campaigns and party strategies.</li>
</ul>
</div>
<br>
<p style="font-size:16px; max-width:850px; margin:auto;">
<b>Take-home message:</b>
While aggregate results tell a story of dominance, local-level data shows that electoral competitiveness and voter diversity remain strong across Victoria. This demonstrates how <b>data storytelling</b> can move beyond headlines to uncover deeper truths in political behaviour.
</p>
</div>
### References
<div style="text-align:center; padding:30px;">
<h2 style="font-weight:800; color:#7B1FA2;">References</h2>
<div style="text-align:left; max-width:850px; font-size:16px; line-height:1.6;">
<p><b>Data Sources:</b></p>
<ul>
<li>Australian Electoral Commission (AEC). (2025). <em>House of Representatives – Two Candidate Preferred by Polling Place (2025 Federal Election)</em>.
Retrieved from <a href="https://results.aec.gov.au/31496/Website/HouseDownloadsMenu-31496-Csv.htm">results.aec.gov.au</a></li>
<li>Australian Electoral Commission (AEC). (2025). <em>House of Representatives – State First Preferences by Polling Place (Victoria)</em>.
Retrieved from <a href="https://results.aec.gov.au/31496/Website/HouseDownloadsMenu-31496-Csv.htm">results.aec.gov.au</a></li>
</ul>