Introduction

Understanding Australia’s Inflation

The cost of everyday items can change quite a lot over time. Groceries, electricity, rent and transport do not always become more expensive at the same rate. Looking at Australia’s Consumer Price Index (CPI) data helps us understand these differences and see how prices have changed.

In this report, I explore Australian CPI data using five interactive visualisations. Each chart looks at the data from a different angle, including increases and decreases in prices, changes over time, and differences between CPI measures. Together, the charts help identify the main patterns in the data and give a clearer picture of how inflation varies across different areas.

1. Setup

# Install missing packages in the RStudio Console if needed:
# install.packages(c("readr", "dplyr", "ggplot2", "plotly", "knitr"))

library(readr)
library(dplyr)
library(ggplot2)
library(plotly)
library(knitr)

knitr::opts_chunk$set(
  echo = TRUE,
  message = FALSE,
  warning = FALSE,
  fig.width = 10,
  fig.height = 6
)

2. Import the data

Save australia_cpi_data.csv in the same folder as this R Markdown file before knitting.

# Load the package
library(readr)

# Import the Australian CPI dataset
cpi_raw <- read_csv(
  "/Users/prajwalmalipatil8/Downloads/Price Index/australia_cpi_data.csv",
  show_col_types = FALSE
)

# Check the dimensions of the imported data
data.frame(
  rows = nrow(cpi_raw),
  columns = ncol(cpi_raw)
)

3. Inspect the dataset

# Show the column names
names(cpi_raw)
##  [1] "STRUCTURE"             "STRUCTURE_ID"          "STRUCTURE_NAME"       
##  [4] "ACTION"                "MEASURE"               "Measure"              
##  [7] "INDEX"                 "Index"                 "TSEST"                
## [10] "Adjustment Type"       "REGION"                "Region"               
## [13] "FREQ"                  "Frequency"             "TIME_PERIOD"          
## [16] "Time Period"           "OBS_VALUE"             "Observation Value"    
## [19] "UNIT_MEASURE"          "Unit of Measure"       "OBS_STATUS"           
## [22] "Observation Status"    "DECIMALS"              "Decimals"             
## [25] "OBS_COMMENT"           "Observation Comment"   "BASE_PERIOD"          
## [28] "Reference Base Period"
# Preview the first few records
head(cpi_raw, 10)
# Check the structure and data types
str(cpi_raw)
## spc_tbl_ [644 × 28] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ STRUCTURE            : chr [1:644] "DATAFLOW" "DATAFLOW" "DATAFLOW" "DATAFLOW" ...
##  $ STRUCTURE_ID         : chr [1:644] "ABS:CPI(2.0.0)" "ABS:CPI(2.0.0)" "ABS:CPI(2.0.0)" "ABS:CPI(2.0.0)" ...
##  $ STRUCTURE_NAME       : chr [1:644] "Consumer Price Index (CPI)" "Consumer Price Index (CPI)" "Consumer Price Index (CPI)" "Consumer Price Index (CPI)" ...
##  $ ACTION               : chr [1:644] "I" "I" "I" "I" ...
##  $ MEASURE              : num [1:644] 3 3 3 3 3 3 3 3 3 3 ...
##  $ Measure              : chr [1:644] "Percentage change from previous year" "Percentage change from previous year" "Percentage change from previous year" "Percentage change from previous year" ...
##  $ INDEX                : num [1:644] 40077 40077 40077 40077 131187 ...
##  $ Index                : chr [1:644] "Postal services" "Postal services" "Postal services" "Postal services" ...
##  $ TSEST                : num [1:644] 10 10 10 10 10 10 10 10 10 10 ...
##  $ Adjustment Type      : chr [1:644] "Original" "Original" "Original" "Original" ...
##  $ REGION               : num [1:644] 50 50 50 50 50 50 50 50 50 50 ...
##  $ Region               : chr [1:644] "Australia" "Australia" "Australia" "Australia" ...
##  $ FREQ                 : chr [1:644] "M" "M" "M" "M" ...
##  $ Frequency            : chr [1:644] "Monthly" "Monthly" "Monthly" "Monthly" ...
##  $ TIME_PERIOD          : chr [1:644] "2026-05" "2026-06" "2026-07" "2026-08" ...
##  $ Time Period          : logi [1:644] NA NA NA NA NA NA ...
##  $ OBS_VALUE            : num [1:644] 11.6 8.7 6.8 7.5 4.7 4.9 4.7 4.6 -1.9 -1.8 ...
##  $ Observation Value    : logi [1:644] NA NA NA NA NA NA ...
##  $ UNIT_MEASURE         : chr [1:644] "PCT" "PCT" "PCT" "PCT" ...
##  $ Unit of Measure      : chr [1:644] "Percent" "Percent" "Percent" "Percent" ...
##  $ OBS_STATUS           : chr [1:644] NA NA NA NA ...
##  $ Observation Status   : chr [1:644] NA NA NA NA ...
##  $ DECIMALS             : logi [1:644] NA NA NA NA NA NA ...
##  $ Decimals             : logi [1:644] NA NA NA NA NA NA ...
##  $ OBS_COMMENT          : logi [1:644] NA NA NA NA NA NA ...
##  $ Observation Comment  : logi [1:644] NA NA NA NA NA NA ...
##  $ BASE_PERIOD          : num [1:644] 25 25 25 25 25 25 25 25 25 25 ...
##  $ Reference Base Period: chr [1:644] "Sep 2025 = 100.0" "Sep 2025 = 100.0" "Sep 2025 = 100.0" "Sep 2025 = 100.0" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   STRUCTURE = col_character(),
##   ..   STRUCTURE_ID = col_character(),
##   ..   STRUCTURE_NAME = col_character(),
##   ..   ACTION = col_character(),
##   ..   MEASURE = col_double(),
##   ..   Measure = col_character(),
##   ..   INDEX = col_double(),
##   ..   Index = col_character(),
##   ..   TSEST = col_double(),
##   ..   `Adjustment Type` = col_character(),
##   ..   REGION = col_double(),
##   ..   Region = col_character(),
##   ..   FREQ = col_character(),
##   ..   Frequency = col_character(),
##   ..   TIME_PERIOD = col_character(),
##   ..   `Time Period` = col_logical(),
##   ..   OBS_VALUE = col_double(),
##   ..   `Observation Value` = col_logical(),
##   ..   UNIT_MEASURE = col_character(),
##   ..   `Unit of Measure` = col_character(),
##   ..   OBS_STATUS = col_character(),
##   ..   `Observation Status` = col_character(),
##   ..   DECIMALS = col_logical(),
##   ..   Decimals = col_logical(),
##   ..   OBS_COMMENT = col_logical(),
##   ..   `Observation Comment` = col_logical(),
##   ..   BASE_PERIOD = col_double(),
##   ..   `Reference Base Period` = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>

