Dashboard

Aussie Rising Crisis in a Cost of Living Storm

Cover page

The Price of Growth

Intro Page

Essential spending categories impacting Australians (2015 vs 2025)


Inflation drivers by city and expenditure category (2015–2025)


Relationship between migration growth and CPI in major cities (2015–2025)


Migration origins to Australia (2015–2025)


Do cities with higher migrant inflows experience higher inflation in housing or transport?


Conclusion and Reference

At the end of the day, Aussie have every right to be mad. The Consumer Price Index keeps climbing accross all cities.

Yet, the data do show that larger migrant inflow cities did not necesserily have the largest price gap. Instead, outback city like Darwin faced a more drastic price rise.

Yes, Gen Z are moving back home. Yes, prices are crazy. But the issue is not migrants, it is a lack of government long term planning and policies.

References:

Australian Bureau of Statistics. (2025, September). TABLE 5. CPI: Groups, Index Numbers by Capital City. https://www.abs.gov.au/statistics/economy/price-indexes-and-inflation/consumer-price-index-australia/latest-release#data-downloads

Australian Bureau of Statistics. (2025,September). TABLES 1 and 2. CPI: All Groups, Index Numbers and Percentage Changes. https://www.abs.gov.au/statistics/economy/price-indexes-and-inflation/consumer-price-index-australia/latest-release#data-downloads

Australian Bureau of Statistics. (2024, December 19).Overseas migrant arrivals by country of birth, state/territory - financial years, 2004-05 to 2023-24. https://www.abs.gov.au/statistics/people/population/overseas-migration/latest-release#data-downloads

---
title: "Aussie rising crisis in a cost of living storm! Blame the Migrants or Blame the Market? "
output:
  flexdashboard::flex_dashboard:
    storyboard: true
    theme: flatly
    favicon: null
    social: menu
    source_code: embed
    css: null
    self_contained: true
---

