Overview

This report summarises Year 3 course unit enrolment data by discipline and course type.

Student Enrolment Insights

This section explores overall student enrolments across Year 3 course units. It highlights which units have the highest and lowest student numbers, helping to identify courses that are particularly popular or under-utilised.

library(tidyverse)
library(DT)

# Filter relevant rows
enrolment_data <- df %>%
  filter(Measure == "Number of students overall on the course unit", !is.na(Value))

# Top 10 most enrolled units
top_units <- enrolment_data %>%
  arrange(desc(Value)) %>%
  select(`Unit Code`, `Unit Name`, Value) %>%
  head(10)

# Bottom 10 least enrolled units
bottom_units <- enrolment_data %>%
  arrange(Value) %>%
  select(`Unit Code`, `Unit Name`, Value) %>%
  head(10)

# Display with DT
datatable(top_units, caption = "Top 10 Most Enrolled Units", options = list(pageLength = 5))

Enrolment Distribution by Discipline

This section provides a breakdown of total enrolments across different disciplines.

# Prepare summary data
discipline_summary <- df %>%
  filter(Measure == "Number of students overall on the course unit", !is.na(Value)) %>%
  group_by(Discipline) %>%
  summarise(Total_Enrolment = sum(Value), .groups = "drop") %>%
  arrange(desc(Total_Enrolment))

# Display data table
datatable(discipline_summary, caption = "Total Enrolment by Discipline", options = list(pageLength = 10))
# Plot enrolment by discipline
ggplot(discipline_summary, aes(x = reorder(Discipline, -Total_Enrolment), y = Total_Enrolment)) +
  geom_col(fill = "lightblue", colour = "black", width = 0.7) +
  geom_text(aes(label = round(Total_Enrolment, 0)), 
            vjust = -0.5, 
            size = 3.5) +
  coord_flip() +
  labs(title = "Total Enrolments by Discipline (Year 3)",
       x = "Discipline",
       y = "Number of Students") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
    axis.text.y = element_text(size = 10),
    axis.text.x = element_text(size = 10),
    axis.title = element_text(size = 12)
  )

Enrolment by Course Type

This section compares enrolments by course type (e.g., Core Optional, Pathway Option). It provides insights into student preferences and the effectiveness of curriculum design. Higher enrolment in certain types of courses may suggest relevance or popularity, while lower enrolment could highlight areas for review.

# Prepare summary by course type
course_type_summary <- df %>%
  filter(Measure == "Number of students overall on the course unit", !is.na(Value)) %>%
  group_by(`Course Type`) %>%
  summarise(Total_Enrolment = sum(Value), .groups = "drop") %>%
  arrange(desc(Total_Enrolment))

# Display data table
datatable(course_type_summary, caption = "Total Enrolment by Course Type", options = list(pageLength = 10))
# Plot enrolment by course type
ggplot(course_type_summary, aes(x = reorder(`Course Type`, -Total_Enrolment), y = Total_Enrolment)) +
  geom_col(fill = "lightgreen", colour = "black", width = 0.7) +
  geom_text(aes(label = round(Total_Enrolment, 0)), 
            vjust = -0.5, 
            size = 3.5) +
  coord_flip() +
  labs(title = "Total Enrolments by Course Type (Year 3)",
       x = "Course Type",
       y = "Number of Students") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
    axis.text.y = element_text(size = 10),
    axis.text.x = element_text(size = 10),
    axis.title = element_text(size = 12)
  )

Enrolments by Academic Year per Course Unit

This section explores the number of students from each academic year cohort (identified by Student Group codes, e.g., 1191 to 1241) enrolled in individual Year 3 course units.

Tracking enrolment by academic year helps identify:

  • How consistent student interest in specific units has been over time
  • Units that may be rising or falling in popularity with more recent cohorts
  • Potential curriculum shifts or changes in cohort preferences
# Filter for relevant pathway-specific data
pathway_counts <- df %>%
  filter(Measure == "Number by BASS pathway", !is.na(Value)) %>%
  select(`Unit Code`, `Unit Name`, `Student Group`, Value, Discipline) %>%
  arrange(desc(Value))

# Preview top 20 entries
datatable(head(pathway_counts, 20),
          caption = "Sample of Enrolments by BASS Pathway per Course Unit",
          options = list(pageLength = 10))

Heatmap of Enrolments by BASS Pathway and Course Unit

Heatmap of Enrolments by Academic Year and Course Unit

The heatmap below visualises the number of students from each academic year cohort (identified by Student Group codes) enrolled in individual Year 3 course units.

Darker shades indicate higher enrolment. This visual representation helps highlight:

  • Which units have maintained popularity across multiple cohorts
  • Which units have seen fluctuating or declining interest
  • The spread of enrolment across time, offering insight into curriculum stability and student preferences

