Public debate around public safety in Victoria often oscillates between narratives of rising disorder and blanket reassurance. Examining open data published by the Crime Statistics Agency (CSA) Victoria for the year ending June 2026 reveals a distinct structural transformation. Traditional property crimes such as residential burglary have trended downward over the past decade, while opportunistic theft, family-incident offences, and regional variations present distinct challenges. Furthermore, investigation resolution rates vary drastically across offence types.
Has crime in Victoria increased uniformly across categories, or are specific divisions driving the overall rate?
# Load Table 01 from Recorded Offences
df_t1 <- read_excel("D:/assignment/10/visualization/Data_Tables_Recorded_Offences_Visualisation_Year_Ending_June_2026.xlsx", sheet = "Table 01")
# Aggregate by Year and Division
p1_data <- df_t1 %>%
group_by(Year, `Offence Division`) %>%
summarise(
Total_Count = sum(`Offence Count`, na.rm = TRUE),
Total_Rate = sum(`Rate per 100,000 population`, na.rm = TRUE),
.groups = "drop"
) %>%
mutate(
Division_Short = str_remove(`Offence Division`, "^[A-F] "),
Hover_Text = paste0(
"<b>Division:</b> ", Division_Short, "<br>",
"<b>Year:</b> ", Year, "<br>",
"<b>Rate per 100k:</b> ", comma(round(Total_Rate, 1)), "<br>",
"<b>Total Offences:</b> ", comma(Total_Count)
)
)
p1 <- ggplot(p1_data, aes(x = Year, y = Total_Rate, color = Division_Short, group = Division_Short, text = Hover_Text)) +
geom_line(linewidth = 1.1) +
geom_point(size = 2.2) +
scale_color_viridis_d(option = "viridis") +
scale_x_continuous(breaks = seq(2017, 2026, by = 1)) +
scale_y_continuous(labels = comma) +
labs(
title = "Recorded Offence Rate by Broad Division (2017–2026)",
subtitle = "Property and deception remains the highest volume category across Victoria.",
x = "Year Ending June",
y = "Rate per 100,000 Population",
color = "Offence Division",
caption = "Data Source: Crime Statistics Agency Victoria (Table 01)"
) +
theme_minimal(base_size = 12) +
theme(
legend.position = "bottom",
plot.title = element_text(face = "bold"),
panel.grid.minor = element_blank()
)
ggplotly(p1, tooltip = "text")
Looking beneath Division B reveals that physical break-ins have fallen markedly, while general theft has surged since 2021.
p2_data <- df_t1 %>%
filter(`Offence Division` == "B Property and deception offences") %>%
group_by(Year, `Offence Subdivision`) %>%
summarise(
Total_Count = sum(`Offence Count`, na.rm = TRUE),
Total_Rate = sum(`Rate per 100,000 population`, na.rm = TRUE),
.groups = "drop"
) %>%
mutate(
Subdiv_Short = str_remove(`Offence Subdivision`, "^B[0-9]{2} "),
Hover_Text = paste0(
"<b>Subdivision:</b> ", Subdiv_Short, "<br>",
"<b>Year:</b> ", Year, "<br>",
"<b>Rate per 100k:</b> ", comma(round(Total_Rate, 1)), "<br>",
"<b>Total Offences:</b> ", comma(Total_Count)
)
)
p2 <- ggplot(p2_data, aes(x = Year, y = Total_Rate, color = Subdiv_Short, group = Subdiv_Short, text = Hover_Text)) +
geom_line(linewidth = 1.1) +
geom_point(size = 2.2) +
scale_color_viridis_d(option = "viridis") +
scale_x_continuous(breaks = seq(2017, 2026, by = 1)) +
scale_y_continuous(labels = comma) +
labs(
title = "Property & Deception Offence Rates by Sub-category (2017–2026)",
subtitle = "Burglary has dropped since 2017, whereas theft has climbed sharply post-pandemic.",
x = "Year Ending June",
y = "Rate per 100,000 Population",
color = "Subdivision",
caption = "Data Source: Crime Statistics Agency Victoria (Table 01)"
) +
theme_minimal(base_size = 12) +
theme(
legend.position = "bottom",
plot.title = element_text(face = "bold"),
panel.grid.minor = element_blank()
)
ggplotly(p2, tooltip = "text")
Examining the latest 2026 recorded offence rates reveals how regional centers compare with metropolitan regions.
# Load LGA Table 01
df_lga <- read_excel("D:/assignment/10/visualization/Data_Tables_LGA_Recorded_Offences_Year_Ending_June_2026.xlsx", sheet = "Table 01")
p4_data <- df_lga %>%
filter(Year == 2026) %>%
filter(!`Police Region` %in% c("Justice Institutions and Immigration Facilities", "Unincorporated Vic")) %>%
filter(`Local Government Area` != "Total") %>% # drop police-region subtotal rows
arrange(desc(`Rate per 100,000 population`)) %>%
slice_head(n = 25) %>%
mutate(
Hover_Text = paste0(
"<b>LGA:</b> ", `Local Government Area`, "<br>",
"<b>Police Region:</b> ", `Police Region`, "<br>",
"<b>Rate per 100k:</b> ", comma(round(`Rate per 100,000 population`, 1)), "<br>",
"<b>Offence Count:</b> ", comma(`Offence Count`)
)
)
p4 <- ggplot(p4_data, aes(x = reorder(`Local Government Area`, `Rate per 100,000 population`),
y = `Rate per 100,000 population`,
fill = `Police Region`,
text = Hover_Text)) +
geom_bar(stat = "identity", width = 0.75) +
coord_flip() +
scale_fill_viridis_d(option = "cividis") +
scale_y_continuous(labels = comma) +
labs(
title = "Top 25 LGAs by Recorded Offence Rate (Year Ending June 2026)",
subtitle = "Per-capita offence rates in regional hubs (e.g., Latrobe, Mildura) rival or exceed metropolitan figures.",
x = "Local Government Area",
y = "Rate per 100,000 Population",
fill = "Police Region",
caption = "Data Source: Crime Statistics Agency Victoria (LGA Table 01)"
) +
theme_minimal(base_size = 11) +
theme(
plot.title = element_text(face = "bold"),
legend.position = "bottom"
)
ggplotly(p4, tooltip = "text")
Once an offence is reported to Victoria Police, how is it finalized across different divisions?
# Load Table 04
df_t4 <- read_excel("D:/assignment/10/visualization/Data_Tables_Recorded_Offences_Visualisation_Year_Ending_June_2026.xlsx", sheet = "Table 04")
p5_data <- df_t4 %>%
filter(Year == 2026) %>%
group_by(`Offence Division`, `Investigation Status`) %>%
summarise(Count = sum(`Offence Count`, na.rm = TRUE), .groups = "drop") %>%
group_by(`Offence Division`) %>%
mutate(
Total = sum(Count),
Proportion = (Count / Total) * 100,
Division_Short = str_remove(`Offence Division`, "^[A-F] "),
Hover_Text = paste0(
"<b>Division:</b> ", Division_Short, "<br>",
"<b>Status:</b> ", `Investigation Status`, "<br>",
"<b>Offence Count:</b> ", comma(Count), "<br>",
"<b>Proportion:</b> ", round(Proportion, 1), "%"
)
)
p5 <- ggplot(p5_data, aes(x = Division_Short, y = Proportion, fill = `Investigation Status`, text = Hover_Text)) +
geom_bar(stat = "identity", position = "fill", width = 0.7) +
coord_flip() +
scale_y_continuous(labels = percent_format()) +
scale_fill_viridis_d(option = "viridis") +
labs(
title = "Investigation Outcome Breakdown by Offence Division (2026)",
subtitle = "Crimes against the person and drugs show high clearance rates, while property offences remain mostly unsolved.",
x = "Offence Division",
y = "Proportion of Recorded Offences",
fill = "Investigation Status",
caption = "Data Source: Crime Statistics Agency Victoria (Table 04)"
) +
theme_minimal(base_size = 11) +
theme(
plot.title = element_text(face = "bold"),
legend.position = "bottom"
)
ggplotly(p5, tooltip = "text")