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

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

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

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 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:

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