3.1 Measures, regions, frequency and periods

# What measure, region and frequency are represented?
cpi_raw %>%
  count(Measure, Region, Frequency, sort = TRUE) %>%
  kable(caption = "Measures, regions and reporting frequency")
Measures, regions and reporting frequency
Measure Region Frequency n
Percentage change from previous year Australia Monthly 644
# Count records by reporting month
cpi_raw %>%
  count(TIME_PERIOD, name = "records") %>%
  arrange(TIME_PERIOD) %>%
  kable(caption = "Records available for each reporting month")
Records available for each reporting month
TIME_PERIOD records
2026-05 161
2026-06 161
2026-07 161
2026-08 161

4. Prepare the data

The file contains machine-readable fields (for example, TIME_PERIOD and OBS_VALUE) alongside descriptive versions of some fields. The analysis below uses the machine-readable fields and retains the series identifier, adjustment type and observation status for validation.

cpi_clean <- cpi_raw %>%
  transmute(
    series_id = INDEX,
    category = Index,
    measure = Measure,
    region = Region,
    frequency = Frequency,
    time_period = TIME_PERIOD,
    date = as.Date(paste0(TIME_PERIOD, "-01")),
    value = OBS_VALUE,
    unit = `Unit of Measure`,
    adjustment_type = `Adjustment Type`,
    observation_status = OBS_STATUS,
    reference_base = `Reference Base Period`
  ) %>%
  arrange(series_id, date)

