multiEquatError <- function (x1, x2, x3, difBound = 1, dependentDIF =FALSE, testletStr = NULL, verbose=TRUE){
liste <- lapply(list(x1, x2, x3), eatTools::makeDataFrame, verbose = FALSE)
checkmate::assert_numeric(difBound, len = 1)
lapply(c(dependentDIF, verbose), checkmate::assert_logical, len = 1)
checkmate::assert_data_frame(testletStr, ncols = 2, col.names = "named", null.ok = TRUE)
#
if("derived.par" %in% colnames(x1)){
obj <- prepareAndCheckEatModelObject(liste, difBound=difBound, verbose=verbose)
link <- lapply(names(obj), FUN = function (dim ) {
tripleEquatError(e1=obj[[dim]][[1]][,c("item", "est")],
e2=obj[[dim]][[2]][,c("item", "est")],
e3= obj[[dim]][[3]][,c("item", "est")],
dependentDIF=dependentDIF, testletStr=testletStr, difBound=difBound, calledForEatModel = TRUE)})
names(link) <- names(obj)
} else {
link <- tripleEquatError(e1=liste[[1]], e2=liste[[2]], e3=liste[[3]], dependentDIF=dependentDIF,
testletStr=testletStr, difBound=difBound, calledForEatModel = FALSE)
}
return(link)
}
### ----------------------------------------------------------------------------
tripleEquatError <- function(e1, e2, e3, dependentDIF, testletStr, difBound, calledForEatModel) {
chk1 <- checkInputConsistency(e1=e1,e2=e2,e3=e3,testletStr=testletStr)
if (isFALSE(calledForEatModel)) {
chk2 <- checkIPD(e1=e1,e2=e2,e3=e3,testletStr=chk1, difBound=difBound)
if ( length(chk2[["e1_weg"]])>0) {e1 <- e1[-which(e1[,1] %in% chk2[["e1_weg"]]),]}
if ( length(chk2[["e3_weg"]])>0) {e3 <- e3[-which(e3[,1] %in% chk2[["e3_weg"]]),]}
}
el21 <- equ.rasch(e2,e1)
el31 <- equ.rasch(e3,e1)
el32 <- equ.rasch(e3,e2)
l21 <- el21$descriptives
l31 <- el31$descriptives
l32 <- el32$descriptives
trend3221 <- el32$B.est$Mean.Mean + el21$B.est$Mean.Mean
trend31 <- el31$B.est$Mean.Mean
if(is.null(chk1)) {
if(dependentDIF) {
is3221 <- intersect(el21$anchor$item, el32$anchor$item)
dif21 <- el21$anchor$TransfItempar.Gr1[match(is3221, el21$anchor$item)] - el21$anchor$Itempar.Gr2[match(is3221, el21$anchor$item)]
dif32 <- el32$anchor$TransfItempar.Gr1[match(is3221, el32$anchor$item)] - el32$anchor$Itempar.Gr2[match(is3221, el32$anchor$item)]
cov1223a <- cov(dif21, dif32)
if(cov1223a < 0) {cov1223a <- 0}
cov1223 <- sqrt(cov1223a)/sqrt(length(dif21))
le3221 <- sqrt(l21$linkerror^2 + l32$linkerror^2 - 2*cov1223^2)
} else {
le3221 <- sqrt(l21$linkerror^2 + l32$linkerror^2)
}
le31 <- l31$linkerror
} else {
e12a <- merge(e1, e2, by = "item", all=TRUE)
e12 <- merge(chk1, e12a, by="item", all.x=FALSE, all.y=TRUE)
e12 <- e12[,c(2,3,4,1)]
e13a <- merge(e1, e3, by = "item", all=TRUE)
e13 <- merge(chk1, e13a, by="item", all.x=FALSE, all.y=TRUE)
e13 <- e13[,c(2,3,4,1)]
e23a <- merge(e2, e3, by = "item", all=TRUE)
e23 <- merge(chk1, e23a, by="item", all.x=FALSE, all.y=TRUE)
e23 <- e23[,c(2,3,4,1)]
eli12 <- equa.rasch.jk(e12)
eli13 <- equa.rasch.jk(e13)
eli23 <- equa.rasch.jk(e23)
l12 <- eli12$descriptives$linkerror.jackknife
le31 <- eli13$descriptives$linkerror.jackknife
l23 <- eli23$descriptives$linkerror.jackknife
if(dependentDIF) {
eli12$pars.data[,2] <- eli12$pars.data[,2]+eli12$descriptives$shift
eli23$pars.data[,2] <- eli23$pars.data[,2]+eli23$descriptives$shift
e1223 <- stats::na.omit(merge(eli12$pars.data, eli23$pars.data, by=c("unit","item"),all=TRUE))
e1223$dif12 <- e1223[,3] - e1223[,4]
e1223$dif23 <- e1223[,5] - e1223[,6]
res1 <- NULL
for(un in e1223$unit) {
p1223 <- e1223[e1223[,1] != un,]
res1 <- c(cov(p1223$dif12,p1223$dif23),res1)
}
cov1223a <- mean(res1, na.rm=TRUE)
if(cov1223a < 0) {cov1223a <- 0}
cov1223 <- sqrt(cov1223a)/sqrt(length(p1223$dif12))
if((l12^2 + l23^2 - 2*cov1223^2) < 0) {
le3221 <- sqrt(l12^2 + l23^2)
} else {
le3221 <- sqrt(l12^2 + l23^2 - 2*cov1223^2)
}
} else {
le3221 <- sqrt(l12^2 + l23^2)
}
}
res <- data.frame(trend31 = trend31, trend3221 = trend3221, le31 = le31, le3221 = le3221)
return(res)
}
### ----------------------------------------------------------------------------
prepareAndCheckEatModelObject <- function ( liste, difBound, verbose ) {
uni <- sapply(liste, FUN = function ( x ) { "correlation" %in% x[,"par"] })
chk1 <- which(uni == TRUE)
if ( length(chk1)>0) {stop("Following model(s) do not seem to be unidimensional: ",
paste(chk1, collapse = ", "),".")}
doms <- lapply(liste, FUN = function (o) {na.omit(setdiff(unique(o[,"group"]), "persons"))})
comps<- data.frame(combinat::combn(1:length(doms),2))
chk2 <- sapply(comps, FUN = function (col) {if(!all.equal(doms[[col[1]]], doms[[col[2]]])) {
stop(paste0("Domains between ",col[1]," and ",col[2]," do not match."))}})
its <- lapply(liste, itemFromRes)
dims <- unique(its[[1]][,"dimension"])
its <- lapply(dims, FUN = function (d) { list(t1 = its[[1]][which(its[[1]][,"dimension"] == d),],
t2 = its[[2]][which(its[[2]][,"dimension"] == d),],
t3 = its[[3]][which(its[[3]][,"dimension"] == d),]) })
names(its) <- dims
chk3 <- lapply(dims, FUN = function ( d ) {
bridges <- data.frame(combinat::combn(1:length(its[[d]]),2), stringsAsFactors = FALSE)
bridges <- bridges[,which(sapply(bridges, diff)==1)]
chk4 <- lapply(bridges, FUN = function (b ) {
com<- length(intersect( its[[d]][[b[1]]][,"item"], its[[d]][[b[2]]][,"item"]))
if ( com == 0 ) {stop(paste0("No common items for linking mzp ",
b[1]," to mzp ", b[2]," for domain '",
d,"'."))}
if ( com < 10 ) {cat(paste0("Warning: Only ",com," common items for linking mzp ",
b[1]," to mzp ", b[2]," for domain '",d,"'.\n"))}
})})
ref <- itemFromRes(liste[[1]])
if (verbose) {
eq <- equat1pl(liste[[2]], prmNorm = ref[,c("item", "est")],
difBound = difBound, iterativ = TRUE)
} else {
txt <- capture.output(eq <- equat1pl(liste[[2]], prmNorm = ref[,c("item", "est")],
difBound = difBound, iterativ = TRUE))
}
stru<- lapply(eq[["items"]], names)
for ( d in 1:length(stru)) {
weg <- setdiff(eq[["items"]][[names(stru)[d]]][[stru[[d]]]][["info"]][["itemExcluded"]], "")
if ( length(weg)>0) {
its[[stru[[d]]]][[1]] <- its[[stru[[d]]]][[1]][-match(weg,its[[stru[[d]]]][[1]][,"item"]),]
}
}
ref2<- itemFromRes(liste[[2]])
if (verbose) {
eq <- equat1pl(liste[[3]], prmNorm = ref2[,c("item", "est")],
difBound = difBound, iterativ = TRUE)
} else {
txt <- capture.output(eq <- equat1pl(liste[[3]], prmNorm = ref2[,c("item", "est")],
difBound = difBound, iterativ = TRUE))
}
stru<- lapply(eq[["items"]], names)
for ( d in 1:length(stru)) {
weg <- setdiff(eq[["items"]][[names(stru)[d]]][[stru[[d]]]][["info"]][["itemExcluded"]], "")
if ( length(weg)>0) {
its[[d]][[3]] <- its[[d]][[3]][-match(weg, its[[d]][[3]][,"item"]),]
}
}
return(its)}
### ----------------------------------------------------------------------------
checkInputConsistency <- function(e1,e2,e3,testletStr) {
il <- list(e1, e2, e3)
if(!all(sapply(il, ncol) == 2)) {stop("All thre data.frames must have two columns.")}
if(!all(lapply(il, FUN = function ( x ) { all(colnames(x) == c("item", "est")) })==TRUE)) {
il <- lapply(il, FUN = function ( x ) { colnames(x) <- c("item", "est"); return(x)})
}
it <- lapply(il, FUN = function (x) {
stopifnot ( length(x[,"item"]) == length(unique(x[,"item"])))
return(x[,"item"])})
comp<- as.data.frame(combinat::combn(length(it),2))
chk1<- lapply(comp, FUN = function ( com ){ if (length(intersect(it[[com[1]]], it[[com[2]]])) < 2) {
stop(paste0(length(intersect(it[[com[1]]], it[[com[2]]])) ," common items between measurement occasions ",
com[1], " and ", com[2], "."))} })
if (!is.null(testletStr)) {
if(!"data.frame" %in% class(testletStr) || "tbl" %in% class(testletStr) ) {
stop("'testletStr' must be a data.frame.")}
if ( ncol(testletStr) != 2) {stop("'testletStr' must have two columns.")}
if(!"item" %in% colnames(testletStr)) { stop("One column in 'testletStr' must be named 'item'.")}
if (setdiff(colnames(testletStr), "item") != "unit") {
cat(paste0("The 'unit' column in 'testletStr' seemed to be called '",
setdiff(colnames(testletStr), "item"),"'. Rename this column into 'unit'.\n"))
colnames(testletStr)[setdiff(1:2, match("item", colnames(testletStr)))] <- "unit"
}
allI<- unique(unlist(lapply(il, FUN = function (x){x[,"item"]})))
if(!all(allI %in% testletStr[,"item"])) {
message(paste0("Following items without testlet definition: '",
paste(setdiff(allI,testletStr[,"item"]) , collapse="', '"),
"'.\n Linking errors will be compute without considering testlets."))
testletStr <- NULL
}
tls <- testletStr[which(testletStr[,"item"] %in% allI),]
isna<- length(which(is.na(tls[,setdiff(colnames(tls), "item")])))
if(isna>0) {
message(paste0("Following ",isna," items without testlet definition: '",
paste(tls[which(is.na(tls[,setdiff(colnames(tls), "item")])), "item"],
collapse="', '"),
"'.\n Linking errors will be compute without considering testlets."))
testletStr <- NULL
}
return(testletStr)
} else {
return(NULL)
} }
### ----------------------------------------------------------------------------
equa.rasch.jk <- function (pars.data, se.linkerror = FALSE, alpha1 = 0, alpha2 = 0) {
pars.data <- as.data.frame(stats::na.omit(pars.data))
itemunits <- unique(pars.data[, 1])
N.units <- length(itemunits)
N.items <- nrow(pars.data)
mod1 <- equ.rasch(pars.data[, c(4, 2)], pars.data[, c(4, 3)])
res1 <- data.frame(unit = itemunits, shift = 0, SD = 0, linkerror = 0)
for (nn in 1:N.units) {
pars.data1 <- pars.data[pars.data[, 1] != itemunits[nn], ]
mod.nn <- equ.rasch(x = pars.data1[, c(4, 2)], y = pars.data1[, c(4, 3)])
res1[nn, "shift"] <- mod.nn$B.est$Mean.Mean
res1[nn, "SD"] <- mod.nn$descriptives$SD
if (se.linkerror) {
itemunits.nn <- itemunits[-nn]
l1 <- NULL
for (ii in itemunits.nn) {
pars.data1.ii <- pars.data1[paste(pars.data1[, 1]) != ii, ]
mod.ii <- equ.rasch(x = pars.data1.ii[, c(4, 2)], y = pars.data1.ii[, c(4, 3)],
alpha1 = alpha1, alpha2 = alpha2)
l1 <- c(l1, mod.ii$B.est$Mean.Mean)
}
res1[nn, "linkerror"] <- sqrt((N.units - 2)/(N.units - 1) * sum((l1 - res1[nn, "shift"])^2))
}
}
linkerror <- sqrt((N.units - 1)/N.units * sum((res1[, 2] - mod1$B.est$Mean.Mean)^2))
se.sd <- sqrt((N.units - 1)/N.units * sum((res1[, 3] - mod1$descriptives$SD)^2))
if (se.linkerror) {
se.linkerror <- sqrt((N.units - 1)/N.units * sum((res1[, 4] - linkerror)^2))
}
else {
se.linkerror <- NA
}
descriptives <- data.frame(N.items = N.items, N.units = N.units,
shift = mod1$B.est$Mean.Mean, SD = mod1$descriptives$SD,
linkerror.jackknife = linkerror, SE.SD.jackknife = se.sd,
se.linkerror.jackknife = se.linkerror)
res <- list(pars.data = pars.data, itemunits = itemunits, descriptives = descriptives)
return(res)
}
### ----------------------------------------------------------------------------
equ.rasch <- function (x, y, theta = seq(-4, 4, len = 100), alpha1 = 0, alpha2 = 0) {
x[, 1] <- gsub(" ", "", paste(x[, 1]))
y[, 1] <- gsub(" ", "", paste(y[, 1]))
b.xy <- stats::na.omit(merge(x, y, by = "item"))
colnames(b.xy) <- c("item", "Itempar.Gr1", "Itempar.Gr2")
B.mm <- mean(b.xy[, 3]) - mean(b.xy[, 2])
g1 <- prob_raschtype_genlogis(theta = theta, b = b.xy[, 2], alpha1 = 0, alpha2 = 0)
opt_interval <- 10 * c(-1, 1)
ha <- function(B) {
fct1 <- prob_raschtype_genlogis(theta = theta, b = b.xy[, 2],
alpha1 = alpha1, alpha2 = alpha2)
fct2 <- prob_raschtype_genlogis(theta = theta, b = b.xy[, 3] - B,
alpha1 = alpha1, alpha2 = alpha2)
sum((fct1 - fct2)^2)
}
B.ha <- stats::optimize(f = ha, interval = opt_interval)$minimum
sl <- function(B) {
fct1 <- prob_raschtype_genlogis(theta = theta, b = b.xy[, 2],
alpha1 = alpha1, alpha2 = alpha2)
fct2 <- prob_raschtype_genlogis(theta = theta, b = b.xy[, 3] - B,
alpha1 = alpha1, alpha2 = alpha2)
sum((rowSums(fct1 - fct2))^2)
}
B.sl <- stats::optimize(f = sl, interval = opt_interval)$minimum
B.est <- data.frame(B.mm, B.ha, B.sl)
colnames(B.est) <- c("Mean.Mean", "Haebara","Stocking.Lord")
b.xy$TransfItempar.Gr1 <- b.xy[, 2] + B.est[1, "Mean.Mean"]
x[, 2] <- x[, 2] + B.est[1, "Mean.Mean"]
transf.par <- merge(x = x, y = y, by.x = 1, by.y = 1, all = TRUE)
colnames(transf.par) <- c("item", "TransfItempar.Gr1", "Itempar.Gr2")
transf.par <- transf.par[order(paste(transf.par$item)), ]
des <- data.frame(N.Items = nrow(b.xy), SD = stats::sd(b.xy$TransfItempar.Gr1 - b.xy$Itempar.Gr2),
stringsAsFactors = FALSE)
des$Var <- des$SD^2
des$linkerror <- sqrt(des[,"SD"]^2/des[,"N.Items"])
res <- list(B.est = B.est, descriptives = des, anchor = b.xy[, c(1, 2, 4, 3)], transf.par = transf.par)
return(res)}
### ----------------------------------------------------------------------------
prob_raschtype_genlogis <- function(theta, b, alpha1, alpha2, fixed.a=1+0*b,
Qmatrix=NULL, dimensions=NULL) {
LT <- length(theta)
if (is.matrix(theta)){
LT <- nrow(theta)
}
if ( is.null(Qmatrix) ){
XX <- TAM::tam_outer(x=theta, y=b, op="-")
XX <- as.vector(XX)
aM <- sirt::sirt_matrix2(x=fixed.a, nrow=LT)
XX <- aM * XX
}
if ( ! is.null(Qmatrix) ){
XX0 <- tcrossprod( as.matrix(theta), Qmatrix )
XX <- XX0 - outer( rep(1,nrow(theta)), b )
XX <- as.vector(XX)
aM <- sirt::sirt_matrix2(x=fixed.a, nrow=LT)
XX <- aM * XX
}
pm <- sirt::pgenlogis(x=XX, alpha1=alpha1, alpha2=alpha2 )
pm <- matrix( pm, ncol=length(b))
return(pm)}
### ----------------------------------------------------------------------------
checkIPD <- function(e1,e2,e3,testletStr, difBound){
eq1 <- equat1pl(results=e2, itemF = colnames(e2)[1], valueF = colnames(e2)[2],
prmNorm = e1, difBound=difBound, iterativ=TRUE)
eq2 <- equat1pl(results=e3, itemF = colnames(e3)[1], valueF = colnames(e3)[2],
prmNorm = e2, difBound=difBound, iterativ=TRUE)
if("itemExcluded" %in% colnames(eq1[["items"]][["global"]][["global"]][["info"]])) {
e1_weg <- setdiff(eq1[["items"]][["global"]][["global"]][["info"]][,"itemExcluded"], "")
} else {
e1_weg <- NULL
}
if("itemExcluded" %in% colnames(eq2[["items"]][["global"]][["global"]][["info"]])) {
e3_weg <- setdiff(eq2[["items"]][["global"]][["global"]][["info"]][,"itemExcluded"], "")
} else {
e3_weg <- NULL
}
return(list(e1_weg = e1_weg, e3_weg = e3_weg))}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.