```{r setup, include=FALSE}
library(flexdashboard)
library(dplyr)
library(tidyr)
library(readxl)
library(janitor)
library(lubridate)
library(xts)
library(zoo)
library(stringr)
library(dygraphs)
library(fmsb)
library(plotly)
library(readr)

#Import and clean datasets
##Chart 1 Prep - Cost of living trends across cities (2015–2025)
cpi_city_raw <- read_excel("Grouped CPI per city (Australia).xlsx") |> clean_names()
parse_month <- function(x){
  suppressWarnings({
    y <- parse_date_time(x, orders = c("b-Y","B Y","Y-m","Y/m","d-b-Y","d-B-Y"))
  })
  y_na <- is.na(y)
  if(any(y_na)) {
    ym <- suppressWarnings(as.yearmon(x[y_na]))
    y[y_na] <- as.Date(ym, frac = 1)
  }
  as.Date(floor_date(y, "month"))
}

cpi_city <- cpi_city_raw |>
  mutate(month = parse_month(!!sym(names(cpi_city_raw)[1]))) |>
  select(month, starts_with("all_groups_cpi")) |>
  arrange(month) |>
  filter(month >= as.Date("2015-01-01") & month <= as.Date("2025-12-31"))

city_cols <- names(cpi_city)[names(cpi_city) != "month"]

cpi_city_xts <- xts(cpi_city[-1], order.by = cpi_city$month)

colnames(cpi_city_xts) <- gsub("^all_groups_cpi_", "", colnames(cpi_city_xts)) |> 
  stringr::str_to_title()

##Chart 2 Prep - Essential spending categories impacting Australians (2015 vs 2025)
cpi_cat_raw <- read_excel("Consumer Price Index by Capital cities.xlsx") |> clean_names()

cpi_cat <- cpi_cat_raw |>
  mutate(month = parse_month(!!sym(names(cpi_cat_raw)[1]))) |>
  filter(month >= as.Date("2015-01-01")) |>
  select(month, everything())

cpi_long <- cpi_cat |>
  pivot_longer(cols = -month,
               names_to = c("category", "city"),
               names_sep = "_") |>
  mutate(category = str_replace_all(category, "all_groups_cpi", "all"))

cpi_long <- cpi_long |> filter(!str_detect(category, "all"))

cpi_2015 <- cpi_long |>
  filter(year(month) == 2015) |>
  group_by(category) |>
  summarise(mean_cpi = mean(value, na.rm = TRUE))

cpi_2025 <- cpi_long |>
  filter(year(month) == 2025) |>
  group_by(category) |>
  summarise(mean_cpi = mean(value, na.rm = TRUE))

categories <- intersect(cpi_2015$category, cpi_2025$category)
cpi_2015 <- cpi_2015 |> filter(category %in% categories)
cpi_2025 <- cpi_2025 |> filter(category %in% categories)

radar_df <- rbind(cpi_2015$mean_cpi, cpi_2025$mean_cpi)
colnames(radar_df) <- str_to_title(categories)

radar_df <- rbind(
  rep(max(radar_df), ncol(radar_df)),
  rep(min(radar_df), ncol(radar_df)),
  radar_df
)

##Chart 3 Prep - Inflation drivers by city and expenditure category (2015–2025)
cpi_long_q3 <- cpi_cat |>
  pivot_longer(
    cols = -month,
    names_to = "var",
    values_to = "value"
  ) |>
  mutate(
    city = str_extract(var, "(?<=_)[:alpha:]+$"),
    category = str_remove(var, "_[:alpha:]+$"),
    city = str_to_title(city),
    category = str_to_title(category)
  ) |>
  filter(!is.na(city), !is.na(category)) |>
  filter(city %in% c("Sydney", "Melbourne", "Brisbane", "Perth",
                     "Adelaide", "Hobart", "Darwin", "Canberra")) |>
  filter(!str_detect(category, "All"))  # exclude "All" categories

cpi_heat_q3 <- cpi_long_q3 |>
  group_by(city, category) |>
  summarise(mean_cpi = mean(value, na.rm = TRUE), .groups = "drop")

heat_matrix_q3 <- tidyr::pivot_wider(
  cpi_heat_q3,
  names_from = city,
  values_from = mean_cpi
)

row_labels_q3 <- heat_matrix_q3$category
heat_matrix_q3 <- as.matrix(heat_matrix_q3[ , -1])
rownames(heat_matrix_q3) <- row_labels_q3

##Chart 4 Prep - Relationship between migration growth and CPI in major cities (2015–2025)

migration_raw <- read_excel("Overseas migrant arrivals by country of birth in Australia.xlsx") |> clean_names()

cpi_yearly <- cpi_city |>
  mutate(year = year(month)) |>
  group_by(year) |>
  summarise(across(starts_with("all_groups_cpi"), mean, na.rm = TRUE)) |>
  pivot_longer(
    cols = starts_with("all_groups_cpi"),
    names_to = "city",
    values_to = "avg_cpi"
  ) |>
  mutate(city = str_remove(city, "all_groups_cpi_"),
         city = str_to_title(city))

year_cols <- names(migration_raw)[
  str_detect(names(migration_raw), "\\d{4}")
]
migration_long <- migration_raw |>
  pivot_longer(
    cols = all_of(year_cols),
    names_to = "year_raw",
    values_to = "arrivals"
  ) |>
  mutate(
    year = readr::parse_number(year_raw),
    arrivals = as.numeric(arrivals)
  ) |>
  filter(!is.na(year), !is.na(arrivals)) |>
  group_by(year) |>
  summarise(total_arrivals = sum(arrivals, na.rm = TRUE)) |>
  arrange(year) |>
  mutate(
    migration_growth = (total_arrivals - lag(total_arrivals)) / lag(total_arrivals) * 100
  )


migration_cpi <- cpi_yearly |>
  left_join(migration_long, by = "year") |>
  filter(year >= 2015, year <= 2025)


##Chart 5 Prep - Migration origins to Australia (2015–2025)
year_cols <- names(migration_raw)[
  str_detect(names(migration_raw), "\\d{4}")
]

migration_map_long <- migration_raw |>
  pivot_longer(
    cols = all_of(year_cols),
    names_to = "year_raw",
    values_to = "arrivals"
  ) |>
  mutate(
    country = country_of_birth_e,
    year = parse_number(year_raw),
    arrivals = as.numeric(arrivals)
  ) |>
  filter(!is.na(year), year >= 2015, year <= 2025) |>
  group_by(country, year) |>
  summarise(total_arrivals = sum(arrivals, na.rm = TRUE), .groups = "drop")

##Chart 6 Prep - Do cities with higher migrant inflows experience higher inflation in housing or transport?
cpi_mig_q6 <- cpi_cat |>
  pivot_longer(
    cols = -month,
    names_to = c("category", "city"),
    names_sep = "_"
  ) |>
  mutate(
    category = str_to_title(category),
    city = str_to_title(city),
    year = lubridate::year(month)
  ) |>
  filter(category %in% c("Housing", "Transport")) |>
  group_by(city, year, category) |>
  summarise(avg_cpi = mean(value, na.rm = TRUE), .groups = "drop")

migration_yearly_q6 <- migration_long |>
  group_by(year) |>
  summarise(total_arrivals = sum(total_arrivals, na.rm = TRUE))

migration_cpi_q6 <- cpi_mig_q6 |>
  left_join(migration_yearly_q6, by = "year") |>
  filter(city %in% c("Sydney", "Melbourne", "Darwin")) |>
  filter(year >= 2015, year <= 2025)

corr_city_cat <- migration_cpi_q6 |>
  group_by(city, category) |>
  summarise(
    correlation = cor(avg_cpi, total_arrivals, use = "complete.obs"),
    .groups = "drop"
  ) |>
  mutate(correlation_percent = round(correlation * 100, 1))

corr_city_cat
```