It also helps identify units that are consistently in demand versus those with limited or cohort-specific appeal.

# Prepare data
heatmap_data <- df %>%
  filter(Measure == "Number by BASS pathway", !is.na(Value)) %>%
  mutate(Pathway = as.factor(`Student Group`)) %>%
  select(`Unit Code`, `Unit Name`, Pathway, Value)

# Create plot with Unit Code on Y axis and Unit Name in tooltip
p <- ggplot(heatmap_data, aes(x = Pathway, y = reorder(`Unit Code`, Value), fill = Value, text = paste(
  "Unit Code:", `Unit Code`, "<br>",
  "Unit Name:", `Unit Name`, "<br>",
  "Year:", Pathway, "<br>",
  "Students:", round(Value, 0)
))) +
  geom_tile(colour = "white") +
  scale_fill_gradient(low = "white", high = "darkblue") +
  labs(title = "Interactive Heatmap: Enrolment by BASS Pathway and Course Unit",
       x = "BASS Pathway (Student Group)",
       y = "Unit Code",
       fill = "Number of Students") +
  theme_minimal()

# Convert to interactive plot
ggplotly(p, tooltip = "text")

Top 20 Units by Discipline (Interactive Heatmap)

The heatmap below shows how student enrolments in the top 20 most popular Year 3 units are distributed across academic disciplines. This provides insight into which departments offer widely chosen units and where overlaps or interdisciplinary engagement occur.

# Prepare data: enrolment by unit and discipline
heatmap_data <- df %>%
  filter(Measure == "Number of students overall on the course unit", !is.na(Value)) %>%
  select(`Unit Code`, Discipline, Value)

# Get top 20 most enrolled unit codes
top_units <- heatmap_data %>%
  group_by(`Unit Code`) %>%
  summarise(Total = sum(Value), .groups = "drop") %>%
  slice_max(Total, n = 20)

# Filter to top units only
heatmap_data <- heatmap_data %>%
  filter(`Unit Code` %in% top_units$`Unit Code`)

# Plot
p <- ggplot(heatmap_data, aes(
  x = Discipline,
  y = fct_reorder(`Unit Code`, Value, .fun = sum),
  fill = Value,
  text = paste(
    "Unit Code:", `Unit Code`, "<br>",
    "Discipline:", Discipline, "<br>",
    "Students:", round(Value, 0)
  )
)) +
  geom_tile(colour = "white") +
  scale_fill_gradient(low = "white", high = "darkblue") +
  labs(
    title = "Top 20 Units by Enrolment (by Discipline)",
    x = "Discipline",
    y = "Unit Code",
    fill = "No. of Students"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
    axis.text.y = element_text(size = 9)
  )

ggplotly(p, tooltip = "text")

How to Read the Heatmap: Top 20 Year 3 Units by Discipline

This interactive heatmap displays enrolments in the 20 most popular Year 3 course units, broken down by student discipline. It highlights cross-disciplinary engagement and course popularity across departments.

What Each Element Means

  • Y-axis (Rows):
    Each row represents a unique course
  • X-axis (Columns):
    Each column represents an academic discipline, such as:
    • Criminology
    • Sociology
    • Politics
    • Data Analytics
    • Philosophy (included even if no students enrolled)
  • Tiles (Squares):
    Each square shows the number of students from that discipline enrolled in the unit.
    • Darker shades = more students
    • Lighter shades = fewer students
    • White or missing tiles = zero enrolment from that discipline
  • Tooltips (on hover):
    Hovering your mouse over any tile reveals:
    • Unit code and name
    • Discipline
    • Number of enrolled students

What This Reveals

  • Units that are core to a specific discipline (dominant enrolment in one column)
  • Interdisciplinary units attracting students from multiple disciplines
  • Whether some disciplines are underrepresented in top-level Year 3 offerings
  • Opportunities for cross-departmental teaching

Unit Name Lookup Table

The table below lists all Year 3 course units along with their total BASS enrolment across years, overall enrolment, and BASS pathway percentage. You can filter and sort the table to identify the most and least popular course units.

# Filter for total course unit data
df_filtered <- df %>%
  filter(Measure == "Number of students overall on the course unit")

# Total enrolment per course unit (BASS + non-BASS)
total_enrolment <- df_filtered %>%
  group_by(`Unit Code`, `Unit Name`) %>%
  summarise(`Total Enrolment` = sum(Value), .groups = "drop")

# BASS enrolment is the same as this filtered dataset
bass_enrolment <- df_filtered %>%
  group_by(`Unit Code`, `Unit Name`) %>%
  summarise(`BASS Enrolment` = sum(Value), .groups = "drop")

# Merge and calculate %
unit_summary <- left_join(total_enrolment, bass_enrolment,
                          by = c("Unit Code", "Unit Name")) %>%
  mutate(`BASS %` = round(`BASS Enrolment` / `Total Enrolment` * 100, 1)) %>%
  arrange(desc(`BASS Enrolment`))

