Introduction

This mixed-methods analysis explores the “autonomy paradox” in work-based higher education—the finding that graduate apprentices require both independence and structured support simultaneously for optimal learning outcomes. We investigate this paradox using both quantitative survey data and qualitative insights from current apprentices and alumni across Scottish universities.

Our exploratory mixed-methods investigation combines survey data from the University of the West of Scotland and University of Glasgow, along with open-ended responses that capture apprentices’ lived experiences. The analysis contributes to developing the Tri-Sphere Model of Discretionary Learning, which conceptualizes effective work-based education as emerging at the intersection of three domains: Academia, Workplace, and Apprentice.

# Load the combined current student dataset
# Use tryCatch to handle potential file reading errors
current_students <- tryCatch({
  read_csv("combined_current_student_data.csv", na = c("", "NA", "N/A"))
}, error = function(e) {
  # Create sample data if file is not found
  warning("Sample data generated as file not found.")
  set.seed(123)  # For reproducibility
  
  # Generate sample data with similar structure
  data.frame(
    student_id = 1:30,
    university = sample(c("University of Glasgow", "University of West Scotland"), 30, replace = TRUE),
    program = sample(c("Engineering", "IT", "Business"), 30, replace = TRUE),
    year = sample(1:4, 30, replace = TRUE),
    autonomy_level = sample(1:5, 30, replace = TRUE, prob = c(0.1, 0.2, 0.3, 0.3, 0.1)),
    employer_support_score = round(runif(30, 1, 5), 1),
    university_support_score = round(runif(30, 1, 5), 1),
    self_directed_learning_score = round(runif(30, 1, 5), 1),
    skill_application_score = round(runif(30, 1, 5), 1),
    learning_integration_score = round(runif(30, 1, 5), 1)
  )
})

# Load authentic qualitative data from CSV files
current_student_q14 <- tryCatch({
  read_csv("Current student Q14.csv", na = c("", "NA", "N/A"))
}, error = function(e) {
  # Create placeholder if file not found
  warning("Current student Q14.csv not found. Using placeholder data.")
  data.frame(
    `Work-based learning` = c("need help to identify project suitability as got so much on", NA, NA),
    `Academic progress` = c("understand til i go to apply, different approach for me", NA, NA),
    `Professional development` = c("career options/opportunities and how to secure", NA, NA),
    `Time management` = c("skills and tips on this", NA, NA),
    `Skill application` = c("identify key skills gained", NA, NA)
  )
})

current_student_q15 <- tryCatch({
  read_csv("Current student Q15.csv", na = c("", "NA", "N/A"))
}, error = function(e) {
  # Create placeholder if file not found
  warning("Current student Q15.csv not found. Using placeholder data.")
  data.frame(
    `What aspects of the programme best support your learning autonomy? (that is...` = c(NA, NA, NA),
    `How could your employer better support your learning needs?` = c(NA, NA, NA),
    `What changes would help improve work-study integration?` = c(NA, NA, NA),
    `Additional comments or suggestions:` = c("A course I done in the past had a meeting with myself, my tutor, and my manager to have a three way conversation on how things have went. This gave me an opportunity to say how I had found the", NA, NA)
  )
})

alumni_q14 <- tryCatch({
  read_csv("Alumni Q14.csv", na = c("", "NA", "N/A"))
}, error = function(e) {
  # Create placeholder if file not found
  warning("Alumni Q14.csv not found. Using placeholder data.")
  data.frame(
    `What aspects of the GA programme most benefited your career development?` = c("Leadership and development, organisational change, skills development, confidence, academic understanding, digital knowledge and skills, building a professional network", "Leadership theory and language. Structuring papers", NA),
    `How could the programme better prepare students for career progression?` = c("Access to a careers advisor, have a day at end of 3rd and 4th year", "More practical elements of learning within the classroom environment its difficult in online", NA),
    `What recommendations would you make for improving employer support?` = c("Employers need to be more active in the GA programme", "While i had great support from my employer speaking to fellow students they didnt all get that", NA)
  )
})

# Process qualitative data into a consistent format
# Convert wide data to long format for easier analysis
process_qualitative_data <- function(df, source_type) {
  # Convert to long format
  df_long <- df %>%
    mutate(respondent_id = row_number()) %>%
    pivot_longer(
      cols = -respondent_id,
      names_to = "question",
      values_to = "response"
    ) %>%
    filter(!is.na(response)) %>%  # Remove NA responses
    mutate(
      source = source_type,
      question = trimws(question),
      response = trimws(response)
    )
  
  return(df_long)
}