Dashboard


### Aussie Rising Crisis in a Cost of Living Storm
<div style="text-align:center; margin-top:0px;">
  <img src="Cover page.jpg" 
       alt="Cover page" 
       width="100%"
       height="100%"
       style="border-radius:0px; box-shadow:0 0 0px #aaa;">
</div>


### The Price of Growth
<div style="text-align:center; margin-top:0px;">
  <img src="IntroPage.jpg" 
       alt="Intro Page" 
       width="100%"
       height="100%"
       style="border-radius:0px; box-shadow:0 0 0px #aaa;">
</div>
### Cost of living trends across cities (2015–2025)

```{r}
dygraph(cpi_city_xts, main = "All Groups CPI by Capital City (2015–2025)") %>%
  dyLegend(show = "follow") %>%
  dyOptions(colors = RColorBrewer::brewer.pal(6, "Set2"),
            stackedGraph = FALSE,
            fillGraph = FALSE) %>%
  dyAxis("y", label = "CPI Index (All Groups)") %>%
  dyRangeSelector(dateWindow = c("2015-01-01", "2025-12-31"))
```

***
* The dygraph shows the trend for total CPI for each city from 2015 - 2025.

* All cities show an upward trajectory in CPI values.

* In 2015, Darwin had the lead with Hobart last. In 2025, Brisbane catches up at the top with Darwin last.

* The CPI gap among cities grows along with the years.


### Essential spending categories impacting Australians (2015 vs 2025)


```{r}

plot_ly(type = 'scatterpolar', fill = 'toself') %>%
  add_trace(r = cpi_2015$mean_cpi,
            theta = str_to_title(cpi_2015$category),
            name = "2015",
            line = list(color = '#4C72B0')) %>%
  add_trace(r = cpi_2025$mean_cpi,
            theta = str_to_title(cpi_2025$category),
            name = "2025",
            line = list(color = '#D55E00')) %>%
  layout(title = "Average CPI by Expenditure Category (2015 vs 2025)",
         polar = list(radialaxis = list(visible = TRUE)))
```
***
* The spider graph shows average CPI by spending category between 2015 vs 2025.

* While prices grew for almost all expenditure, average price for communication has dropped and clothing has stayed steady.

* The biggest rise can be seen for Alcohol followed by Education, Health and Housing.

### Inflation drivers by city and expenditure category (2015–2025)