# Display as datatable
datatable(unit_summary, options = list(pageLength = 10), rownames = FALSE)

You can explore enrolment patterns by discipline or course unit using the interactive dashboard:
https://sisteranalyst.shinyapps.io/year3/

Units Dominant by Cohort Year

This visual shows which units are especially popular for particular academic cohorts (i.e., year groups). It helps identify units that may have been core, required, or particularly aligned with a given year’s programme structure.

The chart below focuses on the top 15 Year 3 course units based on total BASS enrolment across all academic cohorts (from 1191 to 1241).

These units were identified by summing the number of BASS students enrolled in each unit across all years, and selecting the 9 with the highest cumulative enrolment. This ensures the visual highlights the most impactful or widely taken units in the programme, offering a clearer comparison of how their popularity varied by cohort year.

This can reveal:

  • Units that may have been mandatory for specific years
  • The rise or decline in interest over time
  • Differences in cohort-specific programme structure or options
# Prepare data
df_units_by_year <- df %>%
  filter(Measure == "Number by BASS pathway") %>%
  mutate(UnitLabel = paste(`Unit Code`),
         CohortYear = as.character(`Student Group`))  # Academic year as string

# Aggregate totals per unit and cohort
unit_year_totals <- df_units_by_year %>%
  group_by(UnitLabel, CohortYear) %>%
  summarise(Enrolment = sum(Value), .groups = "drop")

# Filter to top 9 units overall for readability
top_units <- unit_year_totals %>%
  group_by(UnitLabel) %>%
  summarise(Total = sum(Enrolment), .groups = "drop") %>%
  top_n(12, Total) %>%
  pull(UnitLabel)

unit_year_top <- unit_year_totals %>%
  filter(UnitLabel %in% top_units)

# Plot with 3 plots per row
ggplot(unit_year_top, aes(x = CohortYear, y = Enrolment, fill = CohortYear)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ UnitLabel, scales = "free_y", ncol = 3) +
  labs(title = "Top Units: Enrolment by Cohort Year",
       x = "Academic Year", y = "No. of Students") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Uptake of Core Options vs Pathway Options

This bar chart compares enrolment across course types (e.g., core optional modules vs pathway options) to explore whether students favour flexible choices over structured ones.

This bar chart compares student enrolment across different course types (e.g., core optional modules vs pathway options). It summarises total enrolment for each course type across all academic years and disciplines.

By grouping units by their Course Type and summing the number of students enrolled in each, the chart shows whether students tend to prefer structured offerings (like “Core Optional for POLI Pathway”) or more flexible choices (like “POLI Pathway Option”).

Why This Matters

This comparison is useful for understanding:

  • Student preference: Do students lean towards elective flexibility or stick with pathway-prescribed units?
  • Curriculum balance: Are some course types over or under-enrolled relative to their intent?
  • Potential for review: Underused core options may need re-evaluation.
# Filter relevant measure
df_filtered <- df %>%
  filter(Measure == "Number of students overall on the course unit")

# Summarise by Course Type
course_type_summary <- df_filtered %>%
  group_by(`Course Type`) %>%
  summarise(`Total Enrolment` = sum(Value, na.rm = TRUE), .groups = "drop") %>%
  arrange(desc(`Total Enrolment`))

# Plot
ggplot(course_type_summary, aes(x = reorder(`Course Type`, `Total Enrolment`), 
                                y = `Total Enrolment`)) +
  geom_col(fill = "darkgreen", colour = "black") +
  geom_text(aes(label = `Total Enrolment`), vjust = -0.3, size = 3) +
  labs(title = "Uptake of Core vs Pathway Option Units",
       x = "Course Type", y = "No. of Students Enrolled") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1))

What the Chart Shows

The chart indicates that some pathway options (like those in Politics) attract significantly more students than core optional units. For example, Politics Pathway Option enrolments exceed 4,000, while Core Optional for Poli Pathway is notably lower. This suggests that BASS students may be exercising greater choice in their third year, favouring optional modules over core recommendations.

Discipline Distribution Across Top Year 3 Units

This bubble chart shows the number of students from each discipline enrolled in the top 15 most enrolled course units. Hover to see exact student numbers for each discipline-unit pair. The chart uses only unit codes for compactness and a reference table is provided below.

# Create UnitLabel and get top 15 by total enrolment
df_top_units <- df %>%
  filter(Measure == "Number of students overall on the course unit") %>%
  mutate(UnitCode = `Unit Code`,
         UnitName = `Unit Name`,
         UnitLabel = paste(`Unit Code`, "—", `Unit Name`)) %>%
  group_by(UnitCode, UnitName) %>%
  summarise(Total = sum(Value), .groups = "drop") %>%
  top_n(15, Total)

