###############
#DOWNLOAD DATA#
###############

# Define the URL and destination folder
url <- "https://webfs.oecd.org/pisa2022/STU_QQQ_SPSS.zip"
dest_dir <- "C:/" #Choose a destiny to download the database
zip_file <- file.path(dest_dir, "STU_QQQ_SPSS.zip")

# Create the destination directory if it doesn't exist
if (!dir.exists(dest_dir)) {
  dir.create(dest_dir, recursive = TRUE)
}

# Download the ZIP file
download.file(url, destfile = zip_file, mode = "wb")

# Unzip the file
unzip(zip_file, exdir = dest_dir)

# Remove the ZIP file after extraction
file.remove(zip_file)
cat("File downloaded and unzipped successfully to:", dest_dir, "\n")


###########
# 0. OPEN #
###########
if (!require("haven")) install.packages("haven")
library(haven)
file_path <- "C:/CY08MSP_STU_QQQ.SAV"
dataPropensityScore <- read_sav(file_path)

#Select Spain
if (!require("dplyr")) install.packages("dplyr")
library(dplyr)
dataSpain <- dataPropensityScore %>%
  filter(CNT == "ESP")

#Select only with information in Repeat
dataSpain <- dataSpain %>%
  filter(!is.na(REPEAT))

#Recode and rename
dataSpain <- dataSpain %>%
  mutate(ST004D01T = case_when(
    ST004D01T == 2 ~ 0,  # Male becomes 0
    ST004D01T == 1 ~ 1,  # Female becomes 1
  ))

dataSpain <- dataSpain %>%
  mutate(IMMIG = case_when(
    IMMIG == 3 ~ 1,          # First-Generation → 1
    IMMIG %in% c(1, 2) ~ 0,  # Native & Second-Generation → 0
  ))

dataSpain <- dataSpain %>%
  mutate(ST022Q01TA = case_when(
    ST022Q01TA == 2 ~ 1,  # Other language → 1
    ST022Q01TA == 1 ~ 0,  # Language of the test → 0
  ))

dataSpain <- dataSpain %>%
  rename(
    Early_education = DURECEC,
    Month_of_birth = ST003D02T,
    Gender = ST004D01T,
    Migratory_status = IMMIG,
    Home_language = ST022Q01TA,
    Self_efficacy = MATHEFF
  )

dataSpain <- dataSpain %>%
  mutate(
    ESCS = as.numeric(ESCS),              
    Month_of_birth = as.numeric(Month_of_birth),    
    Gender = as.numeric(Gender),     
    Home_language = as.numeric(Home_language),   
    Early_education = as.numeric(Early_education),        
    Migratory_status = as.numeric(Migratory_status),             
    Self_efficacy = as.numeric(Self_efficacy)         
  )

########################
# REPLACE MISSING DATA #
########################

if (!require("mice")) install.packages("mice")
library(mice)

# Define the dataset and variables for imputation
vars_to_impute <- c("ESCS", "Home_language", "Early_education", "Migratory_status", "Self_efficacy")

# Create a custom method vector
method <- make.method(dataSpain)
method[!(names(method) %in% vars_to_impute)] <- ""  
method[vars_to_impute] <- "pmm" 

# Create a custom predictor matrix
pred <- make.predictorMatrix(dataSpain)
pred[!(rownames(pred) %in% vars_to_impute), ] <- 0  # Prevent imputing non-target variables
pred[, !(colnames(pred) %in% vars_to_impute)] <- 0  # Exclude non-target variables as predictors

# Impute missing data
mice_result <- mice(
  data = dataSpain,
  method = method,
  predictorMatrix = pred,
  m = 5,        # Number of imputations
  maxit = 5,    # Number of iterations
  seed = 123    # Set seed for reproducibility
)

# Extract the completed dataset
miceDataSpain <- complete(mice_result, 1)  # Extract the first imputed dataset