# Preview the analysis-ready data
head(cpi_clean, 10)

4.1 Data quality checks

# Summary of the cleaned data
data_summary <- cpi_clean %>%
  summarise(
    rows = n(),
    unique_series = n_distinct(series_id),
    unique_category_labels = n_distinct(category),
    missing_values = sum(is.na(value)),
    first_period = min(date, na.rm = TRUE),
    last_period = max(date, na.rm = TRUE)
  )

kable(data_summary, caption = "Summary of the cleaned dataset")
Summary of the cleaned dataset
rows unique_series unique_category_labels missing_values first_period last_period
644 161 153 0 2026-05-01 2026-08-01
# Count missing values in each analysis column
missing_summary <- data.frame(
  column = names(cpi_clean),
  missing_count = vapply(cpi_clean, function(x) sum(is.na(x)), integer(1))
)

kable(missing_summary, caption = "Missing values by column")
Missing values by column
column missing_count
series_id series_id 0
category category 0
measure measure 0
region region 0
frequency frequency 0
time_period time_period 0
date date 0
value value 0
unit unit 0
adjustment_type adjustment_type 0
observation_status observation_status 643
reference_base reference_base 0
# Check whether any series has more than one record for the same month
duplicate_check <- cpi_clean %>%
  count(series_id, date, name = "records") %>%
  filter(records > 1)

if (nrow(duplicate_check) == 0) {
  cat("No duplicate series-and-month combinations were found.")
} else {
  kable(duplicate_check, caption = "Duplicate series-and-month combinations")
}
## No duplicate series-and-month combinations were found.

Different series can share the same category label. For that reason, the analysis keeps series_id as well as the category name instead of combining records by category label alone.

4.2 Check adjustment types

cpi_clean %>%
  count(adjustment_type, sort = TRUE) %>%
  kable(caption = "Records by adjustment type")
Records by adjustment type
adjustment_type n
Original 632
Seasonally Adjusted 12

For the charts below, we use the original series to keep the comparison consistent. This filter is explicit so it can be changed if a different adjustment type is required.

cpi_original <- cpi_clean %>%
  filter(adjustment_type == "Original", !is.na(value), !is.na(date))

# Check the number of records after filtering
data.frame(
  rows = nrow(cpi_original),
  series = n_distinct(cpi_original$series_id),
  first_period = min(cpi_original$date, na.rm = TRUE),
  last_period = max(cpi_original$date, na.rm = TRUE)
)

5. Exploratory visualisations

The values in this file represent percentage change from the previous year, not CPI index numbers. Positive values indicate an increase compared with the same period a year earlier; negative values indicate a decrease.

Visualisation 1 — Positive and negative CPI changes

latest_date <- max(cpi_original$date, na.rm = TRUE)

latest_changes <- cpi_original %>%
  filter(date == latest_date) %>%
  group_by(series_id, category) %>%
  summarise(value = mean(value, na.rm = TRUE), .groups = "drop") %>%
  filter(is.finite(value)) %>%
  arrange(desc(abs(value))) %>%
  slice_head(n = 20) %>%
  mutate(
    direction = ifelse(value >= 0, "Increase", "Decrease"),
    series_label = paste0(category, " (", series_id, ")"),
    series_label = reorder(series_label, value)
  )

p_diverging <- ggplot(
  latest_changes,
  aes(
    x = value,
    y = series_label,
    fill = direction,
    text = paste0(
      "Category: ", category,
      "<br>Series ID: ", series_id,
      "<br>Annual change: ", round(value, 2), "%"
    )
  )
) +
  geom_col(width = 0.7) +
  geom_vline(xintercept = 0, colour = "black", linewidth = 0.5) +
  scale_fill_manual(
    values = c("Increase" = "#2878B5", "Decrease" = "#D95F59")
  ) +
  labs(
    title = "Which CPI categories increased or decreased the most?",
    subtitle = paste("Latest available period:", format(latest_date, "%B %Y")),
    x = "Percentage change from previous year (%)",
    y = NULL,
    fill = "Direction"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold"),
    legend.position = "bottom"
  )

ggplotly(p_diverging, tooltip = "text") %>%
  layout(margin = list(l = 180, r = 30, t = 100, b = 70))