```{r}
plot_ly(
  x = colnames(heat_matrix_q3),
  y = rownames(heat_matrix_q3),
  z = heat_matrix_q3,
  type = "heatmap",
  colors = colorRamp(c("lightblue", "darkred")),
  hovertemplate = paste(
    "<b>City:</b> %{x}<br>",
    "<b>Category:</b> %{y}<br>",
    "<b>Avg CPI:</b> %{z:.1f}<extra></extra>"
  )
) %>%
  layout(
    title = "Average CPI by Expenditure Category and City (2015–2025)",
    xaxis = list(title = "Capital City"),
    yaxis = list(title = "Spending Category")
  )
```
***
* The heatmap shows high CPI through the red shading and low CPI by the bleu shading.

* Expenditure wise, Alcohol, Education and Health prices have soared.

* If we look at the cities, we can see that Adelaide, Brisbane and Melbourne is victime to lots of red shading


### Relationship between migration growth and CPI in major cities (2015–2025)
```{r}
plot_ly(
  data = migration_cpi,
  x = ~migration_growth,
  y = ~avg_cpi,
  size = ~total_arrivals,
  color = ~city,
  type = 'scatter',
  mode = 'markers',
  marker = list(sizemode = 'diameter', opacity = 0.7),
  text = ~paste(
    "City:", city,
    "<br>Year:", year,
    "<br>CPI:", round(avg_cpi, 1),
    "<br>Migration Growth (%):", round(migration_growth, 1),
    "<br>Total Arrivals:", format(total_arrivals, big.mark = ",")
  )
) %>%
  layout(
    title = "Migration Growth vs CPI by City (2015–2025)",
    xaxis = list(title = "Migration Growth (%)"),
    yaxis = list(title = "Average CPI (All Groups)"),
    showlegend = TRUE
  )


```
***
* The bubble graph shows cities with migration flows against the average CPI.

* We can see a concentration at the end of the y axis. In the years 2015-2019, Australian cities started to feel a rise in their CPI.

* In the year 2022, as immigration rate increase, the country faces lower CPI.

* Constrastingly, in 2023, as immigration rate goes into negative, CPI value escalate.

### Migration origins to Australia (2015–2025)
```{r}
plot_ly(
  data = migration_map_long,
  type = 'choropleth',
  locations = ~country,              
  locationmode = 'country names',
  z = ~total_arrivals,
  frame = ~year,                     
  colorscale = 'Reds',
  text = ~paste(
    "<b>Country:</b>", country,
    "<br><b>Year:</b>", year,
    "<br><b>Total Arrivals:</b>", format(total_arrivals, big.mark = ",")
  ),
  hoverinfo = "text",
  colorbar = list(title = "Total Migrant Arrivals")
) %>%
  layout(
    title = "Migrant Origins by Country of Birth (2015–2025)",
    geo = list(
      projection = list(type = 'robinson'),
      showframe = FALSE,
      showcoastlines = TRUE,
      coastlinecolor = "gray70",
      landcolor = "gray95"
    )
  ) %>%
  animation_opts(
    frame = 1200,
    transition = 500,
    easing = "linear"
  ) %>%
  animation_slider(currentvalue = list(prefix = "Year: "))

```
***
* This interactive world map shows the concentration of the origins of migrants in Australia.

* From 2015 to 2025, there is a consistent trend where the majority of immigrants come from China and India.

* The exception occurs during the Covid era (2020), where the highest migrants are Australians. This could be explained by citizens risiding in other countries and of Australian descendent, coming back during the pandemic.

### Do cities with higher migrant inflows experience higher inflation in housing or transport?