# Verify the imputed dataset
summary(miceDataSpain[vars_to_impute])        # Summary of imputed variables
colSums(is.na(miceDataSpain[vars_to_impute])) # Check for remaining missing values

# Count the number of non-missing observations for each variable
n_miceDataSpain <- sapply(vars_to_impute, function(var) {
  sum(!is.na(miceDataSpain[[var]]))  # Count non-missing values
})

# Display the counts
n_miceDataSpain

##################
# 1. DESCRIPTIVE #
##################

# Plausible values
plausible_values_math <- paste0("PV", 1:10, "MATH")
plausible_values_scie <- paste0("PV", 1:10, "SCIE")
plausible_values_read <- paste0("PV", 1:10, "READ")

# Aditional variables
additional_variables <- c(
  "ESCS",
  "Month_of_birth",  
  "Gender",  
  "Home_language", 
  "Early_education",    
  "Migratory_status",      
  "Self_efficacy"   
)

# Weighted means by REPEAT
mean_by_repeat <- lapply(split(miceDataSpain, miceDataSpain$REPEAT), function(group) {
  
  # Plausible values
  math_means <- sapply(plausible_values_math, function(pv) {
    weighted.mean(group[[pv]], group$W_FSTUWT, na.rm = TRUE)
  })
  
  scie_means <- sapply(plausible_values_scie, function(pv) {
    weighted.mean(group[[pv]], group$W_FSTUWT, na.rm = TRUE)
  })
  
  read_means <- sapply(plausible_values_read, function(pv) {
    weighted.mean(group[[pv]], group$W_FSTUWT, na.rm = TRUE)
  })
  
  # Means
  overall_means <- list(
    MATH = mean(math_means, na.rm = TRUE),
    SCIE = mean(scie_means, na.rm = TRUE),
    READ = mean(read_means, na.rm = TRUE)
  )
  additional_means <- sapply(additional_variables, function(var) {
    weighted.mean(group[[var]], group$W_FSTUWT, na.rm = TRUE)
  })
  list(
    MATH = math_means,
    SCIE = scie_means,
    READ = read_means,
    Overall = overall_means,
    Additional = additional_means
  )
})

# Retained vs. non-retained
n_retained <- sum(miceDataSpain$REPEAT == 1, na.rm = TRUE)
n_non_retained <- sum(miceDataSpain$REPEAT == 0, na.rm = TRUE)

# Extract results from mean_by_repeat
retained_data <- mean_by_repeat[["1"]]
non_retained_data <- mean_by_repeat[["0"]]

# Create a data frame
Table.2 <- data.frame(
  Variable = c(
    "N",
    "ESCS",
    "Early education",
    "Migratory status",
    "Month of birth",
    "Gender",
    "Home language",
    "Math",
    "Science",
    "Reading",
    "Self-efficacy"
  ),
  Retained = c(
    n_retained,
    round(retained_data$Additional["ESCS"], 2),
    round(retained_data$Additional["Early_education"], 2),
    round(retained_data$Additional["Migratory_status"], 2),
    round(retained_data$Additional["Month_of_birth"], 2),
    round(retained_data$Additional["Gender"], 2),
    round(retained_data$Additional["Home_language"], 2),
    round(retained_data$Overall[["MATH"]], 2),
    round(retained_data$Overall[["SCIE"]], 2),
    round(retained_data$Overall[["READ"]], 2),
    round(retained_data$Additional["Self_efficacy"], 2)
  ),
  `Non-retained` = c(
    n_non_retained,
    round(non_retained_data$Additional["ESCS"], 2),
    round(non_retained_data$Additional["Early_education"], 2),
    round(non_retained_data$Additional["Migratory_status"], 2),
    round(non_retained_data$Additional["Month_of_birth"], 2),
    round(non_retained_data$Additional["Gender"], 2),
    round(non_retained_data$Additional["Home_language"], 2),
    round(non_retained_data$Overall[["MATH"]], 2),
    round(non_retained_data$Overall[["SCIE"]], 2),
    round(non_retained_data$Overall[["READ"]], 2),
    round(non_retained_data$Additional["Self_efficacy"], 2)
  ),
  stringsAsFactors = FALSE
)