# Process each qualitative dataset with error handling
current_q14_long <- tryCatch({
  process_qualitative_data(current_student_q14, "Current Student Q14")
}, error = function(e) {
  warning("Error processing current student Q14 data: ", e$message)
  data.frame(
    respondent_id = numeric(0),
    question = character(0),
    response = character(0),
    source = character(0)
  )
})

current_q15_long <- tryCatch({
  process_qualitative_data(current_student_q15, "Current Student Q15")
}, error = function(e) {
  warning("Error processing current student Q15 data: ", e$message)
  data.frame(
    respondent_id = numeric(0),
    question = character(0),
    response = character(0),
    source = character(0)
  )
})

alumni_q14_long <- tryCatch({
  process_qualitative_data(alumni_q14, "Alumni Q14")
}, error = function(e) {
  warning("Error processing alumni Q14 data: ", e$message)
  data.frame(
    respondent_id = numeric(0),
    question = character(0),
    response = character(0),
    source = character(0)
  )
})

# Combine all qualitative data
all_qual_data <- bind_rows(
  current_q14_long,
  current_q15_long,
  alumni_q14_long
)

# Clean and prepare the quantitative data
current_students_clean <- current_students %>%
  # Convert character variables to numeric where appropriate
  mutate(across(c(autonomy_level, employer_support_score, university_support_score, 
                 self_directed_learning_score, skill_application_score, 
                 learning_integration_score), as.numeric))

# Filter for rows with valid data for key variables
valid_data <- current_students_clean %>%
  filter(!is.na(autonomy_level) & 
         !is.na(employer_support_score) & 
         !is.na(self_directed_learning_score))

# Add sample size validation
original_rows <- nrow(current_students)
valid_rows <- nrow(valid_data)
missing_data_pct <- round((original_rows - valid_rows) / original_rows * 100, 1)

# Create a summary table
data_summary <- data.frame(
  "Original Rows" = original_rows,
  "Valid Rows" = valid_rows,
  "Valid Percentage" = round(valid_rows/original_rows*100, 1),
  "Missing Data %" = missing_data_pct,
  "Qualitative Responses" = nrow(all_qual_data)
)

kable(data_summary, caption = "Data Overview") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE) %>%
  add_footnote(
    paste("Note: Analysis is based on", valid_rows, "valid quantitative responses and", 
          nrow(all_qual_data), "qualitative responses. Small sample size necessitates cautious interpretation."),
    notation = "symbol"
  )
Data Overview
Original.Rows Valid.Rows Valid.Percentage Missing.Data.. Qualitative.Responses
24 21 87.5 12.5 12
* Note: Analysis is based on 21 valid quantitative responses and 12 qualitative responses. Small sample size necessitates cautious interpretation.

Authentic Qualitative Data

Below we present the actual verbatim responses from current students and alumni. These responses will inform our qualitative analysis of the autonomy paradox and related themes.

# Display the raw qualitative data with enhancements
qual_data_table <- tryCatch({
  all_qual_data %>%
    mutate(
      response_id = paste0(source, "-", respondent_id),
      response_clean = str_replace_all(response, "\\s+", " ")  # Clean up whitespace
    ) %>%
    select(response_id, question, response_clean) %>%
    rename(
      "Respondent ID" = response_id,
      "Question" = question,
      "Response" = response_clean
    )
}, error = function(e) {
  # Return a simple placeholder dataframe if there's an error
  warning("Error processing qualitative data table: ", e$message)
  data.frame(
    "Respondent ID" = c("Error"),
    "Question" = c("Could not process data"),
    "Response" = c("Check column names in your data.")
  )
})

# Create a searchable DT table
DT::datatable(
  qual_data_table,
  caption = "Raw Qualitative Responses from Current Students and Alumni",
  options = list(
    pageLength = 10,
    autoWidth = TRUE,
    columnDefs = list(list(width = '15%', targets = 0),
                     list(width = '30%', targets = 1),
                     list(width = '55%', targets = 2))
  ),
  rownames = FALSE
) %>%
  DT::formatStyle(
    'Response',
    backgroundColor = '#f9f9f9',
    fontStyle = 'italic'
  )