```{r}
make_gauge <- function(value, title, color_low, color_mid, color_high, xpos, ypos) {
  plot_ly(
    domain = list(x = xpos, y = ypos),
    value = value,
    title = list(text = title, font = list(size = 11)),
    type = "indicator",
    mode = "gauge+number",
    number = list(font = list(size = 24)),   
    gauge = list(
      axis = list(range = list(0, 100), tickfont = list(size = 10)),
      bar = list(color = color_high),
      steps = list(
        list(range = c(0, 40), color = color_low),
        list(range = c(40, 70), color = color_mid),
        list(range = c(70, 100), color = color_high)
      )
    )
  )
}

domains <- list(
  list(x = c(0, 0.45), y = c(0.74, 0.93)), 
  list(x = c(0.55, 1), y = c(0.74, 0.93)),  
  list(x = c(0, 0.45), y = c(0.42, 0.61)),  
  list(x = c(0.55, 1), y = c(0.42, 0.61)),  
  list(x = c(0, 0.45), y = c(0.10, 0.29)),  
  list(x = c(0.55, 1), y = c(0.10, 0.29))   
)

gauges <- list(
  make_gauge(corr_city_cat$correlation_percent[corr_city_cat$city == "Sydney" & corr_city_cat$category == "Housing"],
             "Sydney – Housing", "lightgray", "orange", "darkred", domains[[1]]$x, domains[[1]]$y),
  make_gauge(corr_city_cat$correlation_percent[corr_city_cat$city == "Sydney" & corr_city_cat$category == "Transport"],
             "Sydney – Transport", "lightgray", "lightblue", "steelblue", domains[[2]]$x, domains[[2]]$y),
  make_gauge(corr_city_cat$correlation_percent[corr_city_cat$city == "Melbourne" & corr_city_cat$category == "Housing"],
             "Melbourne – Housing", "lightgray", "orange", "darkred", domains[[3]]$x, domains[[3]]$y),
  make_gauge(corr_city_cat$correlation_percent[corr_city_cat$city == "Melbourne" & corr_city_cat$category == "Transport"],
             "Melbourne – Transport", "lightgray", "lightblue", "steelblue", domains[[4]]$x, domains[[4]]$y),
  make_gauge(corr_city_cat$correlation_percent[corr_city_cat$city == "Darwin" & corr_city_cat$category == "Housing"],
             "Darwin – Housing", "lightgray", "orange", "darkred", domains[[5]]$x, domains[[5]]$y),
  make_gauge(corr_city_cat$correlation_percent[corr_city_cat$city == "Darwin" & corr_city_cat$category == "Transport"],
             "Darwin – Transport", "lightgray", "lightblue", "steelblue", domains[[6]]$x, domains[[6]]$y)
)
fig <- subplot(gauges, nrows = 1, shareX = FALSE, shareY = FALSE) %>%
  layout(
    title = "Correlation between Migration Inflows and CPI (2015–2025)",
    margin = list(t = 80)
  )

fig
```
***
* These gauge indicators show us how the 2 expenditures (Housing and Transport) relates to cities with the highest (Sydney and Melbourne) and lower migrant (Darwin) inflow.


* Darwin shows the highest correlation among the 3 cities. With housing at 82.3% and Transport at 60.3%, this shows that when migration increase in Darwin so does the price of housing and transport.

* Shokingly, Cities with higher migration flows such as Sydney and Melbourne shows a less larger price impact on housing and transport costs

### Conclusion and Reference
<div style="text-align:left; margin-top: 50px;">
  <h2>
  At the end of the day, Aussie have every right to be mad. The Consumer Price Index keeps climbing accross all cities. 

Yet, the data do show that larger migrant inflow cities did not necesserily have the largest price gap. Instead, outback city like Darwin faced a more drastic price rise.

Yes, Gen Z are moving back home. Yes, prices are crazy. But the issue is not migrants, it is a lack of government long term planning and policies.
  </h2>
</div>



<div style="text-align:left; margin-top: 30px;">
  <h4>
  References:

Australian Bureau of Statistics. (2025, September). TABLE 5. CPI: Groups, Index Numbers by Capital City. https://www.abs.gov.au/statistics/economy/price-indexes-and-inflation/consumer-price-index-australia/latest-release#data-downloads

Australian Bureau of Statistics. (2025,September). TABLES 1 and 2. CPI: All Groups, Index Numbers and Percentage Changes. https://www.abs.gov.au/statistics/economy/price-indexes-and-inflation/consumer-price-index-australia/latest-release#data-downloads

Australian Bureau of Statistics. (2024, December 19).Overseas migrant arrivals by country of birth, state/territory - financial years, 2004-05 to 2023-24. https://www.abs.gov.au/statistics/people/population/overseas-migration/latest-release#data-downloads

  </h4>
</div>