# Print the summary table
print(Table.2)

##################################
# 2. PROPENSITY SCORE ESTIMATION #
##################################

# Testing models
# Full
if (!require("MatchIt")) install.packages("MatchIt")
library(MatchIt)

fpsm.PS = matchit(REPEAT ~ ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language, 
                  data = miceDataSpain, method = "quick", 
                  distance = "glm", s.weights = miceDataSpain$W_FSTUWT)
fpsm.Data <- match.data(fpsm.PS)

# Nearest ratio 1
nearest.PS1 = matchit(
  REPEAT ~ ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language, 
  data = miceDataSpain, 
  method = "nearest", 
  distance = "glm", 
  ratio = 1,
  s.weights = miceDataSpain$W_FSTUWT)
nearest.Data <- match.data(nearest.PS1)

# Caliper 0.1
nearest.01 <- matchit(
  REPEAT ~ ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language, 
  data = miceDataSpain,
  method = "nearest", 
  distance = "glm",
  s.weights = miceDataSpain$W_FSTUWT,
  caliper = 0.1,        
  std.caliper = TRUE)
 nearest.01.Data <- match.data(nearest.01)

# Caliper 0.2
nearest.02 <- matchit(
  REPEAT ~ ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language, 
  data = miceDataSpain,
  method = "nearest", 
  distance = "glm",
  s.weights = miceDataSpain$W_FSTUWT,
  caliper = 0.2,        
  std.caliper = TRUE)
nearest.02.Data <- match.data(nearest.02)

# Caliper 0.3
nearest.03 <- matchit(
  REPEAT ~ ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language, 
  data = miceDataSpain,
  method = "nearest", 
  distance = "glm",
  s.weights = miceDataSpain$W_FSTUWT,
  caliper = 0.3,        
  std.caliper = TRUE)
nearest.03.Data <- match.data(nearest.03)

# Caliper 0.4
nearest.04 <- matchit(
  REPEAT ~ ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language, 
  data = miceDataSpain,
  method = "nearest", 
  distance = "glm",
  s.weights = miceDataSpain$W_FSTUWT,
  caliper = 0.4,        
  std.caliper = TRUE)
nearest.04.Data <- match.data(nearest.04)

# Caliper 0.5
nearest.05 <- matchit(
  REPEAT ~ ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language, 
  data = miceDataSpain,
  method = "nearest", 
  distance = "glm",
  s.weights = miceDataSpain$W_FSTUWT,
  caliper = 0.5,     
  std.caliper = TRUE)
nearest.05.Data <- match.data(nearest.05)

# Nearest ratio 2
nearest.PS2 = matchit(
  REPEAT ~ ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language, 
  data = miceDataSpain, 
  method = "nearest", 
  distance = "glm", 
  ratio = 2,
  s.weights = miceDataSpain$W_FSTUWT)
nearest.Data2 <- match.data(nearest.PS2)

# Show results
# Using summary 
get_matched_info <- function(m_obj, treat_var = "REPEAT") {
  s <- summary(m_obj, un = FALSE)
  df_matched <- match.data(m_obj)
  n_treated <- sum(df_matched[[treat_var]] == 1)
  n_control <- sum(df_matched[[treat_var]] == 0)
  vars <- c("ESCS", "Early_education", "Migratory_status", "Month_of_birth", "Gender", "Home_language")
  matched_vars <- intersect(vars, rownames(s$sum.matched))
  means_treated <- round(s$sum.matched[matched_vars, "Means Treated"], 2)
  means_control <- round(s$sum.matched[matched_vars, "Means Control"], 2)
  list(
    n_treated = n_treated,
    n_control = n_control,
    means_treated = means_treated,
    means_control = means_control
  )
}