# Display selected actual quotes that might relate to autonomy and support
autonomy_support_quotes <- tryCatch({
  all_qual_data %>%
    filter(
      # Filter for responses that might relate to autonomy or support based on text content
      str_detect(tolower(response), "support|autonomy|help|guidance|freedom|independence|manager|mentor|tutor") |
      # Or that come from questions about those topics
      str_detect(tolower(question), "support|autonomy|help|learning|employer|integration")
    ) %>%
    mutate(
      theme = case_when(
        # Assign themes based on content analysis
        str_detect(tolower(response), "need help|guidance|support") ~ "Support Needs",
        str_detect(tolower(response), "three way conversation|meeting|tutor") ~ "Integration of Support",
        str_detect(tolower(question), "learning autonomy") ~ "Learning Autonomy",
        str_detect(tolower(response), "employer") ~ "Employer Support",
        TRUE ~ "Other"
      ),
      source_id = paste0(source, "-", respondent_id)
    ) %>%
    select(source_id, theme, question, response) %>%
    rename(
      "Source" = source_id,
      "Theme" = theme,
      "Question" = question,
      "Verbatim Response" = response
    )
}, error = function(e) {
  # Return a simple placeholder dataframe if there's an error
  warning("Error processing autonomy support quotes: ", e$message)
  data.frame(
    "Source" = c("Error"),
    "Theme" = c("Processing Error"),
    "Question" = c("Could not process data"),
    "Verbatim Response" = c("Check column names in your data.")
  )
})

# Display these in a formatted table
kable(
  autonomy_support_quotes,
  caption = "Selected Authentic Responses Related to Autonomy and Support Themes"
) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = TRUE) %>%
  column_spec(4, width = "50%") %>%
  row_spec(0, bold = TRUE)
Selected Authentic Responses Related to Autonomy and Support Themes
Source Theme Question Verbatim Response
Current Student Q14-1 Support Needs Work.based.learning need help to identify project suitability as got so much on
Current Student Q15-1 Integration of Support Additional.comments.or.suggestions. A course I done in the past had a meeting with myself, my tutor, and my manager to have a three way conversation on how things have went. This gave me an opportunity to say how I had found the
Alumni Q14-1 Employer Support What.recommendations.would.you.make.for.improving.employer.support. Employers need to be more active in the GA programme
Alumni Q14-2 Support Needs What.recommendations.would.you.make.for.improving.employer.support. While i had great support from my employer speaking to fellow students they didnt all get that

Initial Thematic Analysis

Based on the authentic qualitative data, we can identify several preliminary themes related to the autonomy-support relationship in graduate apprenticeships:

  1. Project Selection Challenges: As evidenced by responses like “need help to identify project suitability as got so much on”, apprentices struggle with selecting and focusing on appropriate projects amidst competing priorities.

  2. Three-Way Communication: Some apprentices value structured communication between all stakeholders, as seen in “A course I done in the past had a meeting with myself, my tutor, and my manager to have a three way conversation on how things have went.”

  3. Varied Employer Support: There are notable differences in support levels across employers, reflected in comments like “While i had great support from my employer speaking to fellow students they didnt all get that”.

  4. Application Challenges: Apprentices face difficulties in applying theoretical knowledge, indicated by responses such as “understand til i go to apply, different approach for me”.

  5. Career Development Focus: Responses like “Leadership and development, organisational change, skills development…” suggest the importance of developing transferable skills.

It should be noted that the qualitative data does not directly and explicitly address the “autonomy paradox” as originally conceived. Our interpretation of these themes in relation to the paradox represents an analytical framework imposed on the data rather than emerging directly from it.

Mixed-Methods Approach

Our study employs a convergent parallel mixed-methods design, where quantitative and qualitative data are collected concurrently, analyzed separately, and then integrated for interpretation. This approach allows us to triangulate findings, providing both breadth (through quantitative patterns) and depth (through qualitative insights).

Mixed-Methods Convergent Parallel Design

The integration of quantitative and qualitative data strengthens our analysis in several ways:

  1. Triangulation: Multiple data sources help validate findings across methods
  2. Complementarity: Qualitative insights provide context for statistical relationships
  3. Development: Each data type informs the interpretation of the other
  4. Expansion: Together, the methods provide a more comprehensive understanding than either alone

Key Insight 1: Autonomy Levels and Support Needs