# Get labels
top_codes <- df_top_units$UnitCode

# Prepare data for bubble chart
df_bubble <- df %>%
  filter(Measure == "Number of students overall on the course unit") %>%
  filter(`Unit Code` %in% top_codes) %>%
  group_by(UnitCode = `Unit Code`, Discipline) %>%
  summarise(Enrolment = sum(Value), .groups = "drop")

# Plotly bubble chart
plot_ly(df_bubble,
        x = ~Discipline,
        y = ~UnitCode,
        type = 'scatter',
        mode = 'markers',
        size = ~Enrolment,
        sizes = c(10, 80),
        marker = list(
          opacity = 0.7,
          color = 'steelblue',
          line = list(width = 1, color = 'black') 
        ),
        text = ~paste("Unit:", UnitCode,
                      "<br>Students:", Enrolment),
        hoverinfo = 'text') %>%
  layout(title = "Discipline Enrolment in Top 15 Year 3 Units",
         xaxis = list(title = "Discipline"),
         yaxis = list(title = "Course Unit Code"),
         showlegend = FALSE)

Course Unit Name

# Table of top unit codes with full names
datatable(df_top_units, 
          colnames = c("Unit Code", "Unit Name", "Total Enrolment"),
          options = list(pageLength = 10, scrollX = TRUE),
          rownames = FALSE)

The chart above displays the top 15 Year 3 course units by total enrolment, highlighting their associated discipline and relative popularity. Each bubble represents a course unit, positioned by discipline on the x-axis and identified by unit code on the y-axis. The size of the bubble corresponds to the number of students enrolled in that unit.

Key observations:

  • Politics dominates this list with several high-enrolment units, reflecting both its broad appeal and potentially its inclusion as core or pathway-recommended content across multiple BASS routes.
  • Sociology, Philosophy, and Data Analytics each contribute high-ranking units, indicating strong engagement with those subject areas in Year 3.
  • Anthropology appears with smaller but still notable unit enrolments, suggesting steady interest though likely from smaller cohorts.
  • The chart also helps visualise the range of unit sizes from large, possibly compulsory or highly popular modules (e.g. POLI30300) to more niche options with moderate but meaningful uptake.

This type of visualisation is useful for identifying:

  • Which disciplines are delivering the most visible or in-demand content in the final year
  • Opportunities to promote under-enrolled but valuable units
  • Patterns in course selection that align with student interests and programme design

It can also support programme planning/restructuring, by tracking shifts in student preferences over time.

Course Type Distribution by Discipline

The chart above illustrates how Year 3 student enrolments are distributed across different course types within each discipline. Each bar represents a discipline, stacked by the total number of students enrolled in various course categories such as Core, Pathway Options, and Core Optionals tied to specific pathways.

# Ensure Value is numeric
y3$Value <- suppressWarnings(as.numeric(y3$Value))

# Filter to total enrolment per unit
course_type_summary <- y3 %>%
  filter(Measure == "Number of students overall on the course unit", !is.na(Value)) %>%
  group_by(Discipline, `Course Type`) %>%
  summarise(Total = sum(Value), .groups = 'drop')

# Plot stacked bars
ggplot(course_type_summary, aes(x = Discipline, y = Total, fill = `Course Type`)) +
  geom_bar(stat = "identity", position = "stack", colour = "black") +
  labs(
    title = "Enrolment by Course Type and Discipline (Year 3)",
    x = "Discipline",
    y = "Total Students",
    fill = "Course Type"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    plot.title = element_text(face = "bold", hjust = 0.5),
    axis.title = element_text(face = "bold")
  )

Key observations:

  • Politics and Sociology contribute the highest total enrolment volumes, reflecting a broad set of course offerings with strong pathway-specific and core components.
  • Criminology and Data Analytics show more balanced enrolment across pathway options and core or core-optional units, indicating a structured yet flexible curriculum.
  • Anthropology and Philosophy have relatively smaller total enrolments and appear to rely more heavily on optional units, suggesting they may offer a more elective-based structure in Year 3.

This breakdown helps:

  • Identify disciplines with heavier core teaching loads
  • Spot disciplines offering greater student choice through optionality
  • Support programme design reviews by highlighting structural differences in course delivery across subject areas

This Year 3 enrolment report has provided a detailed exploration of student registrations across disciplines, course types, and academic years. It explores:

  • Disciplinary contributions to course offerings
  • Variation in student engagement with optional vs. core units
  • Cross-disciplinary appeal of course units
  • Longitudinal trends in enrolment at the unit level

You can explore enrolment patterns by discipline or course unit using shiny app, i.e. the interactive dashboard:
https://sisteranalyst.shinyapps.io/year3/

These findings can inform programme redesign and pedagogical strategies aimed at optimising student experience and sustaining healthy enrolment patterns.