res_nearest.PS <- get_matched_info(fpsm.PS)    
res_nearest.01 <- get_matched_info(nearest.01)
res_nearest.02 <- get_matched_info(nearest.02)
res_nearest.03 <- get_matched_info(nearest.03)
res_nearest.04 <- get_matched_info(nearest.04)
res_nearest.05 <- get_matched_info(nearest.05)
res_nearest.1 <- get_matched_info(nearest.PS1)
res_nearest.2 <- get_matched_info(nearest.PS2)

match_results <- list(
  "Full" = res_nearest.PS,
  "Nearest1:1" = res_nearest.1,
  "Cal.0.1" = res_nearest.01,
  "Cal.0.2" = res_nearest.02,
  "Cal.0.3" = res_nearest.03,
  "Cal.0.4" = res_nearest.04,
  "Cal.0.5" = res_nearest.05,
  "Nearest1:2" = res_nearest.2
)

N_treated_row <- sapply(match_results, function(x) x$n_treated)
var_order <- c("ESCS", "Early_education", "Migratory_status", "Month_of_birth", "Gender", "Home_language")
means_treated_matrix <- sapply(match_results, function(x) x$means_treated[var_order])
rownames(means_treated_matrix) <- c("ESCS", 
                                    "Early educ.",    
                                    "Migrat. status", 
                                    "Month of birth", 
                                    "Gender",         
                                    "Home lang.")

final_table_treated <- rbind(N = N_treated_row, means_treated_matrix)
colnames(final_table_treated) <- c("Full", "Nearest 1:1", "Cal. 0.1", "Cal. 0.2", "Cal. 0.3", 
                                   "Cal. 0.4", "Cal. 0.5", 
                                   "Nearest 1:2")

N_control_row <- sapply(match_results, function(x) x$n_control)

means_control_matrix <- sapply(match_results, function(x) x$means_control[var_order])

rownames(means_control_matrix) <- c("ESCS", 
                                    "Early educ.",    
                                    "Migrat. status", 
                                    "Month of birth", 
                                    "Gender",         
                                    "Home lang.")

Table.3 <- rbind(N = N_control_row, means_control_matrix)
colnames(Table.3) <- c("Full", "Nearest 1:1", "Cal. 0.1", "Cal. 0.2", "Cal. 0.3", 
                                   "Cal. 0.4", "Cal. 0.5", 
                                    "Nearest 1:2")

final_table_treated
Table.3

#Graphical testing

#Distribution
x11(width = 12, height = 8)
plot(
  nearest.PS1, 
  type = "density", 
  main = "Propensity Score Distribution"
)

# Balance Plot
graf <- summary(nearest.PS1, interactions = TRUE)
x11(width = 12, height = 8)
plot(
  graf,
  abs       = FALSE, 
  main      = "Balance Summary",
)

#Weights with decay
head(nearest.Data$weights)
head(nearest.Data$distance)
x11(width = 12, height = 8) 
plot(nearest.Data$distance, nearest.Data$weights,
     xlab = "Distance",
     ylab = "Weights",
     main = "Weight Decay with Distance",
     pch = 19, col = "blue")
lines(lowess(nearest.Data$distance, nearest.Data$weights), col = "red", lwd = 2)
summary(nearest.Data$weights)
summary(nearest.Data$distance)

################################################
# 3. DESCRIPTIVE INCLUDING DEPENDENT VARIABLES #
################################################

# Databases with adjusted weights
# New weights match * PISA
# Calculate W_OR
nearest.Data$W_OR <- nearest.Data$weights / nearest.Data$W_FSTUWT

# Loop to calculate W_FSTURWPT1 to W_FSTURWPT80
for (i in 1:80) {
  var_w_fsturwpt <- paste0("W_FSTURWPT", i)
  var_w_fsturwt <- paste0("W_FSTURWT", i)
  nearest.Data[[var_w_fsturwpt]] <- nearest.Data[[var_w_fsturwt]] * nearest.Data$W_OR
}