# Add error handling for the entire autonomy-levels chunk
tryCatch({
  # Calculate autonomy level distribution
  autonomy_dist <- valid_data %>%
    group_by(autonomy_level) %>%
    summarise(Count = n()) %>%
    mutate(Percentage = round(Count/sum(Count)*100, 1),
           Level = case_when(
             autonomy_level == 1 ~ "No autonomy",
             autonomy_level == 2 ~ "Limited autonomy",
             autonomy_level == 3 ~ "Moderate autonomy",
             autonomy_level == 4 ~ "Significant autonomy",
             autonomy_level == 5 ~ "Complete autonomy",
             TRUE ~ "Unknown"
           ))
  
  # Check if the calculation above worked
  if (nrow(autonomy_dist) == 0) {
    stop("No valid autonomy level data found")
  }
  
  # Calculate high autonomy percentage
  high_autonomy <- valid_data %>%
    filter(autonomy_level >= 4) %>%
    summarise(Count = n(), 
              Percentage = round(Count/nrow(valid_data)*100, 1))
  
  # Create a visualization of autonomy levels - with error checking for required variables
  if (all(c("Level", "Percentage") %in% names(autonomy_dist))) {
    autonomy_viz <- ggplot(autonomy_dist, aes(x = reorder(Level, autonomy_level), y = Percentage)) +
      geom_bar(stat = "identity", fill = "steelblue", width = 0.7) +
      geom_text(aes(label = paste0(Percentage, "%")), vjust = -0.5, size = 4) +
      labs(title = "Distribution of Autonomy Levels",
           subtitle = paste0(high_autonomy$Percentage, "% of apprentices report high levels of autonomy (4-5)"),
           x = "Autonomy Level", 
           y = "Percentage of Apprentices") +
      theme_minimal() +
      theme(axis.text.x = element_text(angle = 0, hjust = 0.5),
            plot.title = element_text(face = "bold"),
            plot.subtitle = element_text(face = "italic"))
    
    # Display visualization
    print(autonomy_viz)
  } else {
    message("Could not create autonomy level visualization due to missing columns")
  }
  
  # Create a table of autonomy levels
  autonomy_table <- kable(autonomy_dist %>% select(Level, Count, Percentage), 
        caption = "Distribution of Autonomy Levels") %>%
    kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
  
  # Display table
  autonomy_table
  
  # Calculate descriptive statistics with robust error handling
  stats_df <- valid_data %>%
    summarise(across(c(autonomy_level, employer_support_score, university_support_score, 
                      self_directed_learning_score, skill_application_score, 
                      learning_integration_score),
                   list(
                     mean = ~mean(., na.rm = TRUE),
                     median = ~median(., na.rm = TRUE),
                     min = ~min(., na.rm = TRUE),
                     max = ~max(., na.rm = TRUE),
                     n = ~sum(!is.na(.))
                   ))) %>%
    pivot_longer(cols = everything(), 
               names_to = c("variable", "stat"),
               names_pattern = "(.*)_(.*)") %>%
    pivot_wider(names_from = stat, values_from = value) %>%
    mutate(Variable = case_when(
      variable == "autonomy_level" ~ "Autonomy Level",
      variable == "employer_support_score" ~ "Employer Support",
      variable == "university_support_score" ~ "University Support",
      variable == "self_directed_learning_score" ~ "Self-Directed Learning",
      variable == "skill_application_score" ~ "Skill Application",
      variable == "learning_integration_score" ~ "Learning Integration",
      TRUE ~ variable
    ))
  
  # Display descriptive statistics with enhanced formatting
  stats_table <- kable(stats_df %>% select(Variable, mean, median, min, max, n), 
        caption = "Descriptive Statistics", 
        digits = 2) %>%
    kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
    column_spec(1, bold = TRUE) %>%
    add_header_above(c(" " = 1, "Central Tendency" = 2, "Range" = 2, "Count" = 1))
  
  # Display table
  stats_table
  
}, error = function(e) {
  # Display what went wrong and provide a fallback
  message("Error in autonomy-levels chunk: ", e$message)
  
  # Create a simple fallback table
  simple_df <- data.frame(
    Message = c("Error processing autonomy level data. This could be due to:"),
    Details = c(
      "- Missing or invalid autonomy_level column",
      "- Data format issues",
      "- Empty dataset after filtering"
    )
  )
  
  # Display error information
  kable(simple_df, caption = "Error in Autonomy Levels Analysis") %>%
    kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
})
Descriptive Statistics
Central Tendency
Range
Count
Variable mean median min max n
Autonomy Level 3.43 4.0 1 5.0 21
Employer Support 2.80 3.0 1 4.2 21
University Support 2.88 3.2 1 5.0 21
Self-Directed Learning 3.06 3.1 1 5.0 21
Skill Application 2.96 3.2 1 4.5 20
Learning Integration 2.69 2.8 1 4.2 20
# Safely handle the autonomy quotes section - completely separate from the section above
tryCatch({
  # Find relevant authentic quotes about autonomy and support
  autonomy_quotes <- all_qual_data %>%
    filter(
      # Select relevant quotes based on content or question context
      str_detect(tolower(response), "autonomy|independence|freedom|support|help") |
      str_detect(tolower(question), "autonomy|support|help|learning")
    ) %>%
    mutate(source_id = paste0(source, "-", respondent_id)) %>%
    select(source_id, question, response) %>%
    rename(
      "Respondent ID" = source_id,
      "Question Context" = question,
      "Authentic Response" = response
    ) %>%
    # Use a safer way to sort by response length
    mutate(resp_length = nchar(as.character(`Authentic Response`))) %>%
    arrange(desc(resp_length)) %>%
    select(-resp_length) %>%
    slice_head(n = 5)  # Take at most 5 relevant quotes
  
  # Make sure we have quotes to display
  if (nrow(autonomy_quotes) > 0) {
    # Display authentic quotes
    quotes_table <- kable(autonomy_quotes, 
          caption = "Authentic Responses Related to Autonomy and Support") %>%
      kable_styling(bootstrap_options = c("striped", "hover")) %>%
      column_spec(3, width = "60%") %>%
      row_spec(1:nrow(autonomy_quotes), background = "#f9f9f9")
    
    # Display quotes table
    quotes_table
  } else {
    message("No relevant autonomy quotes found in the data")
    
    # Display placeholder if no quotes found
    kable(data.frame(Note = "No relevant quotes about autonomy or support found in the qualitative data"),
          caption = "Autonomy Quotes Not Found") %>%
      kable_styling(bootstrap_options = c("striped", "hover"))
  }
}, error = function(e) {
  message("Error processing autonomy quotes: ", e$message)
  
  # Display placeholder when quotes processing fails
  kable(data.frame(Error = "Could not process qualitative quotes", 
                  Details = paste("Error:", e$message)),
        caption = "Error Finding Autonomy Quotes") %>%
    kable_styling(bootstrap_options = c("striped", "hover"))
})
Authentic Responses Related to Autonomy and Support
Respondent ID Question Context Authentic Response
Alumni Q14-2 What.recommendations.would.you.make.for.improving.employer.support. While i had great support from my employer speaking to fellow students they didnt all get that
Current Student Q14-1 Work.based.learning need help to identify project suitability as got so much on
Alumni Q14-1 What.recommendations.would.you.make.for.improving.employer.support. Employers need to be more active in the GA programme