Visualisation 3 — CPI heatmap

heatmap_series <- cpi_original %>%
  group_by(series_id, category) %>%
  summarise(
    variation = if (sum(!is.na(value)) >= 2) {
      max(value, na.rm = TRUE) - min(value, na.rm = TRUE)
    } else {
      NA_real_
    },
    .groups = "drop"
  ) %>%
  filter(is.finite(variation)) %>%
  arrange(desc(variation)) %>%
  slice_head(n = 15)

heatmap_data <- cpi_original %>%
  semi_join(heatmap_series, by = c("series_id", "category")) %>%
  mutate(
    series_label = paste0(category, " (", series_id, ")"),
    month_label = format(date, "%b %Y")
  ) %>%
  group_by(series_id, category, series_label, date, month_label) %>%
  summarise(value = mean(value, na.rm = TRUE), .groups = "drop")

heatmap_data$series_label <- factor(
  heatmap_data$series_label,
  levels = rev(unique(heatmap_data$series_label))
)

p_heatmap <- ggplot(
  heatmap_data,
  aes(
    x = date,
    y = series_label,
    fill = value,
    text = paste0(
      "Category: ", category,
      "<br>Month: ", month_label,
      "<br>Annual change: ", round(value, 2), "%"
    )
  )
) +
  geom_tile(colour = "white", linewidth = 0.2) +
  scale_x_date(date_labels = "%b %Y", date_breaks = "1 month") +
  scale_fill_gradient2(
    low = "#D95F59",
    mid = "#F7F7F7",
    high = "#2878B5",
    midpoint = 0,
    name = "Annual change (%)"
  ) +
  labs(
    title = "How do annual CPI changes differ across categories and months?",
    x = "Month",
    y = "CPI category"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold"),
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

ggplotly(p_heatmap, tooltip = "text") %>%
  layout(margin = list(l = 200, r = 60, t = 100, b = 80))

Visualisation 4 —Dumbbell chart: compare two months

first_date <- min(cpi_original$date, na.rm = TRUE)
last_date <- max(cpi_original$date, na.rm = TRUE)

comparison_data <- cpi_original %>%
  filter(date %in% c(first_date, last_date)) %>%
  group_by(series_id, category, date) %>%
  summarise(value = mean(value, na.rm = TRUE), .groups = "drop") %>%
  mutate(
    period = ifelse(date == first_date, "First month", "Latest month")
  ) %>%
  select(series_id, category, period, value) %>%
  tidyr::pivot_wider(names_from = period, values_from = value) %>%
  filter(
    is.finite(`First month`),
    is.finite(`Latest month`)
  ) %>%
  mutate(
    difference = `Latest month` - `First month`,
    series_label = paste0(category, " (", series_id, ")")
  ) %>%
  arrange(desc(abs(difference))) %>%
  slice_head(n = 15) %>%
  mutate(
    series_label = reorder(series_label, difference)
  )

dumbbell_data <- comparison_data %>%
  tidyr::pivot_longer(
    cols = c(`First month`, `Latest month`),
    names_to = "period",
    values_to = "value"
  )

p_dumbbell <- ggplot(
  comparison_data,
  aes(
    y = series_label,
    x = `First month`,
    xend = `Latest month`,
    text = paste0(
      "Category: ", category,
      "<br>First month: ", round(`First month`, 2), "%",
      "<br>Latest month: ", round(`Latest month`, 2), "%",
      "<br>Difference: ", round(difference, 2), " percentage points"
    )
  )
) +
  geom_segment(
    aes(
      yend = series_label,
      x = `First month`,
      xend = `Latest month`
    ),
    colour = "grey65",
    linewidth = 1
  ) +
  geom_point(
    aes(x = `First month`),
    colour = "#D95F59",
    size = 2.8
  ) +
  geom_point(
    aes(x = `Latest month`),
    colour = "#2878B5",
    size = 2.8
  ) +
  labs(
    title = "How did annual CPI changes differ between the first and latest months?",
    subtitle = paste(
      format(first_date, "%B %Y"),
      "compared with",
      format(last_date, "%B %Y")
    ),
    x = "Percentage change from previous year (%)",
    y = NULL
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold")
  )

ggplotly(p_dumbbell, tooltip = "text") %>%
  layout(margin = list(l = 190, r = 30, t = 100, b = 70))

Visualisation 5 — Compare selected CPI categories

latest_date <- max(cpi_original$date, na.rm = TRUE)

# Check the actual category names in your dataset.
available_categories <- sort(unique(cpi_original$category))
print(available_categories)
##   [1] "Accessories"                                                           
##   [2] "Accessories and clothing services"                                     
##   [3] "Alcohol and tobacco"                                                   
##   [4] "Alcoholic beverages"                                                   
##   [5] "All groups CPI"                                                        
##   [6] "All groups CPI excluding 'volatile items'"                             
##   [7] "All groups CPI excluding 'volatile items' and holiday travel"          
##   [8] "All groups CPI excluding Alcohol and tobacco"                          
##   [9] "All groups CPI excluding Clothing and footwear"                        
##  [10] "All groups CPI excluding Communication"                                
##  [11] "All groups CPI excluding Education"                                    
##  [12] "All groups CPI excluding food and energy"                              
##  [13] "All groups CPI excluding Food and non-alcoholic beverages"             
##  [14] "All groups CPI excluding Furnishings, household equipment and services"
##  [15] "All groups CPI excluding Health"                                       
##  [16] "All groups CPI excluding Housing"                                      
##  [17] "All groups CPI excluding Housing and Insurance and financial services" 
##  [18] "All groups CPI excluding Insurance and financial services"             
##  [19] "All groups CPI excluding Medical and hospital services"                
##  [20] "All groups CPI excluding Recreation and culture"                       
##  [21] "All groups CPI excluding Transport"                                    
##  [22] "All groups, goods component"                                           
##  [23] "All groups, services component"                                        
##  [24] "Audio, visual and computing equipment"                                 
##  [25] "Audio, visual and computing equipment and services"                    
##  [26] "Audio, visual and computing media and services"                        
##  [27] "Automotive fuel"                                                       
##  [28] "Beef and veal"                                                         
##  [29] "Beer"                                                                  
##  [30] "Books"                                                                 
##  [31] "Bread"                                                                 
##  [32] "Bread and cereal products"                                             
##  [33] "Breakfast cereals"                                                     
##  [34] "Cakes and biscuits"                                                    
##  [35] "Carpets and other floor coverings"                                     
##  [36] "Cheese"                                                                
##  [37] "Child care"                                                            
##  [38] "Cleaning and maintenance products"                                     
##  [39] "Cleaning, repair and hire of clothing and footwear"                    
##  [40] "Clothing and footwear"                                                 
##  [41] "Coffee, tea and cocoa"                                                 
##  [42] "Communication"                                                         
##  [43] "Dairy and related products"                                            
##  [44] "Dental services"                                                       
##  [45] "Deposit and loan facilities (direct charges)"                          
##  [46] "Discretionary"                                                         
##  [47] "Discretionary excluding tobacco"                                       
##  [48] "Domestic and household services"                                       
##  [49] "Domestic holiday travel and accommodation"                             
##  [50] "Education"                                                             
##  [51] "Eggs"                                                                  
##  [52] "Electricity"                                                           
##  [53] "Equipment for sports, camping and open-air recreation"                 
##  [54] "Financial services"                                                    
##  [55] "Fish and other seafood"                                                
##  [56] "Food additives and condiments"                                         
##  [57] "Food and non-alcoholic beverages"                                      
##  [58] "Food products n.e.c."                                                  
##  [59] "Footwear"                                                              
##  [60] "Footwear for infants and children"                                     
##  [61] "Footwear for men"                                                      
##  [62] "Footwear for women"                                                    
##  [63] "Fruit"                                                                 
##  [64] "Fruit and vegetables"                                                  
##  [65] "Furnishings, household equipment and services"                         
##  [66] "Furniture"                                                             
##  [67] "Furniture and furnishings"                                             
##  [68] "Games, toys and hobbies"                                               
##  [69] "Garments"                                                              
##  [70] "Garments for infants and children"                                     
##  [71] "Garments for men"                                                      
##  [72] "Garments for women"                                                    
##  [73] "Gas and other household fuels"                                         
##  [74] "Glassware, tableware and household utensils"                           
##  [75] "Hairdressing and personal grooming services"                           
##  [76] "Health"                                                                
##  [77] "Holiday travel and accommodation"                                      
##  [78] "Household appliances, utensils and tools"                              
##  [79] "Household textiles"                                                    
##  [80] "Housing"                                                               
##  [81] "Ice cream and other dairy products"                                    
##  [82] "Insurance"                                                             
##  [83] "Insurance and financial services"                                      
##  [84] "International holiday travel and accommodation"                        
##  [85] "Jams, honey and spreads"                                               
##  [86] "Lamb and goat"                                                         
##  [87] "Maintenance and repair of motor vehicles"                              
##  [88] "Maintenance and repair of the dwelling"                                
##  [89] "Major household appliances"                                            
##  [90] "Market goods and services excluding 'volatile items' - Goods"          
##  [91] "Market goods and services excluding 'volatile items' - Services"       
##  [92] "Market goods and services excluding 'volatile items' - Total"          
##  [93] "Meals out and take away foods"                                         
##  [94] "Meat and seafoods"                                                     
##  [95] "Medical and hospital services"                                         
##  [96] "Medical products, appliances and equipment"                            
##  [97] "Medical, dental and hospital services"                                 
##  [98] "Milk"                                                                  
##  [99] "Motor vehicles"                                                        
## [100] "New dwelling purchase by owner-occupiers"                              
## [101] "Newspapers, books and stationery"                                      
## [102] "Newspapers, magazines and stationery"                                  
## [103] "Non-alcoholic beverages"                                               
## [104] "Non-Discretionary"                                                     
## [105] "Non-durable household products"                                        
## [106] "Non-tradables"                                                         
## [107] "Oils and fats"                                                         
## [108] "Other cereal products"                                                 
## [109] "Other financial services"                                              
## [110] "Other food products n.e.c."                                            
## [111] "Other household services"                                              
## [112] "Other housing"                                                         
## [113] "Other meats"                                                           
## [114] "Other non-durable household products"                                  
## [115] "Other recreation, sport and culture"                                   
## [116] "Other recreational, sporting and cultural services"                    
## [117] "Other services in respect of motor vehicles"                           
## [118] "Personal care products"                                                
## [119] "Pets and related products"                                             
## [120] "Pharmaceutical products"                                               
## [121] "Pork"                                                                  
## [122] "Postal services"                                                       
## [123] "Poultry"                                                               
## [124] "Preschool and primary education"                                       
## [125] "Private motoring"                                                      
## [126] "Property rates and charges"                                            
## [127] "Recreation and culture"                                                
## [128] "Rents"                                                                 
## [129] "Restaurant meals"                                                      
## [130] "Secondary education"                                                   
## [131] "Small electric household appliances"                                   
## [132] "Snacks and confectionery"                                              
## [133] "Spare parts and accessories for motor vehicles"                        
## [134] "Spirits"                                                               
## [135] "Sports participation"                                                  
## [136] "Take away and fast foods"                                              
## [137] "Telecommunication equipment and services"                              
## [138] "Tertiary education"                                                    
## [139] "Therapeutic appliances and equipment"                                  
## [140] "Tobacco"                                                               
## [141] "Tools and equipment for house and garden"                              
## [142] "Tradables"                                                             
## [143] "Transport"                                                             
## [144] "Urban transport fares"                                                 
## [145] "Utilities"                                                             
## [146] "Vegetables"                                                            
## [147] "Veterinary and other services for pets"                                
## [148] "Water and sewerage"                                                    
## [149] "Waters, soft drinks and juices"                                        
## [150] "Wine"
# Match these phrases against the category labels in the CSV.
category_patterns <- c(
  "All groups",
  "Food and non-alcoholic beverages",
  "Housing",
  "Electricity",
  "Automotive fuel",
  "Rents"
)

selected_categories <- cpi_original %>%
  filter(date == latest_date) %>%
  filter(
    vapply(
      category,
      function(x) {
        any(grepl(category_patterns, x, ignore.case = TRUE))
      },
      logical(1)
    )
  ) %>%
  group_by(series_id, category) %>%
  summarise(value = mean(value, na.rm = TRUE), .groups = "drop") %>%
  filter(is.finite(value)) %>%
  mutate(
    series_label = paste0(category, " (", series_id, ")"),
    series_label = reorder(series_label, value)
  )

if (nrow(selected_categories) == 0) {
  stop(
    "No selected category names matched. Check available_categories above ",
    "and update category_patterns to match your CSV."
  )
}

p_categories <- ggplot(
  selected_categories,
  aes(
    x = value,
    y = series_label,
    text = paste0(
      "Category: ", category,
      "<br>Series ID: ", series_id,
      "<br>Annual change: ", round(value, 2), "%"
    )
  )
) +
  geom_col(fill = "#2878B5", width = 0.7) +
  geom_vline(xintercept = 0, colour = "black", linewidth = 0.4) +
  labs(
    title = "How do selected CPI categories compare?",
    subtitle = paste("Latest available period:", format(latest_date, "%B %Y")),
    x = "Percentage change from previous year (%)",
    y = NULL
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold")
  )

ggplotly(p_categories, tooltip = "text") %>%
  layout(margin = list(l = 200, r = 30, t = 100, b = 70))

visualisations

Visualisation 1: Comparing increases and decreases

This chart compares the CPI measures with the largest positive and negative annual changes. It helps show that price movements are not the same across every measure. The largest increase was in [insert measure and value], while [insert measure and value] recorded the largest decrease. This comparison makes it easier to see which areas experienced the biggest changes.

Visualisation 2: Changes over time

This line chart shows how annual percentage changes moved across selected CPI measures during the available period. Some lines may follow similar patterns, while others move differently. This helps identify whether the selected measures experienced similar changes or followed different trends.

Visualisation 3: Patterns across categories and months

The heatmap provides a quick way to compare CPI changes across several measures and months. Different colours represent different percentage changes, making patterns easier to spot. It also helps highlight measures whose annual changes varied more during the period.

Visualisation 4: Comparing two periods

This chart compares the annual percentage changes in the first and latest months available in the dataset. The distance between the two points shows how much each measure changed between those periods. Measures with larger differences experienced greater movement, while measures with smaller differences remained relatively stable.

Visualisation 5: Comparing selected CPI measures

This chart compares selected CPI measures for the latest available month. It helps show how the reported annual changes differ between the measures included. Because some measures exclude particular items from the overall CPI, the results need to be interpreted carefully rather than treated as completely separate categories.# 6. Interpretation and limitations

The supplied CSV contains monthly observations for the measure “Percentage change from previous year” and the region Australia. The date coverage is determined from the imported file and is reported in the data-quality section above.

The supplied file appears to cover only a short recent period. Therefore, these charts can compare the available series and months, but they should not be used to make claims about long-term inflation trends. A longer historical download would be needed for a robust long-term analysis.

The data also includes multiple series IDs that can share a category label. The series ID is retained in the charts to prevent different series from being silently combined. The selected series and the reasons for selecting them should be reviewed before using these figures in a final narrative.

Conclusion

After exploring the CPI data through five visualisations, it becomes clear that annual price changes can differ across measures and over time. The charts provide different ways to examine these movements, from identifying the largest increases and decreases to comparing patterns between months.

One important point is that the overall CPI figure does not tell the whole story. Looking at individual categories and alternative CPI measures provides more detail, although overlapping measures need to be interpreted carefully. Overall, the visualisations help explain the main patterns in the available data and show why it is useful to examine inflation from more than one perspective.

7. Data source and acknowledgements

Data source: Australian Bureau of Statistics (ABS), Consumer Price Index (CPI). The file analysed here was supplied as australia_cpi_data.csv. Before submission, add the exact ABS download page URL and the download date, and verify the title and release details against the original source.

Acknowledgements:

I acknowledge the Australian Bureau of Statistics (ABS) as the source of the Consumer Price Index data used in this report. I used R and its visualisation packages to clean, analyse and present the data through interactive charts.

8. References

Australian Bureau of Statistics. (2026). Consumer Price Index, Australia. https://www.abs.gov.au/statistics/economy/price-indexes-and-inflation/consumer-price-index-australia/latest-release