############################################################################ # MLwiN User Manual # # 18 Modelling Cross-classified Data . . . . . . . . . . . . . . . . . .271 # # Rasbash, J., Steele, F., Browne, W. J. and Goldstein, H. (2012). # A User's Guide to MLwiN, v2.26. Centre for Multilevel Modelling, # University of Bristol. ############################################################################ # R script to replicate all analyses using R2MLwiN # # Zhang, Z., Charlton, C., Parker, R, Leckie, G., and Browne, W.J. # Centre for Multilevel Modelling, 2012 # http://www.bristol.ac.uk/cmm/software/R2MLwiN/ ############################################################################ library(R2MLwiN) # MLwiN folder mlwin <- getOption("MLwiN_path") while (!file.access(mlwin, mode = 1) == 0) { cat("Please specify the root MLwiN folder or the full path to the MLwiN executable:\n") mlwin <- scan(what = character(0), sep = "\n") mlwin <- gsub("\\", "/", mlwin, fixed = TRUE) } options(MLwiN_path = mlwin) # 18.1 An introduction to cross-classification . . . . . . . . . . . . . 271 # 18.2 How cross-classified models are implemented in MLwiN . . . . . . .273 # 18.3 Some computational considerations . . . . . . . . . . . . . . . . 273 # 18.4 Modelling a two-way classification: An example . . . . . . . . . .275 data(xc, package = "R2MLwiN") summary(xc) sid_dummy <- model.matrix(~factor(xc$sid) - 1) sid_dummy_names <- paste0("s", 1:19) colnames(sid_dummy) <- sid_dummy_names xc <- cbind(xc, sid_dummy) covmatrix <- NULL for (i in 1:19) { for (j in 1:i) { if (i != j) { covmatrix <- cbind(covmatrix, c(3, sid_dummy_names[j], sid_dummy_names[i])) } } } random.ui <- matrix(0, 21, 18) random.ui[1, ] <- 1 random.ui[2:19, ] <- diag(18) * -1 random.ci <- rep(0, 18) (mymode1 <- runMLwiN(attain ~ 1 + (s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15 + s16 + s17 + s18 + s19 | cons) + (1 | pid) + (1 | pupil), estoptions = list(clre = covmatrix, constraints = list(random.ui = random.ui, random.ci = random.ci)), data = xc)) # 18.5 Other aspects of the SETX command . . . . . . . . . . . . . . . . 277 sid_inter <- sid_dummy * xc$vrq sid_inter_names <- paste0("s",1:19, "Xvrq") colnames(sid_inter) <- sid_inter_names xc <- cbind(xc, sid_inter) sid_names <- c(sid_dummy_names, sid_inter_names) covmatrix <- NULL for (i in 1:38) { for (j in 1:i) { if (i != j) { covmatrix <- cbind(covmatrix, c(3, sid_names[j], sid_names[i])) } } } random.ui <- matrix(0, 40, 36) random.ui[1, 1:18] <- 1 random.ui[2:19, 1:18] <- diag(18) * -1 random.ui[20, 19:36] <- 1 random.ui[21:38, 19:36] <- diag(18) * -1 random.ci <- rep(0, 36) (mymodel2 <- runMLwiN(attain ~ 1 + (s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15 + s16 + s17 + s18 + s19 + s1Xvrq + s2Xvrq + s3Xvrq + s4Xvrq + s5Xvrq + s6Xvrq + s7Xvrq + s8Xvrq + s9Xvrq + s10Xvrq + s11Xvrq + s12Xvrq + s13Xvrq + s14Xvrq + s15Xvrq + s16Xvrq + s17Xvrq + s18Xvrq + s19Xvrq | cons) + (1 | pid) + (1 | pupil), estoptions = list(clre = covmatrix, constraints = list(random.ui = random.ui, random.ci = random.ci)), data = xc)) # Note: The final models in this section of the manual are for demonstration only. # The models presented in the manual do not converge with the current data. # We have therefore not given the R2MLwiN commands for these models. # 18.6 Reducing storage overhead by grouping . . . . . . . . . . . . . . 279 findClust <- function(data, var1, var2) { grplist <- by(xc, xc$sid, function(x) x) moreclust <- TRUE while (moreclust) { found <- FALSE for (i in (length(grplist) - 1):1) { if (length(grplist) == 1) { moreclust <- FALSE break } for (j in length(grplist):(i + 1)) { if (length(intersect(grplist[[i]][[var2]], grplist[[j]][[var2]])) != 0) { grplist[[i]] <- rbind(grplist[[i]], grplist[[j]]) grplist[[j]] <- NULL found <- TRUE } } } if (found) { break } else { moreclust <- FALSE } } cat(paste0("Number of clusters: ", length(grplist), "\n")) ids <- NULL for (i in 1:length(grplist)) { ids <- rbind(ids, cbind(unique(grplist[[i]][[var1]]), i)) } colnames(ids) <- c(var1, "id") merge(data[, c(var1, var2)], ids, sort = FALSE)$id } if (!require(doBy)) { warning("package doBy required to run this example") } else { data(xc, package = "R2MLwiN") xc$region <- findClust(xc, "sid", "pid") xc$region <- NULL numchildren <- summaryBy(cons ~ sid + pid, FUN = length, data = xc) colnames(numchildren) <- c("sid", "pid", "numchildren") xc <- merge(xc, numchildren, sort = FALSE) xc <- xc[order(xc$sid, xc$pid), ] xc <- xc[xc$numchildren > 2, ] xc$region <- findClust(xc, "sid", "pid") xc <- xc[order(xc$region, xc$sid), ] xc$rsid <- rep(1, nrow(xc)) rgrp <- xc$region[1] sgrp <- xc$sid[1] id <- 1 for (i in 2:nrow(xc)) { if (xc$region[i] != rgrp) { rgrp <- xc$region[i] sgrp <- xc$sid[i] id <- 1 } if (xc$sid[i] != sgrp) { sgrp <- xc$sid[i] id <- id + 1 } xc$rsid[i] <- id } rs_dummy <- model.matrix(~factor(xc$rsid) - 1) rs_dummy_names <- paste0("rs", 1:8) colnames(rs_dummy) <- rs_dummy_names xc <- cbind(xc, rs_dummy) covmatrix <- NULL for (i in 1:8) { for (j in 1:i) { if (i != j) { covmatrix <- cbind(covmatrix, c(3, rs_dummy_names[j], rs_dummy_names[i])) } } } random.ui <- matrix(0, 10, 7) random.ui[1, ] <- 1 random.ui[2:8, ] <- diag(7) * -1 random.ci <- rep(0, 7) xc <- xc[order(xc$region, xc$pid, xc$pupil), ] (mymode1 <- runMLwiN(attain ~ 1 + (rs1 + rs2 + rs3 + rs4 + rs5 + rs6 + rs7 + rs8 | region) + (1 | pid) + (1 | pupil), estoptions = list(clre = covmatrix, constraints = list(random.ui = random.ui, random.ci = random.ci)), data = xc)) } # 18.7 Modelling a multi-way cross-classification . . . . . . . . . . . .280 # 18.8 MLwiN commands for cross-classifications . . . . . . . . . . . . .281 # Chapter learning outcomes . . . . . . . . . . . . . . . . . . . . . . .282 ############################################################################