# Create the survey design object
if (!require("survey")) install.packages("survey")
library(survey)
if (!require("mitools")) install.packages("mitools")
library(mitools)

rep_weights <- nearest.Data[, paste0("W_FSTURWPT", 1:80)]
survey_design <- svrepdesign(
  data = nearest.Data,
  weights = ~weights,
  repweights = rep_weights,
  type = "BRR",
  fay.rho = 0.5
)

# Define variables of interest (plausible values + additional variables)
plausible_values_math <- paste0("PV", 1:10, "MATH")
plausible_values_scie <- paste0("PV", 1:10, "SCIE")
plausible_values_read <- paste0("PV", 1:10, "READ")

additional_variables <- c(
  "ESCS",             # Index of economic, social and cultural status
  "Early_education",  # Early education
  "Migratory_status", # Migratory status
  "Month_of_birth",   # Month of birth
  "Gender",           # Gender
  "Home_language"    # Home language
)

# Create subsets for retained (REPEAT == 1) and non-retained (REPEAT == 0)
subset_repeat1 <- subset(survey_design, REPEAT == 1)  # Retained
subset_repeat0 <- subset(survey_design, REPEAT == 0)  # Non-retained

# Compute N 
n_retained <- sum(nearest.Data$REPEAT == 1, na.rm = TRUE)
n_non_retained <- sum(nearest.Data$REPEAT == 0, na.rm = TRUE)

# Compute average of plausible values 
calc_pv_mean <- function(vars, design) {
  means <- sapply(vars, function(v) {
    as.numeric(coef(svymean(as.formula(paste("~", v)), design, na.rm = TRUE)))
  })
  mean(means, na.rm = TRUE)
}

# Compute mean of a single variable 
calc_mean <- function(var, design) {
  as.numeric(coef(svymean(as.formula(paste("~", var)), design, na.rm = TRUE)))
}

# Compute means for Retained group (REPEAT == 1)
ret_ESCS       <- calc_mean("ESCS",             subset_repeat1)
ret_early      <- calc_mean("Early_education",  subset_repeat1)
ret_imm        <- calc_mean("Migratory_status", subset_repeat1)
ret_month      <- calc_mean("Month_of_birth",   subset_repeat1)
ret_gender     <- calc_mean("Gender",           subset_repeat1)
ret_home       <- calc_mean("Home_language",    subset_repeat1)
ret_selfeff    <- calc_mean("Self_efficacy",    subset_repeat1)

ret_math       <- calc_pv_mean(plausible_values_math, subset_repeat1)
ret_scie       <- calc_pv_mean(plausible_values_scie, subset_repeat1)
ret_read       <- calc_pv_mean(plausible_values_read, subset_repeat1)

# Compute means for Non-retained group (REPEAT == 0)
nonret_ESCS    <- calc_mean("ESCS",             subset_repeat0)
nonret_early   <- calc_mean("Early_education",  subset_repeat0)
nonret_imm     <- calc_mean("Migratory_status", subset_repeat0)
nonret_month   <- calc_mean("Month_of_birth",   subset_repeat0)
nonret_gender  <- calc_mean("Gender",           subset_repeat0)
nonret_home    <- calc_mean("Home_language",    subset_repeat0)
nonret_selfeff <- calc_mean("Self_efficacy",    subset_repeat0)

nonret_math    <- calc_pv_mean(plausible_values_math, subset_repeat0)
nonret_scie    <- calc_pv_mean(plausible_values_scie, subset_repeat0)
nonret_read    <- calc_pv_mean(plausible_values_read, subset_repeat0)