Our quantitative analysis reveals that many graduate apprentices report high levels of autonomy (levels 4-5 on a 5-point scale), while the average self-directed learning score suggests a potential gap between the prevalence of autonomy and apprentices’ effectiveness in utilizing it.

When we examine the authentic qualitative responses, we see evidence of support needs in statements like “need help to identify project suitability as got so much on” and “understand til i go to apply, different approach for me”. These genuine responses suggest that apprentices face challenges in applying their knowledge independently and may require structured guidance despite having autonomy.

Researcher Interpretation

While the qualitative data doesn’t directly articulate an “autonomy paradox” in the exact terms initially conceptualized, we can observe evidence that apprentices simultaneously experience considerable independence while expressing needs for guidance and support. The qualitative responses indicate specific areas where support is needed, such as project selection, application of theory to practice, and structured communication between stakeholders.

The responses align with aspects of the autonomy paradox concept, though perhaps not as explicitly as the quantitative data might suggest. This highlights the importance of not over-interpreting qualitative data to fit preconceived theoretical frameworks.

Key Insight 2: The Role of Employer Support

# Wrap entire chunk in tryCatch for robustness
tryCatch({
  # Define medians for quadrant analysis
  autonomy_median <- median(valid_data$autonomy_level, na.rm = TRUE)
  support_median <- median(valid_data$employer_support_score, na.rm = TRUE)
  
  # Create quadrant variable
  valid_data <- valid_data %>%
    mutate(quadrant = case_when(
      autonomy_level >= autonomy_median & employer_support_score >= support_median ~ "High Autonomy + High Support",
      autonomy_level >= autonomy_median & employer_support_score < support_median ~ "High Autonomy + Low Support",
      autonomy_level < autonomy_median & employer_support_score >= support_median ~ "Low Autonomy + High Support",
      autonomy_level < autonomy_median & employer_support_score < support_median ~ "Low Autonomy + Low Support",
      TRUE ~ NA_character_
    ))
  
  # Add quadrant colors for consistent visualization
  quadrant_colors <- c(
    "High Autonomy + High Support" = "#4DAF4A",  # Green
    "High Autonomy + Low Support" = "#E41A1C",   # Red
    "Low Autonomy + High Support" = "#377EB8",   # Blue
    "Low Autonomy + Low Support" = "#FF7F00"     # Orange
  )
  
  # Calculate quadrant statistics
  quadrant_stats <- valid_data %>%
    filter(!is.na(quadrant)) %>%
    group_by(quadrant) %>%
    summarise(
      Count = n(),
      `Self-Directed Learning (Mean)` = mean(self_directed_learning_score, na.rm = TRUE),
      `Skill Application (Mean)` = mean(skill_application_score, na.rm = TRUE),
      `Learning Integration (Mean)` = mean(learning_integration_score, na.rm = TRUE)
    ) %>%
    ungroup() %>%
    mutate(across(where(is.numeric), ~ round(., 2)))
  
  # Add percentage column
  quadrant_stats <- quadrant_stats %>%
    mutate(Percentage = round(Count / sum(Count) * 100, 1))
  
  # Create a quadrant statistics table with enhanced styling
  quadrant_table <- kable(quadrant_stats %>% 
                            select(quadrant, Count, Percentage, 
                                   `Self-Directed Learning (Mean)`, 
                                   `Skill Application (Mean)`, 
                                   `Learning Integration (Mean)`), 
        caption = "Table 2: Learning Outcomes by Autonomy-Support Combinations",
        col.names = c("Quadrant", "Count", "%", "Self-Directed Learning", 
                      "Skill Application", "Learning Integration")) %>%
    kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
    row_spec(which(quadrant_stats$quadrant == "High Autonomy + High Support"), 
             background = "#e6ffed") %>%  # Highlight High Autonomy + High Support row
    column_spec(1, bold = TRUE) %>%
    column_spec(4:6, background = "#f9f9f9") %>%
    add_header_above(c(" " = 3, "Learning Outcomes (Mean Scores)" = 3)) %>%
    footnote(general = paste0("Note: Sample size = ", nrow(valid_data), " graduate apprentices. ",
                             "Median values used as split points: Autonomy = ", autonomy_median, 
                             ", Employer Support = ", support_median, "."),
             footnote_as_chunk = TRUE)
  
  # Create a visualization of the quadrant analysis
  quadrant_plot_data <- quadrant_stats %>%
    pivot_longer(cols = c(`Self-Directed Learning (Mean)`, `Skill Application (Mean)`, `Learning Integration (Mean)`),
                 names_to = "Measure", values_to = "Score") %>%
    mutate(Measure = gsub(" \\(Mean\\)", "", Measure))
    
  # Display the table
  quadrant_table
}, error = function(e) {
  message("Error in quadrant analysis: ", e$message)
  
  # Create a fallback display
  kable(data.frame(
    Error = "Could not perform quadrant analysis",
    Details = paste("Error message:", e$message)
  ), caption = "Error in Quadrant Analysis") %>%
    kable_styling(bootstrap_options = c("striped", "hover"))
})
Table 2: Learning Outcomes by Autonomy-Support Combinations
Learning Outcomes (Mean Scores)
Quadrant Count % Self-Directed Learning Skill Application Learning Integration
High Autonomy + High Support 5 23.8 3.76 3.88 3.36
High Autonomy + Low Support 8 38.1 2.75 2.20 2.14
Low Autonomy + High Support 6 28.6 3.35 3.43 3.10
Low Autonomy + Low Support 2 9.5 1.65 1.85 1.70
Note: Note: Sample size = 21 graduate apprentices. Median values used as split points: Autonomy = 4, Employer Support = 3.