Introduction University is often described as an investment in the future. However, graduate outcomes differ substantially across fields of study. These five interactive visualisations explore how Australian undergraduate degrees compare across salary, employment and medium-term earnings growth. The data is sourced from the Quality Indicators for Learning and Teaching Graduate Outcomes Survey and Graduate Outcomes Survey – Longitudinal datasets.
salary_raw <- read_excel(
"data/GOS_2024_National_Report_Tables.xlsx",
sheet = "SAL_UG_ALL_2Y_AREA_E315",
skip = 9,
col_names = FALSE
)
## New names:
## • `` -> `...1`
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
## • `` -> `...5`
## • `` -> `...6`
## • `` -> `...7`
## • `` -> `...8`
salary_clean <- salary_raw %>%
select(1:8) %>%
set_names(c(
"blank", "study_area", "male_2023", "male_2024",
"female_2023", "female_2024", "total_2023", "total_2024"
)) %>%
filter(!is.na(study_area)) %>%
filter(!study_area %in% c("Back to INDEX", "Total", "Standard deviation")) %>%
mutate(
total_2024 = as.numeric(gsub(",", "", total_2024))
) %>%
filter(!is.na(total_2024))
employment_raw <- read_excel(
"data/GOS_2024_National_Report_Tables.xlsx",
sheet = "EMP_UG_ALL_2Y_AREA45",
skip = 8,
col_names = FALSE
)
## New names:
## • `` -> `...1`
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
## • `` -> `...5`
## • `` -> `...6`
## • `` -> `...7`
## • `` -> `...8`
employment_clean <- employment_raw %>%
select(1:8) %>%
set_names(c(
"blank", "study_area", "fte_2023", "fte_2024",
"overall_2023", "overall_2024", "lfp_2023", "lfp_2024"
)) %>%
filter(!is.na(study_area)) %>%
filter(!study_area %in% c("Back to INDEX", "Total", "Standard deviation")) %>%
mutate(
fte_2024 = as.numeric(fte_2024),
overall_2024 = as.numeric(overall_2024),
lfp_2024 = as.numeric(lfp_2024)
) %>%
filter(!is.na(fte_2024))
employment_grouped <- employment_clean %>%
mutate(
study_group = case_when(
str_detect(study_area, "Engineering") ~ "Engineering",
str_detect(study_area, "Teacher education") ~ "Teacher education",
str_detect(study_area, "Law") ~ "Law and paralegal studies",
str_detect(study_area, "Psychology") ~ "Psychology",
str_detect(study_area, "Social work") ~ "Social work",
str_detect(study_area, "Nursing") ~ "Nursing",
str_detect(study_area, "Medicine") ~ "Medicine",
str_detect(study_area, "Pharmacy") ~ "Pharmacy",
str_detect(study_area, "Dentistry") ~ "Dentistry",
str_detect(study_area, "Veterinary science") ~ "Veterinary science",
str_detect(study_area, "Humanities|Political science|Language") ~ "Humanities & Social Sciences",
str_detect(study_area, "Computing") ~ "Computing & IT",
str_detect(study_area, "Accounting|Banking|Economics|Management|Sales") ~ "Business & Management",
str_detect(study_area, "Art|Music") ~ "Creative arts",
TRUE ~ NA_character_
)
) %>%
filter(!is.na(study_group))
employment_summary <- employment_grouped %>%
group_by(study_group) %>%
summarise(
fte_2024 = mean(fte_2024, na.rm = TRUE),
overall_2024 = mean(overall_2024, na.rm = TRUE),
lfp_2024 = mean(lfp_2024, na.rm = TRUE),
.groups = "drop"
)
salary_summary <- salary_clean %>%
mutate(
study_group = case_when(
study_area == "Business and management" ~ "Business & Management",
study_area == "Computing and information systems" ~ "Computing & IT",
study_area == "Humanities, culture and social sciences" ~ "Humanities & Social Sciences",
TRUE ~ study_area
)
) %>%
select(study_group, total_2024)
merged_data <- left_join(
salary_summary,
employment_summary,
by = "study_group"
)
gosl_raw <- read_excel(
"data/GOSL_2025_National_Tables_Public.xlsx",
sheet = "SAL_UG_ALL_AREA_E315",
skip = 10,
col_names = FALSE
)
## New names:
## • `` -> `...1`
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
## • `` -> `...5`
## • `` -> `...6`
## • `` -> `...7`
## • `` -> `...8`
salary_growth <- gosl_raw %>%
select(1:8) %>%
set_names(c(
"blank", "study_area", "short_male", "short_female",
"short_total", "medium_male", "medium_female", "medium_total"
)) %>%
filter(!is.na(study_area)) %>%
filter(!study_area %in% c("Total", "Standard deviation")) %>%
mutate(
short_total = gsub(",", "", short_total),
medium_total = gsub(",", "", medium_total),
short_total = ifelse(short_total == "n/a", NA, short_total),
medium_total = ifelse(medium_total == "n/a", NA, medium_total),
short_total = as.numeric(short_total),
medium_total = as.numeric(medium_total),
salary_growth = medium_total - short_total
) %>%
filter(!is.na(short_total), !is.na(medium_total))
chart1_data <- salary_clean %>%
slice_max(total_2024, n = 15) %>%
arrange(total_2024)
plot_ly(
chart1_data,
x = ~total_2024,
y = ~reorder(study_area, total_2024),
type = "bar",
orientation = "h",
text = ~paste(
"<b>", study_area, "</b>",
"<br>Median salary:", dollar(total_2024)
),
hoverinfo = "text"
) %>%
layout(
title = "Graduate salaries vary dramatically across disciplines",
xaxis = list(title = "Median full-time salary, 2024 ($)"),
yaxis = list(title = ""),
margin = list(l = 230)
)
chart2_data <- employment_clean %>%
slice_max(fte_2024, n = 15) %>%
arrange(fte_2024)
plot_ly(
chart2_data,
x = ~fte_2024,
y = ~reorder(study_area, fte_2024),
type = "bar",
orientation = "h",
text = ~paste(
"<b>", study_area, "</b>",
"<br>Full-time employment:", round(fte_2024, 1), "%"
),
hoverinfo = "text"
) %>%
layout(
title = "Employment outcomes are not equal across degrees",
xaxis = list(title = "Full-time employment rate, 2024 (%)"),
yaxis = list(title = ""),
margin = list(l = 230)
)
3.HIGH SALARIES DO NOT ALWAYS MEAN BETTER EMPLYEMENT OPORTUNITIES
chart3_data <- merged_data %>%
filter(!is.na(total_2024), !is.na(fte_2024))
plot_ly(
chart3_data,
x = ~total_2024,
y = ~fte_2024,
type = "scatter",
mode = "markers+text",
text = ~study_group,
textposition = "top center",
marker = list(size = 14, opacity = 0.75),
hovertemplate = paste(
"<b>%{text}</b><br>",
"Salary: $%{x:,.0f}<br>",
"Full-time employment: %{y:.1f}%<extra></extra>"
)
) %>%
layout(
title = "High salaries do not always mean better employment prospects",
xaxis = list(title = "Median full-time salary, 2024 ($)"),
yaxis = list(title = "Full-time employment rate, 2024 (%)")
)
growth_chart <- salary_growth %>%
arrange(salary_growth) %>%
mutate(study_area = factor(study_area, levels = study_area))
plot_ly(
growth_chart,
x = ~salary_growth,
y = ~study_area,
type = "bar",
orientation = "h",
text = ~paste0("$", comma(salary_growth)),
hovertemplate = paste(
"<b>%{y}</b><br>",
"Short-term salary: $%{customdata[0]:,.0f}<br>",
"Medium-term salary: $%{customdata[1]:,.0f}<br>",
"Growth: $%{x:,.0f}<extra></extra>"
),
customdata = ~cbind(short_total, medium_total)
) %>%
layout(
title = "The payoff from some degrees emerges years later",
xaxis = list(title = "Increase in median salary ($)"),
yaxis = list(title = "")
)
5.NOT ALL DEGREES PAYOFF EQUALLY
chart5_data <- merged_data %>%
left_join(
salary_growth %>%
mutate(
study_group = case_when(
study_area == "Business and management" ~ "Business & Management",
study_area == "Computing and information systems" ~ "Computing & IT",
study_area == "Humanities, culture and social sciences" ~ "Humanities & Social Sciences",
TRUE ~ study_area
)
) %>%
select(study_group, salary_growth),
by = "study_group"
) %>%
filter(!is.na(total_2024), !is.na(fte_2024), !is.na(salary_growth)) %>%
mutate(
salary_score = rescale(total_2024, to = c(0, 100)),
employment_score = rescale(fte_2024, to = c(0, 100)),
growth_score = rescale(salary_growth, to = c(0, 100)),
return_score = round((salary_score + employment_score + growth_score) / 3, 1)
) %>%
arrange(return_score)
plot_ly(
chart5_data,
x = ~return_score,
y = ~reorder(study_group, return_score),
type = "bar",
orientation = "h",
text = ~paste0(return_score, "/100"),
hovertemplate = paste(
"<b>%{y}</b><br>",
"Overall return score: %{x}/100<br>",
"Median salary: $%{customdata[0]:,.0f}<br>",
"Full-time employment: %{customdata[1]:.1f}%<br>",
"Salary growth: $%{customdata[2]:,.0f}<extra></extra>"
),
customdata = ~cbind(total_2024, fte_2024, salary_growth)
) %>%
layout(
title = "Not all degrees pay off equally",
xaxis = list(title = "Overall return score"),
yaxis = list(title = ""),
margin = list(l = 230)
)