Australian Electoral Commission. (2025). First preferences by candidate by polling place – VIC [Data set]. https://tallyroom.aec.gov.au/HouseDefault-31496.htm
---
title: "Election 2025 (VIC): A Dashboard of Preferences and Swings"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
theme: cosmo
source_code : embed
html_document:
df_print: paged
---
```{r, include=FALSE}
library(readr)
library(dplyr)
library(ggplot2)
library(janitor)
library(plotly)
library(flexdashboard)
# Load and clean data
vic_data <- read_csv("ElectionVIC.csv") |>
clean_names()
```
Overview
=======================================================================
Row
-----------------------------------------------------------------------
### Total Votes by Party
```{r, include=FALSE}
total_party_votes <- vic_data %>%
group_by(party_ab) %>%
summarise(total_votes = sum(ordinary_votes, na.rm = TRUE), .groups = "drop")
```
```{r}
# Plot interactively
p1 <- ggplot(total_party_votes, aes(x = reorder(party_ab, -total_votes), y = total_votes)) +
geom_bar(stat = "identity", fill = "steelblue") +
labs(title = "Total Votes by Party", x = "Party", y = "Votes") +
theme_minimal(base_size = 10)
ggplotly(p1)
```
### Average Swing by Party
```{r}
avg_swing <- vic_data %>%
group_by(party_ab) %>%
summarise(avg_swing = mean(swing, na.rm = TRUE))
ggplot(avg_swing, aes(x = reorder(party_ab, -avg_swing), y = avg_swing)) +
geom_bar(stat = "identity", fill = "darkred") +
labs(title = "Average Swing by Party", x = "Party", y = "Swing (%)") +
theme_minimal(base_size = 10)
```
Row
-----------------------------------------------------------------------
### Number of Candidates by Party
```{r}
candidate_counts <- vic_data |>
group_by(party_ab) |>
summarise(candidate_count = n_distinct(candidate_id))
ggplotly(
ggplot(candidate_counts, aes(x = reorder(party_ab, -candidate_count), y = candidate_count)) +
geom_bar(stat = "identity", fill = "orange") +
labs(title = "Number of Candidates per Party", x = "Party", y = "Count") +
theme_minimal(base_size = 10)
)
```
### Elected vs Not Elected Candidates
```{r}
elected_summary <- vic_data |>
count(elected) |>
mutate(label = ifelse(elected == "Y", "Elected", "Not Elected"))
plot_ly(
elected_summary,
labels = ~label,
values = ~n,
type = 'pie',
hole = 1.5,
textinfo = "label+percent"
) %>%
layout(
title = list(text = "Elected vs Not Elected", x = 0.5),
showlegend = TRUE,
margin = list(l = 10, r = 10, b = 10, t = 30, pad = 4)
)
```
Divisions
================================================================================
Row
-----------------------------------------------------------------------
### Total Votes by Division
```{r}
total_votes_division <- vic_data |>
group_by(division_nm) |>
summarise(total_votes = sum(ordinary_votes, na.rm = TRUE)) |>
arrange(desc(total_votes))
ggplotly(
ggplot(total_votes_division, aes(x = reorder(division_nm, total_votes), y = total_votes)) +
geom_bar(stat = "identity", fill = "lightblue") +
labs(title = "Total Votes by Division", x = "Division", y = "Votes") +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 9), # Rotate x labels
plot.title = element_text(size = 14, face = "bold"),
axis.title = element_text(size = 12)
)
)
```
### Top 5 Divisions by Votes
```{r}
top5_divisions <- head(total_votes_division, 5)$division_nm
votes_top5 <- vic_data |>
filter(division_nm %in% top5_divisions) |>
group_by(division_nm, party_ab) |>
summarise(votes = sum(ordinary_votes, na.rm = TRUE), .groups = "drop")
ggplotly(
ggplot(votes_top5, aes(x = division_nm, y = votes, fill = party_ab)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Votes by Party in Top 5 Divisions", x = "Division", y = "Votes") +
theme_minimal()
)
```
Row
-----------------------------------------------------------------------
### Party Vote Share in Top 5 Divisions (Stacked)
```{r}
ggplotly(
ggplot(votes_top5, aes(x = division_nm, y = votes, fill = party_ab)) +
geom_bar(stat = "identity") +
labs(title = "Stacked Party Vote Share - Top 5 Divisions") +
theme_minimal()
)
```
Insights
================================================================================
Row
-----------------------------------------------------------------------
### Top 10 Candidates by Ordinary Votes
```{r}
top_candidates <- vic_data |>
mutate(full_name = paste(given_nm, surname, "(", party_ab, ")")) |>
group_by(full_name) |>
summarise(votes = sum(ordinary_votes, na.rm = TRUE)) |>
arrange(desc(votes)) |>
head(10)
ggplotly(
ggplot(top_candidates, aes(x = votes, y = reorder(full_name, votes))) +
geom_bar(stat = "identity", fill = "purple") +
labs(title = "Top 10 Candidates by Votes", x = "Votes", y = "Candidate") +
theme_minimal()
)
```
Row
-----------------------------------------------------------------------
### Distribution of Votes by Party (Boxplot)
```{r}
ggplotly(
ggplot(vic_data, aes(x = party_ab, y = ordinary_votes)) +
geom_boxplot(fill = "lightgreen", outlier.colour = "red") +
labs(title = "Distribution of Ordinary Votes by Party", x = "Party", y = "Votes") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
)
```
Row
-----------------------------------------------------------------------
### Reference
Australian Electoral Commission. (2025). First preferences by candidate by polling place – VIC [Data set]. https://tallyroom.aec.gov.au/HouseDefault-31496.htm