# Create a data frame summarizing both groups
Table.4 <- data.frame(
  Variable = c(
    "N",
    "Math",
    "Science",
    "Reading",
    "Self-efficacy",    
    "ESCS",
    "Early education",
    "Migratory status",
    "Month of birth",
    "Gender",
    "Home language"
  ),
  Retained = c(
    n_retained,
    round(ret_math, 2),
    round(ret_scie, 2),
    round(ret_read, 2),
    round(ret_selfeff, 2),
    round(ret_ESCS, 2),
    round(ret_early, 2),
    round(ret_imm, 2),
    round(ret_month, 2),
    round(ret_gender, 2),
    round(ret_home, 2)
  ),
  `Non-retained` = c(
    n_non_retained,
    round(nonret_math, 2),
    round(nonret_scie, 2),
    round(nonret_read, 2),
    round(nonret_selfeff, 2),
    round(nonret_ESCS, 2),
    round(nonret_early, 2),
    round(nonret_imm, 2),
    round(nonret_month, 2),
    round(nonret_gender, 2),
    round(nonret_home, 2)
  ),
  stringsAsFactors = FALSE
)

# Print the summary table
print(Table.4)

# Effect size: Cohen's d
calculate_cohens_d <- function(nearest.Data, dependent_var, group_var) {
  group_1 <- nearest.Data[[dependent_var]][nearest.Data[[group_var]] == 1]
  group_0 <- nearest.Data[[dependent_var]][nearest.Data[[group_var]] == 0]
  
  mean_1 <- mean(group_1, na.rm = TRUE)
  mean_0 <- mean(group_0, na.rm = TRUE)
  sd_1 <- sd(group_1, na.rm = TRUE)
  sd_0 <- sd(group_0, na.rm = TRUE)
  
  pooled_sd <- sqrt(((length(group_1) - 1) * sd_1^2 + (length(group_0) - 1) * sd_0^2) / 
                      (length(group_1) + length(group_0) - 2))
  
  (mean_1 - mean_0) / pooled_sd
}

calculate_cohens_d_pvs <- function(nearest.Data, pv_prefix, group_var) {
  pv_vars <- grep(paste0("^", pv_prefix), names(nearest.Data), value = TRUE)
  cohens_d_values <- sapply(pv_vars, function(pv) {
    calculate_cohens_d(nearest.Data, dependent_var = pv, group_var = group_var)
  })
  
  mean_cohens_d <- mean(cohens_d_values, na.rm = TRUE)
  list(cohens_d_values = cohens_d_values, mean_cohens_d = mean_cohens_d)
}

cohens_d_results_math <- calculate_cohens_d_pvs(nearest.Data, pv_prefix = "PV.*MATH", group_var = "REPEAT")
cohens_d_results_scie <- calculate_cohens_d_pvs(nearest.Data, pv_prefix = "PV.*SCIE", group_var = "REPEAT")
cohens_d_results_read <- calculate_cohens_d_pvs(nearest.Data, pv_prefix = "PV.*READ", group_var = "REPEAT")
cohens_d_matheff <- calculate_cohens_d(nearest.Data, dependent_var = "Self_efficacy", group_var = "REPEAT")

Table.5 <- data.frame(
  Domain = c("Math", "Science", "Reading", "Self-Efficacy"),
  Mean_Effect_Size = round(c(
    cohens_d_results_math$mean_cohens_d,
    cohens_d_results_scie$mean_cohens_d,
    cohens_d_results_read$mean_cohens_d,
    cohens_d_matheff
  ), 2)
)

print(Table.5)

#################
# 5 REGRESSIONS #
#################

rep_weights <- nearest.Data[, paste0("W_FSTURWPT", 1:80)]
survey_design <- svrepdesign(
  data       = nearest.Data,
  weights    = ~weights,
  repweights = rep_weights,
  type       = "BRR",
  fay.rho    = 0.5
)

# Define plausible values mapping as a list of formulas
pv_mapping <- list(
  math = pv ~ PV1MATH + PV2MATH + PV3MATH + PV4MATH + PV5MATH +
    PV6MATH + PV7MATH + PV8MATH + PV9MATH + PV10MATH,
  read = pv ~ PV1READ + PV2READ + PV3READ + PV4READ + PV5READ +
    PV6READ + PV7READ + PV8READ + PV9READ + PV10READ,
  scie = pv ~ PV1SCIE + PV2SCIE + PV3SCIE + PV4SCIE + PV5SCIE +
    PV6SCIE + PV7SCIE + PV8SCIE + PV9SCIE + PV10SCIE
)

# regression analyses for Math, Science, Reading
# Math
results_math <- withPV(
  mapping = pv_mapping$math,
  data    = survey_design,
  action  = quote(svyglm(
    pv ~ REPEAT + ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language,
    design = survey_design
  )),
  rewrite = TRUE
)

# Science
results_scie <- withPV(
  mapping = pv_mapping$scie,
  data    = survey_design,
  action  = quote(svyglm(
    pv ~ REPEAT + ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language,
    design = survey_design
  )),
  rewrite = TRUE
)

# Reading
results_read <- withPV(
  mapping = pv_mapping$read,
  data    = survey_design,
  action  = quote(svyglm(
    pv ~ REPEAT + ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language,
    design = survey_design
  )),
  rewrite = TRUE
)

# 4) Combine results 
summary_math <- MIcombine(results_math)
summary_scie <- MIcombine(results_scie)
summary_read <- MIcombine(results_read)

# 5) P-value and CI
print_MIcombine_with_p <- function(MI_obj, alpha = 0.05, digits = 2) {
  coefs  <- MI_obj$coefficients
  ses    <- sqrt(diag(MI_obj$variance))
  dfs    <- MI_obj$df
  
  tvals  <- coefs / ses
  pvals  <- 2 * pt(abs(tvals), df = dfs, lower.tail = FALSE)
  
  crit   <- qt(1 - alpha/2, df = dfs)
  lower  <- coefs - crit * ses
  upper  <- coefs + crit * ses
  
  tab <- data.frame(
    Estimate    = round(coefs, digits),
    Std.Error   = round(ses,   digits),
    df          = round(dfs,   digits),
    t.value     = round(tvals, digits),
    `Pr(>|t|)`  = formatC(pvals, format = "f", digits = digits),
    CI.Lower    = round(lower, digits),
    CI.Upper    = round(upper, digits)
  )
  
  print(tab)
}

# Single regression for self-efficacy
result_matheff <- svyglm(
  Self_efficacy ~ REPEAT + ESCS + Early_education + Migratory_status + Month_of_birth + Gender + Home_language,
  design = survey_design
)

print_svyglm_with_p_ci <- function(model, alpha = 0.05, digits = 2) {
  coefs <- coef(model)
  vcovm <- vcov(model)
  ses   <- sqrt(diag(vcovm))
  
  s     <- summary(model)
  
  if (!is.null(s$degf)) {
    df_model <- s$degf
  } else {
    df_model <- s$df.residual
  }
  
  crit  <- qt(1 - alpha/2, df = df_model)
  lower <- coefs - crit * ses
  upper <- coefs + crit * ses
  
  tab <- data.frame(
    Estimate   = round(coefs, digits),
    Std.Error  = round(ses,   digits),
    df = df_model,
    T.value    = round(s$coefficients[, "t value"], digits),
    p = formatC(s$coefficients[, "Pr(>|t|)"], format = "f", digits = digits),
    CI.Lower   = round(lower, digits),
    CI.Upper   = round(upper, digits)
  )
  
  print(tab)
}

# Print table

Table.6 <- list(
  Plausible_Values = list(
    Math = print_MIcombine_with_p(summary_math, digits = 2),
    Science = print_MIcombine_with_p(summary_scie, digits = 2),
    Reading = print_MIcombine_with_p(summary_read, digits = 2)
  ),
  Single_Model = list(
    Math_Self_Efficacy = print_svyglm_with_p_ci(result_matheff, digits = 2)
  )
)

#####################
# PRINT ALL TABLES #
####################

Table.2
Table.3
Table.4
Table.5
Table.6


