#
# Copyright (C) 2013-2018 University of Amsterdam
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
AnovaRepeatedMeasuresInternal <- function(jaspResults, dataset = NULL, options) {
initialGlobalOptions <- options()
on.exit(options(initialGlobalOptions), add = TRUE)
numericVariables <- c(unlist(options$repeatedMeasuresCells),unlist(options$covariates))
numericVariables <- numericVariables[numericVariables != ""]
factorVariables <- c(unlist(options$betweenSubjectFactors))
factorVariables <- factorVariables[factorVariables != ""]
if (is.null(dataset))
dataset <- .readDataSetToEnd(columns.as.numeric=numericVariables, columns.as.factor=factorVariables,
exclude.na.listwise=c(numericVariables, factorVariables))
longData <- .BANOVAreadRManovaData(dataset, options)
if (isTryError(longData))
.quitAnalysis(gettext("Error while loading data. Please verify your repeated measures observations."))
ready <- all(options$repeatedMeasuresCells != "") && length(options$withinModelTerms) > 0
rmAnovaContainer <- .getRMAnovaContainer(jaspResults)
.BANOVAerrorhandling(longData, options, "RM-ANOVA")
.rmAnovaComputeResultsContainer(rmAnovaContainer, longData, options, ready)
.rmAnovaWithinSubjectsTable(rmAnovaContainer, dataset, options, ready)
.rmAnovaBetweenSubjectsTable(rmAnovaContainer, dataset, options, ready)
.referenceGrid(rmAnovaContainer, options, ready)
.rmAnovaAssumptionsContainer(rmAnovaContainer, dataset, options, ready)
.anovaOrdinalRestrictions(rmAnovaContainer, dataset, options, ready, analysis = "rmanova")
.rmAnovaPostHocTable(rmAnovaContainer, dataset, longData, options, ready)
.rmAnovaContrastTable(rmAnovaContainer, longData, options, ready)
.rmAnovaMarginalMeansTable(rmAnovaContainer, dataset, options, ready)
.rmAnovaFriedmanTable(rmAnovaContainer, longData, options, ready)
.rmAnovaConoverTable(rmAnovaContainer, longData, options, ready)
.rmAnovaSimpleEffects(rmAnovaContainer, dataset, longData, options, ready)
.BANOVAdescriptives(rmAnovaContainer, longData, options, list(noVariables=FALSE), "RM-ANOVA", ready)
return()
}
.getRMAnovaContainer <- function(jaspResults) {
if (!is.null(jaspResults[["rmAnovaContainer"]])) {
anovaContainer <- jaspResults[["rmAnovaContainer"]]
} else {
anovaContainer <- createJaspContainer()
# we set the dependencies on the container, this means that all items inside the container automatically have these dependencies
anovaContainer$dependOn(c("withinModelTerms", "betweenModelTerms", "repeatedMeasuresCells", "betweenSubjectFactors",
"repeatedMeasuresFactors", "covariates", "sumOfSquares", "poolErrorTermFollowup"))
jaspResults[["rmAnovaContainer"]] <- anovaContainer
}
return(anovaContainer)
}
.rmAnovaCheckErrors <- function(dataset, options, ready) {
if (!ready)
return()
modelTerms <- unlist(options$withinModelTerms, recursive = FALSE)
betweenModelTerms <- options$betweenModelTerms
for(betweenTerm in rev(betweenModelTerms)) {
.hasErrors(
dataset = dataset,
type = c('observations', 'variance', 'infinity'),
all.target = c(options$repeatedMeasuresCells, options$covariates),
all.grouping = betweenTerm,
observations.amount = "< 2",
exitAnalysisIfErrors = TRUE)
}
for(betweenTerm in rev(betweenModelTerms)) {
.hasErrors(
dataset = dataset,
type = c('infinity', 'factorLevels'),
all.target = betweenTerm,
factorLevels.amount = "< 2",
exitAnalysisIfErrors = TRUE)
}
}
.rmAnovaCheck <- function(dataset, options, perform) {
error <- FALSE
errorMessage <- NULL
ready <- "" %in% options$repeatedMeasuresCells == FALSE && length(options$withinModelTerms) > 0
if (ready && perform == "run") {
components <- unique(unlist(options$betweenSubjectFactors))
independentsWithLessThanTwoLevels <- c()
for (component in components) {
nLevels <- length(levels(dataset[[ .v(component) ]]))
if (nLevels < 2)
independentsWithLessThanTwoLevels <- c(independentsWithLessThanTwoLevels, component)
}
if (length(independentsWithLessThanTwoLevels) > 0) {
error <- TRUE
if(length(independentsWithLessThanTwoLevels) == 1) {
errorMessage <- gettextf("Factor: <em>%s</em>, contains fewer than two levels.", independentsWithLessThanTwoLevels)
} else {
errorMessage <- gettextf("Factors: <em>%s</em>, contain fewer than two levels.", paste(independentsWithLessThanTwoLevels, collapse=",", sep=""))
}
}
repeatedMeasuresData <- list()
for(i in options$repeatedMeasuresCells) {
repeatedMeasuresData[[i]] <- dataset[[.v(i)]]
}
infiniteRM <- unlist(lapply(repeatedMeasuresData,function(x)sum(is.infinite(x)) > 0))
if (!is.null(infiniteRM) && sum(infiniteRM) > 0) {
error <- TRUE
if(sum(infiniteRM) == 1) {
errorMessage <- gettextf("The repeated measure: <em>%s</em>, contains infinite values.", options$repeatedMeasuresCells[infiniteRM])
} else {
errorMessage <- gettextf("The repeated measures: <em>%s</em>, contain infinite values.", paste(options$repeatedMeasuresCells[infiniteRM], collapse=", "))
}
}
covariatesData <- list()
for(i in options$covariates) {
covariatesData[[i]] <- dataset[[.v(i)]]
}
infiniteCov <- unlist(lapply(covariatesData,function(x)sum(is.infinite(x)) > 0))
if (!is.null(infiniteCov) && sum(infiniteCov) > 0) {
error <- TRUE
if(sum(infiniteCov) == 1) {
errorMessage <- gettextf("The covariate: <em>%s</em>, contains infinite values.", options$covariates[infiniteCov])
} else {
errorMessage <- gettextf("The covariates: <em>%s</em>, contain infinite values.", paste(options$covariates[infiniteCov], collapse=", "))
}
}
allNames <- unlist(lapply(options[['repeatedMeasuresFactors']], function(x) x$name)) # Factornames
for(factorName in allNames){
if (any(factorName %in% options$betweenSubjectFactors )) {
error <- TRUE
errorMessage <- gettext("Please choose a name for the RM factors that differs from those for the between subjects factors.")
}
}
}
list(ready=ready, error=error, errorMessage=errorMessage)
}
.rmModelFormula <- function(options) {
termsRM.base64 <- c()
termsRM.normal <- c()
for (term in options$withinModelTerms) {
components <- unlist(term$components)
termRM.base64 <- paste(.v(components), collapse=":", sep="")
termRM.normal <- paste(components, collapse=" \u273B ", sep="")
termsRM.base64 <- c(termsRM.base64, termRM.base64)
termsRM.normal <- c(termsRM.normal, termRM.normal)
}
termsBS.base64 <- c()
termsBS.normal <- c()
for (term in options$betweenModelTerms) {
components <- unlist(term$components)
termBS.base64 <- paste(.v(components), collapse=":", sep="")
termBS.normal <- paste(components, collapse=" \u273B ", sep="")
termsBS.base64 <- c(termsBS.base64, termBS.base64)
termsBS.normal <- c(termsBS.normal, termBS.normal)
}
terms.base64 <- list()
terms.normal <- list()
terms.base64[[1]] <- termsBS.base64
terms.normal[[1]] <- termsBS.normal
for (i in 1:length(termsRM.base64)) {
if (is.null(termsBS.base64)) {
terms.base64[[i+1]] <- termsRM.base64[i]
terms.normal[[i+1]] <- termsRM.normal[i]
} else if (!is.null(termsRM.base64)){
terms.base64[[i+1]] <- c(termsRM.base64[i], paste(termsRM.base64[i], termsBS.base64, sep = ":"))
terms.normal[[i+1]] <- c(termsRM.normal[i], paste(termsRM.normal[i], termsBS.normal, sep = " \u273B "))
}
}
main <- paste("(",paste(unlist(terms.base64), collapse=" + "),")", sep="")
termsBS <- paste("(",paste(termsBS.base64, collapse=" + "),")", sep="")
errorRM <- paste("Error(",paste0(.BANOVAsubjectName, "/(", termsRM.base64, ")", collapse=" + "),")",sep="")
if (is.null(termsBS.base64) && is.null(termsRM.base64)) {
model.def <- dependent ~ 1
} else if (is.null(termsBS.base64)) {
model.def <- paste(.BANOVAdependentName, "~", paste(main, errorRM, sep=" + "))
} else if (is.null(termsRM.base64)) {
model.def <- paste(.BANOVAdependentName, "~", main)
} else {
model.def <- paste(.BANOVAdependentName, "~", paste(main, errorRM, termsBS, sep=" + "))
}
list(model.def = model.def, terms.normal = terms.normal, terms.base64 = terms.base64, termsRM.normal = termsRM.normal, termsRM.base64 = termsRM.base64)
}
.rmAnovaComputeResultsContainer <- function(rmAnovaContainer, longData, options, ready) {
if (!ready || !is.null(rmAnovaContainer[["anovaResult"]]))
return()
rmAnovaResult <- .rmAnovaComputeResults(longData, options)
if (rmAnovaResult[["tryResult"]] == "try-error" && grepl(as.character(rmAnovaResult[["tryMessage"]]), pattern = "allocate vector")) {
rmAnovaContainer$setError(gettext('Data set too big for univariate follow-up test. Try unselecting "Pool error term for follow-up tests" in the Model tab.'))
return()
} else if (rmAnovaResult[["tryResult"]] == "try-error") {
rmAnovaContainer$setError(gettext("Some parameters are not estimable, most likely due to empty cells of the design."))
return()
}
# Save model to state
rmAnovaContainer[["anovaResult"]] <- createJaspState(object = rmAnovaResult)
}
.rmAnovaComputeResults <- function(dataset, options, returnResultsEarly = FALSE) {
modelDef <- .rmModelFormula(options)
model.formula <- as.formula(modelDef$model.def)
options(contrasts=c("contr.sum","contr.poly"))
# set these options once for all afex::aov_car calls,
# this ensures for instance that afex::aov_car always returns objects of class afex_aov.
if (options$poolErrorTermFollowup) followupModelType <- "univariate" else followupModelType <- "multivariate"
afex::afex_options(
check_contrasts = TRUE, correction_aov = "GG",
emmeans_model = followupModelType, es_aov = "ges", factorize = TRUE,
lmer_function = "lmerTest", method_mixed = "KR", return_aov = "afex_aov",
set_data_arg = FALSE, sig_symbols = c(" +", " *", " **", " ***"), type = 3
)
# Computations:
if (options$sumOfSquares == "type1") {
tryResult <- try({
result <- stats::aov(model.formula, data=dataset)
summaryResultOne <- summary(result, expand.split = FALSE)
result <- afex::aov_car(model.formula, data=dataset, type= 3, factorize = FALSE,
include_aov = isTRUE(options[["poolErrorTermFollowup"]]))
summaryResult <- summary(result)
# Reformat the results to make it consistent with types 2 and 3
model <- as.data.frame(unclass(summaryResult$univariate.tests))
for (mySub in unlist(summaryResultOne, recursive = FALSE)) {
rownames(mySub) <- trimws(rownames(mySub))
for(term in rownames(mySub)[-nrow(mySub)]) {
model[term, "Sum Sq"] <- mySub[term, "Sum Sq"]
model[term, "num Df"] <- mySub[term, "Df"]
model[term, "F value"] <- mySub[term, "F value"]
model[term, "Pr(>F)"] <- mySub[term, "Pr(>F)"]
model[term, "Error SS"] <- mySub["Residuals", "Sum Sq"]
model[term, "den Df"] <- mySub["Residuals", "Df"]
}
}
})
} else if (options$sumOfSquares == "type2") {
tryResult <- try({
result <- afex::aov_car(model.formula, data=dataset, type= 2, factorize = FALSE,
include_aov = isTRUE(options[["poolErrorTermFollowup"]]))
summaryResult <- summary(result)
model <- as.data.frame(unclass(summaryResult$univariate.tests))
})
} else {
tryResult <- try({
result <- afex::aov_car(model.formula, data=dataset, type= 3, factorize = FALSE,
include_aov = isTRUE(options[["poolErrorTermFollowup"]]))
summaryResult <- summary(result)
model <- as.data.frame(unclass(summaryResult$univariate.tests))
})
}
if (class(tryResult) == "try-error") {
return(list(tryResult = "try-error", tryMessage = as.character(tryResult)))
}
if (returnResultsEarly)
return(list(result = result, model = model))
# Now we reformat the results table some more to make it flow with jaspResults later
interceptRow <- model["(Intercept", ]
model <- model[-1,]
rownames(model) <- trimws(rownames(model))
model[["isWithinTerm"]] <- model[[".isNewGroup"]] <- logical(nrow(model))
sortedModel <- model
cases <- unlist(sapply(modelDef$terms.base64, function(x) x[[1]]))
residualResults <- sortedModel[.mapAnovaTermsToTerms(cases, rownames(model)), ]
nextNewGroup <- 0
for (modelTerm in modelDef$terms.base64) {
if (!is.null(modelTerm)) {
isWithin <- any(modelTerm %in% modelDef$termsRM.base64)
indices <- .mapAnovaTermsToTerms(modelTerm, rownames(model))
nextNewGroup <- c(TRUE, rep(FALSE, length(indices) - 1))
sortedModel[indices, ] <- model[indices, ]
sortedModel[indices, c(".isNewGroup", "isWithinTerm")] <- c(nextNewGroup, rep(isWithin, length(indices)))
residualRow <- c(model[indices[1], c("Error SS", "den Df")], rep(NA, 4), 0, isWithin)
residualResults[.mapAnovaTermsToTerms(modelTerm[[1]], rownames(residualResults)), ] <- residualRow
}
}
# Make sure that order of anova result corresponds to order of specified model terms
mappedRownamesCases <- .mapAnovaTermsToTerms(rownames(sortedModel), unlist(modelDef$terms.base64))
# when interactions effects are excluded from the model terms they are still computed by afex
# their names are mapped to zero by .mapAnovaTermsToTerms so we first exclude them
idxValid <- mappedRownamesCases != 0L
sortedModel <- sortedModel[idxValid, ]
sortedModel[["case"]] <- unlist(modelDef$terms.normal)[mappedRownamesCases]
sortedModel[["Mean Sq"]] <- sortedModel[["Sum Sq"]] / sortedModel[["num Df"]]
sortedModel[["vovkSellke"]] <- VovkSellkeMPR(sortedModel[["Pr(>F)"]])
rownames(residualResults) <- cases
residualResults[["Mean Sq"]] <- residualResults[["Sum Sq"]] / residualResults[["num Df"]]
residualResults[["case"]] <- "Residuals"
# Now we calculate effect sizes
SSr <- sortedModel[["Error SS"]]
MSr <- SSr/sortedModel[["den Df"]]
sortedModel[["eta"]] <- sortedModel[["Sum Sq"]] / (sum(sortedModel[["Sum Sq"]]) + sum(residualResults[["Sum Sq"]]))
sortedModel[["partialEta"]] <- sortedModel[["Sum Sq"]] / (sortedModel[["Sum Sq"]] + SSr)
sortedModel[["genEta"]] <- result[["anova_table"]][["ges"]][idxValid]
n <- interceptRow[["den Df"]] + 1
MSb <- interceptRow[["Error SS"]] / (n-1)
MSm <- sortedModel[["Mean Sq"]]
df <- sortedModel[["num Df"]]
omega <- (df / (n * (df + 1)) * (MSm - MSr)) / (MSr + ((MSb - MSr) / (df + 1)) +
(df / (n * (df + 1))) * (MSm - MSr))
sortedModel[["omega"]] <- sapply(omega, max, 0)
for (i in seq_along(summaryResult)) {
if (any(rownames(summaryResult[[i]]) == "(Intercept)"))
summaryResult[[i]] <- summaryResult[[i]][-1, ]
}
partOmegaResult <- effectsize::omega_squared(result, ci = options[["effectSizeCiLevel"]], partial = TRUE, alternative = "two.sided")
effectSizeIndexMap <- .mapAnovaTermsToTerms(partOmegaResult[["Parameter"]], rownames(sortedModel))
sortedModel[["partialOmega"]] <- partOmegaResult[["Omega2_partial"]][effectSizeIndexMap]
# Now we include the results from the corrections
withinAnovaTable <- ggTable <- hfTable <- subset(sortedModel, isWithinTerm == TRUE)
withinAnovaTable[["correction"]] <- "None"
corrections <- summaryResult$pval.adjustments
sphericityTests <- as.data.frame(unclass(summaryResult$sphericity.tests))
if (!is.null(rownames(corrections)) && length(rownames(corrections)) > 0) {
corrections <- as.data.frame(corrections)
corrections <- corrections[.mapAnovaTermsToTerms(rownames(withinAnovaTable), rownames(corrections)), ]
sphericityTests <- sphericityTests[.mapAnovaTermsToTerms(rownames(withinAnovaTable), rownames(corrections)), ]
rownames(corrections) <- rownames(sphericityTests) <-
rownames(withinAnovaTable)[.mapAnovaTermsToTerms(rownames(corrections), rownames(withinAnovaTable))]
}
# Add NA rows to corrections and sphericity tests for within factors with 2 levels
if (nrow(sphericityTests) != nrow(withinAnovaTable)) {
unavailableCases <- rownames(withinAnovaTable)[!rownames(withinAnovaTable) %in% rownames(sphericityTests)]
emptyCorrections <- matrix(ncol = 4, nrow = length(unavailableCases), NA,
dimnames = list(unavailableCases, c("GG eps", "Pr(>F[GG])", "HF eps", "Pr(>F[HF])")))
if (is.null(rownames(corrections)) || all(is.na(corrections[, "GG eps"]))) {
corrections <- as.data.frame(emptyCorrections)
} else {
corrections <- rbind(corrections, as.data.frame(emptyCorrections))
}
emptyTests <- matrix(ncol = 2, nrow = length(unavailableCases), NA,
dimnames = list(unavailableCases, colnames(sphericityTests)))
sphericityTests <- as.data.frame(rbind(sphericityTests, emptyTests))
}
# If corrections could not be run, create data frame with NA's
if (length(rownames(corrections)) == 0 )
corrections <- matrix(ncol = 4, nrow = nrow(withinAnovaTable), NA,
dimnames = list(rownames(withinAnovaTable), c("GG eps", "Pr(>F[GG])", "HF eps", "Pr(>F[HF])")))
withinIndices <- .mapAnovaTermsToTerms(rownames(withinAnovaTable), rownames(corrections))
# set 1 as an upper-bound of the correction factors, see https://github.com/jasp-stats/jasp-issues/issues/1709
ggCorrections <- pmin(corrections[withinIndices, "GG eps"], 1)
hfCorrections <- pmin(corrections[withinIndices, "HF eps"], 1)
ggTable[["num Df"]] <- withinAnovaTable[["num Df"]] * ggCorrections
ggTable[["Mean Sq"]] <- withinAnovaTable[["Sum Sq"]] / ggTable[["num Df"]]
ggTable[["den Df"]] <- withinAnovaTable[["den Df"]] * ggCorrections
ggTable[["Pr(>F)"]] <- pf(withinAnovaTable[["F value"]], ggTable[["num Df"]], ggTable[["den Df"]], lower.tail = FALSE)
ggTable[["correction"]] <- gettext("Greenhouse-Geisser")
ggTable[[".isNewGroup"]] <- FALSE
hfTable[["num Df"]] <- withinAnovaTable[["num Df"]] * hfCorrections
hfTable[["Mean Sq"]] <- withinAnovaTable[["Sum Sq"]] / hfTable[["num Df"]]
hfTable[["den Df"]] <- withinAnovaTable[["den Df"]] * hfCorrections
hfTable[["Pr(>F)"]] <- pf(withinAnovaTable[["F value"]], hfTable[["num Df"]], hfTable[["den Df"]], lower.tail = FALSE)
hfTable[["correction"]] <- gettext("Huynh-Feldt")
hfTable[[".isNewGroup"]] <- FALSE
residualResults[["eta"]] <- residualResults[["etaPart"]] <- residualResults[["genEta"]] <-
residualResults[["omega"]] <- residualResults[["p"]] <- as.numeric(NA)
wResidualResults <- wResidualResultsGG <- wResidualResultsHF <- subset(residualResults, isWithinTerm == TRUE)
wResidualResults[["correction"]] <- gettext("None")
wResidualResults[[".isNewGroup"]] <- TRUE
residualIndices <- .mapAnovaTermsToTerms(rownames(wResidualResults), rownames(corrections))
wResidualResultsGG[["num Df"]] <- wResidualResults[["num Df"]] * corrections[residualIndices, "GG eps"]
wResidualResultsGG[["Mean Sq"]] <- wResidualResults[["Sum Sq"]] / wResidualResultsGG[["num Df"]]
wResidualResultsGG[["correction"]] <- gettext("Greenhouse-Geisser")
wResidualResultsHF[["num Df"]] <- wResidualResults[["num Df"]] * corrections[residualIndices, "HF eps"]
wResidualResultsHF[["Mean Sq"]] <- wResidualResults[["Sum Sq"]] / wResidualResultsHF[["num Df"]]
wResidualResultsHF[["correction"]] <- gettext("Huynh-Feldt")
withinAnovaTable <- cbind(withinAnovaTable, corrections[withinIndices, ], sphericityTests[withinIndices, ])
# Makes lists with results
withinAnovaTableCollection <- list("None" = withinAnovaTable, "Huynh-Feldt" = hfTable, "Greenhouse-Geisser" = ggTable)
wResidualResultsList <- list("None" = wResidualResults, "Huynh-Feldt" = wResidualResultsHF, "Greenhouse-Geisser" = wResidualResultsGG)
# # Corrections not available
# withinAnovaTableCollection <- list("None" = withinAnovaTable)
# wResidualResultsList <- list("None" = wResidualResults)
wResidualResultsList[["None"]]["BetweenResidualResults", c("Sum Sq", "num Df")] <- interceptRow[, c("Error SS", "den Df")]
wResidualResultsList[["None"]]["BetweenResidualResults", "Mean Sq"] <- interceptRow[["Error SS"]] / interceptRow[["den Df"]]
for (tableIndex in seq_along(withinAnovaTableCollection)) {
withinAnovaTableCollection[[tableIndex]][["vovkSellke"]] <- VovkSellkeMPR(withinAnovaTableCollection[[tableIndex]][["Pr(>F)"]])
}
return(list(anovaResult = sortedModel,
residualTable = wResidualResultsList,
withinAnovaTable = withinAnovaTableCollection,
assumptionResult = cbind(sphericityTests, corrections[withinIndices, ]),
fullModel = result,
tryResult = "tryResult"))
}
.mapAnovaTermsToTerms <- function(oneTerms, twoTerms) {
nTerms <- length(oneTerms)
indices <- numeric(nTerms)
counter <- 1
for (i in 1:nTerms) {
splitFirst <- strsplit(oneTerms[[i]],":")[[1]]
for (j in 1:length(twoTerms)) {
matchedTerms <- match(splitFirst, strsplit(twoTerms[[j]],":")[[1]])
if ((length(strsplit(twoTerms[[j]],":")[[1]]) == length(splitFirst)) && !any(is.na(matchedTerms))) {
indices[counter] <- j
counter <- counter + 1
}
}
}
return(indices)
}
.rmAnovaBetweenSubjectsTable <- function(rmAnovaContainer, dataset, options, ready) {
if(!is.null(rmAnovaContainer[["betweenTable"]]))
return()
betweenTable <- createJaspTable(title = gettext("Between Subjects Effects"), position = 2)
betweenTable$dependOn(c("effectSizeEstimates", "effectSizeEtaSquared", "effectSizePartialEtaSquared",
"effectSizeGeneralEtaSquared", "effectSizeOmegaSquared", "vovkSellke",
"effectSizePartialOmegaSquared", "effectSizeCi", "effectSizeCiLevel"))
betweenTable$addColumnInfo(title = gettext("Cases"), name = "case", type = "string" )
betweenTable$addColumnInfo(title = gettext("Sum of Squares"), name = "Sum Sq", type = "number")
betweenTable$addColumnInfo(title = gettext("df"), name = "num Df", type = "integer")
betweenTable$addColumnInfo(title = gettext("Mean Square"), name = "Mean Sq", type = "number")
betweenTable$addColumnInfo(title = gettext("F"), name = "F value", type = "number")
betweenTable$addColumnInfo(title = gettext("p"), name = "Pr(>F)", type = "pvalue")
if (options$vovkSellke && length(options$betweenSubjectFactors) > 0) {
betweenTable$addColumnInfo(title = gettextf("VS-MPR%s", "\u002A"), name = "vovkSellke", type = "number")
betweenTable$addFootnote(message = .messages("footnote", "VovkSellkeMPR"), symbol = "\u002A")
}
if (options$effectSizeEstimates && length(options$betweenSubjectFactors) > 0) {
if (options$effectSizeEtaSquared)
betweenTable$addColumnInfo(title = "\u03B7\u00B2", name = "eta", type = "number")
if (options$effectSizePartialEtaSquared) {
betweenTable$addColumnInfo(title = "\u03B7\u00B2\u209A", name = "partialEta", type = "number")
if (options$effectSizeCi && !is.null(options$dependent)) {
thisOverTitle <- gettextf("%s%% CI for \u03B7\u00B2\u209A", options$effectSizeCiLevel * 100)
betweenTable$addColumnInfo(name="partialEtaLow", type = "number", title = gettext("Lower"), overtitle = thisOverTitle)
betweenTable$addColumnInfo(name="partialEtaHigh", type = "number", title = gettext("Upper"), overtitle = thisOverTitle)
}
}
if (options$effectSizeGeneralEtaSquared)
betweenTable$addColumnInfo(title = gettextf("%s<sub>G</sub>", "\u03B7\u00B2"), name = "genEta", type = "number")
if (options$effectSizeOmegaSquared) {
betweenTable$addColumnInfo(title = "\u03C9\u00B2", name = "omega", type = "number")
if (options$effectSizeCi && !is.null(options$dependent)) {
thisOverTitle <- gettextf("%s%% CI for \u03C9\u00B2", options$effectSizeCiLevel * 100)
betweenTable$addColumnInfo(name="omegaLow", type = "number", title = gettext("Lower"), overtitle = thisOverTitle)
betweenTable$addColumnInfo(name="omegaHigh", type = "number", title = gettext("Upper"), overtitle = thisOverTitle)
}
}
if (options$effectSizePartialOmegaSquared) {
betweenTable$addColumnInfo(title = "\u03C9\u00B2\u209A", name = "partialOmega", type = "number")
if (options$effectSizeCi && !is.null(options$dependent)) {
thisOverTitle <- gettextf("%s%% CI for \u03C9\u00B2\u209A", options$effectSizeCiLevel * 100)
betweenTable$addColumnInfo(name="partialOmegaLow", type = "number", title = gettext("Lower"), overtitle = thisOverTitle)
betweenTable$addColumnInfo(name="partialOmegaHigh", type = "number", title = gettext("Upper"), overtitle = thisOverTitle)
}
}
}
betweenTable$showSpecifiedColumnsOnly <- TRUE
.addSumSquaresFootnote(betweenTable, options)
rmAnovaContainer[["betweenTable"]] <- betweenTable
if (!ready || rmAnovaContainer$getError()) {
return()
}
result <- rmAnovaContainer[["anovaResult"]]$object$anovaResult
result <- result[result$isWithinTerm == FALSE, ]
betweenwResidualResult <- rmAnovaContainer[["anovaResult"]]$object$residualTable$None["BetweenResidualResults", ]
result["Residuals", "num Df"] <- betweenwResidualResult[["num Df"]]
result["Residuals", "Sum Sq"] <- betweenwResidualResult[["Sum Sq"]]
result["Residuals", "Mean Sq"] <- betweenwResidualResult[["Mean Sq"]]
result["Residuals", "case"] <- "Residuals"
result["Residuals", ".isNewGroup"] <- TRUE
betweenTable$setData(result)
return()
}
.rmAnovaWithinSubjectsTable <- function(rmAnovaContainer, dataset, options, ready) {
if (!is.null(rmAnovaContainer[["withinAnovaTable"]]))
return()
anovaTable <- createJaspTable(title = gettext("Within Subjects Effects"), position = 1)
rmAnovaContainer[["withinAnovaTable"]] <- anovaTable
anovaTable$showSpecifiedColumnsOnly <- TRUE
anovaTable$dependOn(c("sphericityCorrectionGreenhouseGeisser", "sphericityCorrectionHuynhFeldt",
"sphericityCorrectionNone", "vovkSellke", "effectSizeEstimates", "effectSizeEtaSquared",
"effectSizePartialEtaSquared", "effectSizeGeneralEtaSquared", "effectSizeOmegaSquared",
"effectSizePartialOmegaSquared", "effectSizeCi", "effectSizeCiLevel"))
corrections <- c("None", "Greenhouse-Geisser", "Huynh-Feldt")[c(options$sphericityCorrectionNone,
options$sphericityCorrectionGreenhouseGeisser,
options$sphericityCorrectionHuynhFeldt)]
if (length(corrections) == 0) corrections <- "None"
anovaTable$addColumnInfo(title = gettext("Cases"), name = "case", type = "string", combine = TRUE)
dfType <- "integer" # Make df an integer unless corrections are applied
if ((length(corrections) > 1 || any(!"None" %in% corrections))) {
anovaTable$addColumnInfo(title = gettext("Sphericity Correction"), name = "correction", type = "string")
dfType <- "number"
}
anovaTable$addColumnInfo(title = gettext("Sum of Squares"), name = "Sum Sq", type = "number")
anovaTable$addColumnInfo(title = gettext("df"), name = "num Df", type = dfType)
anovaTable$addColumnInfo(title = gettext("Mean Square"), name = "Mean Sq", type = "number")
anovaTable$addColumnInfo(title = gettext("F"), name = "F value", type = "number")
anovaTable$addColumnInfo(title = gettext("p"), name = "p", type = "pvalue")
if (options$vovkSellke) {
anovaTable$addColumnInfo(title = gettextf("VS-MPR%s", "\u002A"), name = "vovkSellke", type = "number")
anovaTable$addFootnote(message = .messages("footnote", "VovkSellkeMPR"), symbol = "\u002A")
}
if (options$effectSizeEstimates) {
if (options$effectSizeEtaSquared) {
anovaTable$addColumnInfo(title = "\u03B7\u00B2", name = "eta", type = "number")
}
if (options$effectSizePartialEtaSquared) {
anovaTable$addColumnInfo(title = "\u03B7\u00B2\u209A", name = "partialEta", type = "number")
if (options$effectSizeCi && !is.null(options$dependent)) {
thisOverTitle <- gettextf("%s%% CI for \u03B7\u00B2\u209A", options$effectSizeCiLevel * 100)
anovaTable$addColumnInfo(name="partialEtaLow", type = "number", title = gettext("Lower"), overtitle = thisOverTitle)
anovaTable$addColumnInfo(name="partialEtaHigh", type = "number", title = gettext("Upper"), overtitle = thisOverTitle)
}
}
if(options$effectSizeGeneralEtaSquared) {
anovaTable$addColumnInfo(name="genEta", type="number", title=gettextf("%s<sub>G</sub>", "\u03B7\u00B2"))
}
if (options$effectSizeOmegaSquared) {
anovaTable$addColumnInfo(title = "\u03C9\u00B2", name = "omega", type = "number")
if (options$effectSizeCi && !is.null(options$dependent)) {
thisOverTitle <- gettextf("%s%% CI for \u03C9\u00B2", options$effectSizeCiLevel * 100)
anovaTable$addColumnInfo(name="omegaLow", type = "number", title = gettext("Lower"), overtitle = thisOverTitle)
anovaTable$addColumnInfo(name="omegaHigh", type = "number", title = gettext("Upper"), overtitle = thisOverTitle)
}
}
if (options$effectSizePartialOmegaSquared) {
anovaTable$addColumnInfo(title = "\u03C9\u00B2\u209A", name = "partialOmega", type = "number")
if (options$effectSizeCi && !is.null(options$dependent)) {
thisOverTitle <- gettextf("%s%% CI for \u03C9\u00B2\u209A", options$effectSizeCiLevel * 100)
anovaTable$addColumnInfo(name="partialOmegaLow", type = "number", title = gettext("Lower"), overtitle = thisOverTitle)
anovaTable$addColumnInfo(name="partialOmegaHigh", type = "number", title = gettext("Upper"), overtitle = thisOverTitle)
}
}
}
.addSumSquaresFootnote(anovaTable, options)
if (!ready || rmAnovaContainer$getError())
return()
withinResults <- rmAnovaContainer[["anovaResult"]]$object$withinAnovaTable
residualResults <- rmAnovaContainer[["anovaResult"]]$object$residualTable
mauchlyResult <- rmAnovaContainer[["anovaResult"]]$object$assumptionResult
for(i in seq_along(withinResults)) {
names(withinResults[[i]])[names(withinResults[[i]]) == "Pr(>F)"] <- "p"
}
modelTerms <- .rmModelFormula(options)$termsRM.base64
allCases <- rownames(withinResults[[1]])
addResidualAfter <- allCases[.mapAnovaTermsToTerms(modelTerms, allCases) + (length(allCases) / length(modelTerms)) - 1]
for (case in allCases) {
for (i in seq_along(corrections)) {
withinResults[[corrections[i]]][case, ".isNewGroup"] <- i == 1
if (!is.na(withinResults[[corrections[i]]][case, "num Df"])) {
anovaTable$addRows(as.list(withinResults[[corrections[i]]][case, ]),
rowNames=paste0(case, corrections[i]))
} else {
anovaTable$addFootnote(gettext("Sphericity corrections not available for factors with 2 levels."))
}
}
if (case %in% modelTerms) {
currentCase <- case
}
if (case %in% addResidualAfter) {
for (i in seq_along(corrections)) {
if (!is.na(withinResults[[corrections[i]]][currentCase, "num Df"])) {
residualResults[[corrections[i]]][currentCase, ".isNewGroup"] <- i == 1
anovaTable$addRows(as.list(residualResults[[corrections[i]]][currentCase, ]),
rowNames=paste0(currentCase, "Resid", corrections[i]))
}
}
}
}
if (!all(is.na(withinResults[[1]][["Test statistic"]]))) {
# when a RM factor consists of only 2 levels the p-value is NA, hence the is.na check
violatedMauchlyCases <- rownames(mauchlyResult)[mauchlyResult[, "p-value"] < 0.05 & !is.na(mauchlyResult[, "p-value"])]
if (length(violatedMauchlyCases) > 0)
anovaTable$addFootnote(message = gettext("Mauchly's test of sphericity indicates that the assumption of sphericity is violated (p < .05)."),
colNames = c("Sum Sq", "num Df", "F value", "Mean Sq", "Pr(F)", "p"),
rowNames = paste0(violatedMauchlyCases, "None"))
}
return()
}
.rmAnovaAssumptionsContainer <- function(rmAnovaContainer, dataset, options, ready) {
if (!is.null(rmAnovaContainer[["assumptionsContainer"]]))
return()
assumptionsContainer <- createJaspContainer(title = gettext("Assumption Checks"),
dependencies = c("homogeneityTests", "sphericityTests", "qqPlot"))
rmAnovaContainer[["assumptionsContainer"]] <- assumptionsContainer
if (options$homogeneityTests == TRUE)
.rmAnovaLevenesTable(rmAnovaContainer, dataset, options, ready)
if (options$sphericityTests == TRUE)
.rmAnovaSphericityTable(rmAnovaContainer, dataset, options, ready)
if (options$qqPlot == TRUE)
.qqPlotFreqAnova(rmAnovaContainer, dataset, options, ready)
return()
}
.rmAnovaLevenesTable <- function(rmAnovaContainer, dataset, options, ready) {
if (!is.null(rmAnovaContainer[["rmAnovaLevenesTable"]]))
return()
rmAnovaLevenesTable <- createJaspTable(gettext("Test for Equality of Variances (Levene's)"))
rmAnovaContainer[["assumptionsContainer"]][["rmAnovaLevenesTable"]] <- rmAnovaLevenesTable
rmAnovaLevenesTable$addColumnInfo(name="case", type="string", title="")
rmAnovaLevenesTable$addColumnInfo(name="F", type="number")
rmAnovaLevenesTable$addColumnInfo(name="df1", type="integer")
rmAnovaLevenesTable$addColumnInfo(name="df2", type="integer")
rmAnovaLevenesTable$addColumnInfo(name="p", type="pvalue")
if (options$vovkSellke) {
rmAnovaLevenesTable$addColumnInfo(title = "VS-MPR\u002A", name = "vovkSellke", type = "number")
rmAnovaLevenesTable$addFootnote(message = .messages("footnote", "VovkSellkeMPR"), symbol = "\u002A")
}
rmAnovaLevenesTable$showSpecifiedColumnsOnly <- TRUE
if (!ready || rmAnovaContainer$getError())
return()
rmAnovaLevenesTable$setExpectedSize(length(options$repeatedMeasuresCells))
if (length(options[["betweenSubjectFactors"]]) == 0) {
rmAnovaLevenesTable$setError(gettext("Cannot perform homogeneity tests because there are no between subjects factors specified."))
return()
}
for (i in seq_along(options$repeatedMeasuresCells)) {
interaction <- paste(.v(options$betweenSubjectFactors), collapse=":", sep="")
if (length(options$covariates) > 0 ) {
covterms <- paste(.v(options$covariates), collapse="+", sep="")
combterms <- paste(c(interaction,covterms), collapse="+", sep="")
levene.def <- paste(.v(options$repeatedMeasuresCells[i]), "~", combterms)
} else {
levene.def <- paste(.v(options$repeatedMeasuresCells[i]), "~", interaction)
}
levene.formula <- as.formula(levene.def)
dummyAov <- aov(levene.formula, data = dataset, qr = T)
resids <- abs(dummyAov$residuals)
levene.def <- paste("resids", "~", interaction)
levene.formula <- as.formula(levene.def)
r <- summary(aov(levene.formula, dataset))
error <- base::tryCatch(summary(aov(levene.formula, dataset)),error=function(e) e, warning=function(w) w)
row <- data.frame(case = options$repeatedMeasuresCells[i],
F = r[[1]]$`F value`[1],
df1 = r[[1]]$Df[1],
df2 = r[[1]]$Df[2],
p=r[[1]]$`Pr(>F)`[1],
".isNewGroup" = i == 1,
vovkSellke = VovkSellkeMPR(r[[1]]$`Pr(>F)`[1]))
rmAnovaLevenesTable$addRows(row)
}
return()
}
.rmAnovaSphericityTable <- function(rmAnovaContainer, dataset, options, ready) {
if (!is.null(rmAnovaContainer[["sphericityTable"]]) || !options$sphericityTests)
return()
sphericityTable <- createJaspTable(gettext("Test of Sphericity"))
sphericityTable$addColumnInfo(name = "case", type = "string", title = "")
sphericityTable$addColumnInfo(name = "Test statistic", type = "number", title = gettext("Mauchly's W"))
sphericityTable$addColumnInfo(name = "approxChi", type = "number", title = gettextf("Approx. %s", "\u03A7\u00B2"))
sphericityTable$addColumnInfo(name = "dfSphericity", type = "integer", title = gettext("df"))
sphericityTable$addColumnInfo(name = "p-value", type = "pvalue", title = gettext("p-value"))
sphericityTable$addColumnInfo(name = "GG eps", type = "number", title = gettextf("Greenhouse-Geisser %s", "\u03B5"))
sphericityTable$addColumnInfo(name = "HF eps", type = "number", title = gettextf("Huynh-Feldt %s", "\u03B5"))
sphericityTable$addColumnInfo(name = "LB", type = "number", title = gettextf("Lower Bound %s", "\u03B5"))
sphericityTable$showSpecifiedColumnsOnly <- TRUE
rmAnovaContainer[["assumptionsContainer"]][["sphericityTable"]] <- sphericityTable
if(!ready)
return()
.approxChi <- function(df, n, W){
d <- 1 - (2*df^2 + df + 2) / (6*df*(n-1))
-(n-1)*d*log(W)
}
assumptionResult <- rmAnovaContainer[["anovaResult"]]$object$assumptionResult
anovaResult <- rmAnovaContainer[["anovaResult"]]$object$withinAnovaTable$None
# if (nrow(assumptionResult) == 0 || all(is.na(assumptionResult[["GG eps"]]))) {
if (all(is.na(anovaResult[["Test statistic"]]))) {
sphericityTable$setError(gettext("Cannot perform sphericity tests because there are only two levels of the RM factor, or because the SSP matrix is singular."))
return()
}
df <- anovaResult[["num Df"]]
anovaResult[["dfSphericity"]] <- df * (df + 1) / 2 - 1
n <- anovaResult[["den Df"]] / df + 1
anovaResult[["approxChi"]] <- .approxChi(df, n, anovaResult[["Test statistic"]])
anovaResult[["LB"]] <- 1 / df
anovaResult[["HF eps"]] <- sapply(anovaResult[["HF eps"]], min, 1)
anovaResult[[".isNewGroup"]] <- FALSE
includeInTable <- rownames(anovaResult) %in% sapply(options$withinModelTerms, function(x) paste(.v(unlist(x)), collapse=":"))
sphericityTable$setData(anovaResult[includeInTable & (!is.na(anovaResult[["approxChi"]])), ])
return()
}
.referenceGrid <- function(rmAnovaContainer, options, ready) {
if (!is.null(rmAnovaContainer[["referenceGrid"]]) || !ready || rmAnovaContainer$getError())
return()
fullModel <- rmAnovaContainer[["anovaResult"]]$object$fullModel
referenceGridList <- list()
variables <- sapply(c(options$withinModelTerms, options$betweenModelTerms),
function(x) {paste(.v(x$components), collapse = ":")})
if (length(options$betweenModelTerms) > 0) {
mixedTerms <- sapply(options$withinModelTerms,
function(x) {sapply(options$betweenModelTerms,
function(y) {paste(c(.v(y$components), .v(x$components)), collapse = ":")})})
variables <- union(variables, mixedTerms)
}
for (var in variables) {
formula <- as.formula(paste("~", var))
referenceGrid <- emmeans::emmeans(fullModel, formula)
# after updating emmeans, check if this is still necessary by following the steps in https://github.com/jasp-stats/jasp-desktop/pull/4323
environment(referenceGrid@dffun) <- baseenv()
referenceGridList[[var]] <- referenceGrid
}
rmAnovaContainer[["referenceGrid"]] <- createJaspState(object = referenceGridList,
dependencies = c("withinModelTerms",
"betweenModelterms"))
return()
}
.rmAnovaPostHocTable <- function(rmAnovaContainer, dataset, longData, options, ready) {
if(!is.null(rmAnovaContainer[["postHocStandardContainer"]]) || length(options$postHocTerms) ==0)
return()
postHocContainer <- createJaspContainer(title = gettext("Post Hoc Tests"))
postHocContainer$dependOn(c("postHocTerms", "postHocEffectSize", "postHocCorrectionBonferroni",
"postHocCorrectionHolm", "postHocCorrectionScheffe", "postHocCorrectionTukey",
"postHocSignificanceFlag", "postHocCi", "postHocLetterAlpha", "postHocLetterTable",
"postHocCiLevel", "postHocConditionalTable"))
rmAnovaContainer[["postHocStandardContainer"]] <- postHocContainer
postHocVariables <- unlist(options$postHocTerms, recursive = FALSE)
postHocVariablesListV <- unname(lapply(postHocVariables, .v))
variables <- unname(sapply(postHocVariables, function(x) paste(.v(x), collapse = ":")))
for (postHocVarIndex in 1:length(postHocVariables)) {
thisTitle <- paste(postHocVariables[[postHocVarIndex]], collapse = " \u273B ")
thisVarName <- paste(postHocVariables[[postHocVarIndex]], collapse = ":")
byVariable <- if (options[["postHocConditionalTable"]] && length(postHocVariables[[postHocVarIndex]]) > 1) postHocVariables[[postHocVarIndex]] else NULL
termsToLoop <- if (options[["postHocConditionalTable"]]) postHocVariables[[postHocVarIndex]] else 1
for (termIndex in seq_along(termsToLoop))
postHocContainer[[ paste0(thisVarName, termIndex)]] <- .createPostHocStandardTable(thisTitle, byVariable[termIndex], options)
if (options[["postHocLetterTable"]]) {
letterTable <- createJaspTable(title = paste0("Letter-Based Grouping - ", thisVarName))
for (letterVar in postHocVariables[[postHocVarIndex]])
letterTable$addColumnInfo(name=letterVar, type="string", combine = TRUE)
letterTable$addColumnInfo(name="Letter", type="string")
letterTable$addFootnote("If two or more means share the same grouping symbol,
then we cannot show them to be different, but we also did not show them to be the same. ")
letterTable$showSpecifiedColumnsOnly <- TRUE
postHocContainer[[paste0(thisVarName, "LetterTable")]] <- letterTable
}
}
if (!ready || rmAnovaContainer$getError())
return()
referenceGrid <- rmAnovaContainer[["referenceGrid"]]$object
fullModel <- rmAnovaContainer[["anovaResult"]]$object$fullModel
allNames <- unlist(lapply(options$repeatedMeasuresFactors, function(x) x$name)) # Factornames
for (postHocVarIndex in seq_along(variables)) {
thisVarName <- variables[postHocVarIndex]
# create vector to loop over for conditional post hoc tables
termsToLoop <- if (options[["postHocConditionalTable"]]) postHocVariables[[postHocVarIndex]] else 1
for (termIndex in seq_along(termsToLoop)) {
thisVarNameRef <- paste0(thisVarName, termIndex)
byVariable <- if (length(termsToLoop) > 1) postHocVariables[[postHocVarIndex]] else NULL
resultPostHoc <- summary(pairs(referenceGrid[[thisVarName]], adjust="bonferroni", by = byVariable[termIndex]),
infer = TRUE, level = options$postHocCiLevel)
numberOfLevels <- nrow(as.data.frame(referenceGrid[[thisVarName]]))
bonfAdjustCIlevel <- .computeBonferroniConfidence(options$postHocCiLevel,
numberOfLevels = numberOfLevels)
effectSizeResult <- as.data.frame(emmeans::eff_size(referenceGrid[[thisVarName]],
sigma = sqrt(mean(sigma(fullModel$lm)^2)),
edf = df.residual(fullModel$lm),
level = bonfAdjustCIlevel,
by = byVariable[termIndex]))
resultPostHoc[["bonferroni"]] <- resultPostHoc[["p.value"]]
resultPostHoc[["tukey"]] <- summary(pairs(referenceGrid[[thisVarName]], adjust="tukey",
by = byVariable[termIndex]))[["p.value"]]
resultPostHoc[["scheffe"]] <- summary(pairs(referenceGrid[[thisVarName]], adjust="scheffe",
by = byVariable[termIndex]))[["p.value"]]
resultPostHoc[["holm"]] <- summary(pairs(referenceGrid[[thisVarName]], adjust="holm",
by = byVariable[termIndex]))[["p.value"]]
resultPostHoc[["cohenD"]] <- effectSizeResult[["effect.size"]]
resultPostHoc[["cohenD_LowerCI"]] <- effectSizeResult[["lower.CL"]]
resultPostHoc[["cohenD_UpperCI"]] <- effectSizeResult[["upper.CL"]]
comparisons <- strsplit(as.character(resultPostHoc$contrast), " - ")
orderOfTerms <- unlist(lapply(options$repeatedMeasuresFactors, function(x) x$name))
indexofOrderFactors <- match(allNames,orderOfTerms)
if (any(postHocVariables[[postHocVarIndex]] %in% allNames)) { ## If the variable is a repeated measures factor
resultPostHoc[["scheffe"]] <- ""
resultPostHoc[["tukey"]] <- ""
if (options$postHocCorrectionScheffe || options$postHocCorrectionTukey) {
cors <- paste(c("Tukey", "Scheffe")[c(options$postHocCorrectionTukey, options$postHocCorrectionScheffe)], collapse = " and ")
postHocContainer[[thisVarNameRef]]$addFootnote(gettextf("%s corrected p-values are not appropriate for repeated measures post-hoc tests (Maxwell, 1980; Field, 2012).", cors))
}
}
resultPostHoc[["contrast_A"]] <- lapply(comparisons, function(x) paste(.unv(strsplit(x[[1]], "[ ,]")[[1]]),
collapse = ", "))
resultPostHoc[["contrast_B"]] <- lapply(comparisons, function(x) paste(.unv(strsplit(x[[2]], "[ ,]")[[1]]),
collapse = ", "))
if (nrow(resultPostHoc[[1]]) > 1 && any(grepl(attr(resultPostHoc[[1]], "mesg"), pattern = "P value adjustment")))
postHocContainer[[thisVarNameRef]]$addFootnote(.getCorrectionFootnoteAnova(resultPostHoc[[1]],
(options$postHocCi && isFALSE(options$postHocTypeStandardBootstrap))))
avFootnote <- attr(resultPostHoc, "mesg")[grep(attr(resultPostHoc, "mesg"), pattern = "Results are averaged")]
if (length(avFootnote) != 0) {
avTerms <- .unv(strsplit(gsub(avFootnote, pattern = "Results are averaged over the levels of: ", replacement = ""),
", ")[[1]])
postHocContainer[[thisVarNameRef]]$addFootnote(gettextf("Results are averaged over the levels of: %s", paste(avTerms, collapse = ", ")))
}
if (options[["postHocConditionalTable"]]) {
resultPostHoc[[".isNewGroup"]] <- !duplicated(resultPostHoc[[byVariable[termIndex]]])
} else {
resultPostHoc[[".isNewGroup"]] <- !duplicated(resultPostHoc[["contrast_A"]])
}
postHocContainer[[thisVarNameRef]]$setData(resultPostHoc)
if (options$postHocSignificanceFlag)
.anovaAddSignificanceSigns(someTable = postHocContainer[[thisVarNameRef]],
allPvalues = resultPostHoc[c("bonferroni", "scheffe", "tukey", "holm")],
resultRowNames = rownames(resultPostHoc))
}
if (isTRUE(options[["postHocLetterTable"]])) {
letterResult <- multcomp::cld(referenceGrid[[thisVarName]],
method = "pairwise",
Letters = letters,
alpha = options[["postHocLetterAlpha"]])
letterResult <- letterResult[c(postHocVariables[[postHocVarIndex]], ".group")]
colnames(letterResult)[ncol(letterResult)] <- "Letter"
letterResult <- letterResult[order(as.numeric(rownames(letterResult))), ]
postHocContainer[[paste0(thisVarName, "LetterTable")]]$setData(letterResult)
}
}
return()
}
.rmAnovaContrastTable <- function(rmAnovaContainer, longData, options, ready) {
if (!ready || is.null(options$contrasts))
return()
#contrasts are encoded so first decode that so we can later check for things like "none" and "custom"
decodedContrasts <- list()
for (i in 1:length(options$contrasts)) {
options$contrasts[[i]]$decoded <- decodedContrasts[[i]] <- jaspBase::decodeColNames(options$contrasts[[i]]$contrast)
}
if (!is.null(rmAnovaContainer[["contrastContainer"]]) || all(grepl("none", decodedContrasts)))
return()
contrastContainer <- createJaspContainer(title = gettext("Contrast Tables"))
contrastContainer$dependOn(c("contrasts", "contrastCiLevel", "contrastEffectSize",
"contrastCi", "customContrasts"))
for (contrast in options$contrasts) {
if (contrast$decoded != "none") {
contrastType <- unlist(strsplit(contrast$contrast, ""))
contrastType[1] <- toupper(contrastType[1])
contrastType <- paste0(contrastType, collapse = "")
if (length(contrast$variable) == 1) {
contrastVariable <- contrast$variable
} else {
contrastVariable <- paste(contrast$variable, collapse = " \u273B ")
}
myTitle <- gettextf("%1$s Contrast - %2$s", contrastType, contrastVariable)
contrastContainerName <- paste0(contrast$contrast, "Contrast_", paste(contrast$variable, collapse = ":"))
dfType <- if (length(contrast$variable) > 1 || contrast$decoded == "custom") "number" else "integer"
contrastContainer[[contrastContainerName]] <- createJaspContainer()
contrastContainer[[contrastContainerName]][["contrastTable"]] <- .createContrastTableAnova(myTitle,
options,
dfType)
}
}
rmAnovaContainer[["contrastContainer"]] <- contrastContainer
if (!ready || rmAnovaContainer$getError())
return()
referenceGrid <- rmAnovaContainer[["referenceGrid"]]$object
for (contrast in options$contrasts) {
contrastContainerName <- paste0(contrast$contrast, "Contrast_", paste(contrast$variable, collapse = ":"))
if (contrast$decoded != "none") {
if (contrast$decoded == "custom") {
customContrastSetup <- options$customContrasts[[which(sapply(options$customContrasts,
function(x) all(contrast$variable %in% x$value) &&
length(contrast$variable) == length(x$value)))]]
} else {
customContrastSetup <- NULL
}
if (length(contrast$variable) == 1) {
column <- longData[[ .v(contrast$variable) ]]
} else {
column <- factor(apply(longData[ .v(contrast$variable) ], 1, paste, collapse =", "))
}
contrastMatrix <- .createContrastAnova(column, contrast$decoded, customContrastSetup)
contrCoef <- lapply(as.data.frame(contrastMatrix), as.vector)
if (contrast$decoded != "custom") {
contrCoef <- lapply(as.data.frame(contrastMatrix), as.vector)
names(contrCoef) <- .anovaContrastCases(column, contrast$decoded)
} else {
contrCoef <- apply(contrastMatrix, 1, list)
}
contrastResult <- try(emmeans::contrast(referenceGrid[[paste(contrast$variable, collapse = ":")]], contrCoef),
silent = TRUE)
contrCoefEmmeans <- coef(contrastResult)
colnames(contrCoefEmmeans) <- c(contrast$variable, paste("Comparison", 1: (ncol(contrCoefEmmeans) - length(contrast$variable))))
fullModel <- rmAnovaContainer[["anovaResult"]]$object$fullModel
effectSizeResult <- as.data.frame(emmeans::eff_size(referenceGrid[[paste(contrast$variable, collapse = ":")]],
sigma = sqrt(mean(sigma(fullModel$lm)^2)),
edf = df.residual(fullModel$lm),
method = contrCoef,
level = options[["contrastCiLevel"]]))
if (contrast$decoded == "custom") {
if (isTryError(contrastResult)) {
if (grepl(contrastResult[1], pattern = "Nonconforming number")) {
contrastContainer[[contrastContainerName]]$setError(gettext("Please specify an additional contrast."))
} else if (grepl(contrastResult[1], pattern = "number of contrast matrix rows")) {
contrastContainer[[contrastContainerName]]$setError(gettext("Wrong number of custom contrast matrix rows."))
}
return()
} else if (any(apply(contrastMatrix, 1, function(x) all(x == 0) ))) {
contrastContainer[[contrastContainerName]]$setError(gettext("Please specify non-zero contrast weights."))
return()
}
}
if (length(contrastResult@misc$avgd.over) != 0)
contrastContainer[[contrastContainerName]][["contrastTable"]]$addFootnote(
message = gettextf("Results are averaged over the levels of: %s", paste(.unv(contrastResult@misc$avgd.over), collapse = ", ")))
contrastResult <- cbind(contrastResult, confint(contrastResult, level = options$contrastCiLevel)[,5:6])
contrastResult[["Comparison"]] <- contrastResult[["contrast"]]
contrastResult[["df"]] <- round(contrastResult[["df"]], 3)
contrastResult[["cohenD"]] <- effectSizeResult[["effect.size"]]
contrastResult[["cohenD_LowerCI"]] <- effectSizeResult[["lower.CL"]]
contrastResult[["cohenD_UpperCI"]] <- effectSizeResult[["upper.CL"]]
if (contrast$decoded == "custom" | length(contrast$variable) > 1) {
contrastResult$Comparison <- 1:nrow(contrastResult)
weightType <- if (all(apply(contrastMatrix, 2, function(x) x %% 1 == 0))) "integer" else "number"
contrastContainer[[contrastContainerName]][["customCoefTable"]] <- .createContrastCoefficientsTableAnova(contrast,
contrCoefEmmeans,
weightType)
}
contrastContainer[[contrastContainerName]][["contrastTable"]]$setData(contrastResult)
}
}
}
.rmAnovaMarginalMeansTable <- function(rmAnovaContainer, dataset, options, ready) {
if (!is.null(rmAnovaContainer[["marginalMeansContainer"]]) || length(options$marginalMeanTerms) == 0)
return ()
marginalMeansContainer <- createJaspContainer(title = gettext("Marginal Means"))
marginalMeansContainer$dependOn(c("marginalMeanTerms", "marginalMeanComparedToZero", "marginalMeanCiCorrection",
"marginalMeanBootstrap", "marginalMeanBootstrapSamples"))
rmAnovaContainer[["marginalMeansContainer"]] <- marginalMeansContainer
marginalTerms <- unlist(options$marginalMeanTerms, recursive = FALSE)
for (term in marginalTerms) {
thisVarName <- paste(term, collapse = " \u273B ")
individualTerms <- term
if (any(term %in% unlist(options$withinModelTerms))) dfType <- "number" else dfType <- "integer"
marginalMeansContainer[[paste0(.v(term), collapse = ":")]] <- .createMarginalMeansTableAnova(thisVarName, options,
individualTerms,
options$marginalMeanBootstrap,
dfType = dfType)
}
if (!ready || rmAnovaContainer$getError())
return()
fullModel <- rmAnovaContainer[["anovaResult"]]$object$fullModel
for (term in marginalTerms) {
termBase64 <- paste0(.v(term), collapse = ":")
formula <- as.formula(paste("~", termBase64))
if(options$marginalMeanCiCorrection == "bonferroni") {
adjMethod <- "bonferroni"
} else if(options$marginalMeanCiCorrection == "sidak") {
adjMethod <- "sidak"
} else {
adjMethod <- "none"
}
marginalResult <- summary(emmeans::lsmeans(fullModel, formula), adjust = adjMethod, infer = c(TRUE,TRUE))
marginalResult[[".isNewGroup"]] <- FALSE
marginalResult[[".isNewGroup"]][which(marginalResult[, 1] == marginalResult[1, 1])] <- TRUE
names(marginalResult)[1:length(term)] <- term
for (var in term)
marginalResult[[var]] <- .unv(marginalResult[[var]])
### Bootstrapping
if (options$marginalMeanBootstrap) {
startProgressbar(options[["marginalMeanBootstrapSamples"]],
label = gettext("Bootstrapping Marginal Means"))
nRows <- nrow(marginalResult)
bootstrapMarginalMeans <- try(boot::boot(data = dataset, statistic = .bootstrapMarginalMeansRmAnova,
R = options[["marginalMeanBootstrapSamples"]],
options = options,
nRows = nRows,
termLength = length(term),
formula = formula), silent = TRUE)
if (class(bootstrapMarginalMeans) == "try-error") {
marginalMeansContainer[[termBase64]]$setError(bootstrapMarginalMeans)
next
}
bootstrapSummary <- summary(bootstrapMarginalMeans)
ci.fails <- FALSE
# bootstrapMarginalMeansCI <- confint(bootstrapMarginalMeans, level = 0.95, type = c("bca"))
bootstrapMarginalMeansCI <- t(sapply(1:nrow(bootstrapSummary), function(index){
res <- try(boot::boot.ci(boot.out = bootstrapMarginalMeans, conf = 0.95, type = "bca",
index = index)[['bca']][1,4:5])
if (!inherits(res, "try-error")){
return(res)
} else {
ci.fails <<- TRUE
return(c(NA, NA))
}
}))
if (ci.fails)
marginalMeansContainer[[termBase64]]$addFootnote(message = gettext("Some confidence intervals could not be computed. Possibly too few bootstrap replicates."))
marginalResult[["lower.CL"]] <- bootstrapMarginalMeansCI[,1]
marginalResult[["upper.CL"]] <- bootstrapMarginalMeansCI[,2]
marginalResult[["lsmean"]] <- bootstrapSummary[["bootMed"]]
marginalResult[["bias"]] <- bootstrapSummary[["bootBias"]]
marginalResult[["SE"]] <- bootstrapSummary[["bootSE"]]
marginalMeansContainer[[termBase64]]$addFootnote(message = gettextf("Bootstrapping based on %s successful replicates.", as.character(bootstrapSummary$R[1])))
}
marginalMeansContainer[[termBase64]]$setData(as.data.frame(marginalResult))
}
return()
}
.bootstrapMarginalMeansRmAnova <- function(data, indices, options, nRows, termLength, formula){
# indices <- sample(indices, replace = TRUE)
resamples <- data[indices, , drop=FALSE]
dataset <- .shortToLong(resamples, options$repeatedMeasuresFactors, options$repeatedMeasuresCells,
c(options$betweenSubjectFactors, options$covariates),
dependentName = .BANOVAdependentName, subjectName = .BANOVAsubjectName)
anovaModelBoots <- .rmAnovaComputeResults(dataset, options, returnResultsEarly = TRUE)$result # refit model
if (!is.null(anovaModelBoots[["tryResult"]]) || is.null(anovaModelBoots))
return(rep(NA, termLength))
resultBoots <- summary(emmeans::lsmeans(anovaModelBoots, formula), infer = c(FALSE,FALSE))
progressbarTick()
if(length(resultBoots$lsmean) == nRows){ # ensure that the bootstrap has all levels
return(resultBoots$lsmean)
} else {
return(rep(NA, termLength))
}
}
.rmAnovaFriedmanTable <- function(rmAnovaContainer, longData, options, ready) {
if (!is.null(rmAnovaContainer[["nonparametricContainer"]]) || length(options$friedmanWithinFactor) == 0)
return ()
rmAnovaContainer[["nonparametricContainer"]] <- createJaspContainer(gettext("Nonparametrics"))
rmAnovaContainer[["nonparametricContainer"]]$dependOn(c("friedmanWithinFactor",
"friedmanBetweenFactor"))
friedmanTable <- createJaspTable(title = gettext("Friedman Test"))
friedmanTable$addColumnInfo(name="Factor", title=gettext("Factor"), type="string")
friedmanTable$addColumnInfo(name="chiSqr", title=gettext("\u03a7\u00b2<sub>F</sub>"), type="number")
friedmanTable$addColumnInfo(name="df", title=gettext("df"), type="integer")
friedmanTable$addColumnInfo(name="p", title=gettext("p"), type="pvalue")
friedmanTable$addColumnInfo(name="kendall", title=gettext("Kendall's W"), type="number")
rmAnovaContainer[["nonparametricContainer"]][["friedmanTable"]] <- friedmanTable
if (!ready || rmAnovaContainer$getError())
return()
withinTerms <- options$friedmanWithinFactor
betweenTerm <- options$friedmanBetweenFactor
withinTerms.base64 <- .v(withinTerms)
betweenTerms.base64 <- .v(betweenTerm)
result <- list()
if( any(!(withinTerms %in% unlist(options$withinModelTerms))) ||
(betweenTerm %in% unlist(options$withinModelTerms)) ) {
friedmanTable$setError(gettext("Please specify appropriate terms for the Friedman/Durbin test."))
return()
}
if (identical(betweenTerm, "")) {
betweenTerms.base64 <- .BANOVAsubjectName
}
rows <- list()
for (withinTermIndex in 1:length(withinTerms)) {
groups <- as.factor(longData[, withinTerms.base64[withinTermIndex]])
blocks <- as.factor(longData[, betweenTerms.base64])
y <- longData[, .BANOVAdependentName]
useDurbin <- any(table(groups, blocks) < 1) && length(unique(table(groups))) == 1
if (any(table(groups, blocks) > 1)) {
friedmanTable$setError(gettextf("Not an unreplicated complete block design."))
return()
}
t <- nlevels(groups)
b <- nlevels(blocks)
r <- unique(table(groups))
k <- unique(table(blocks))
if (length(r) == 1 && length(k) == 1) {
rankPerBlock <- unlist(tapply(y, blocks, rank))
rankPerGroup <- unlist(tapply(y, groups, rank))
rankJ <- tapply(rankPerBlock, groups, sum)
sumRanks <- sum(rankPerBlock^2)
cVal <- (b * k * (k + 1)^2) / 4
dVal <- sum(rankJ^2) - r * cVal
testStatOne <- (t - 1) / (sumRanks - cVal) * dVal
testStatTwo <- (testStatOne / (t - 1)) / ((b * k - b - testStatOne) / (b * k - b - t + 1))
## Code from PMCMRplus
dfChi <- t - 1
dfOneF <- k - 1
dfTwoF <- b * k - b - t + 1
pValOne <- pchisq(testStatOne, dfChi, lower.tail = FALSE)
pValTwo <- pf(testStatTwo, dfOneF, dfTwoF, lower.tail = FALSE)
# Kendall W, from descTools https://github.com/cran/DescTools/blob/513a58f635798dacf49982c396e2af9a81f3491d/R/StatsAndCIs.r#L5553-L5625
ratings <- t(do.call(cbind, tapply(y, groups, cbind)))
ratings <- as.matrix(na.omit(ratings))
ns <- nrow(ratings)
nr <- ncol(ratings)
ratings.rank <- apply(ratings, 2, rank)
tieAdjust <- 0
for (i in 1:nr) {
rater <- table(ratings.rank[,i])
ties <- rater[rater>1]
l <- as.numeric(ties)
tieAdjust <- tieAdjust + sum(l^3-l)
}
kendallWcor <- (12 * var(apply(ratings.rank, 1, sum)) * (ns - 1)) /
(nr^2 * (ns^3 - ns) - nr * tieAdjust)
row <- list()
row[["Factor"]] <- withinTerms[withinTermIndex]
row[["chiSqr"]] <- testStatOne
row[["df"]] <- dfChi
row[["p"]] <- pValOne
row[["kendall"]] <- kendallWcor
if (useDurbin) {
friedmanTable$title <- gettext("Durbin Test")
row[["F"]] <- testStatTwo
row[["dfnum"]] <- dfOneF
row[["dfden"]] <- dfTwoF
row[["pF"]] <- pValTwo
if (withinTermIndex == 1) {
friedmanTable$addColumnInfo(name="F", title=gettext("F"), type="number")
friedmanTable$addColumnInfo(name="dfnum", title=gettext("df num"), type="integer")
friedmanTable$addColumnInfo(name="dfden", title=gettext("df den"), type="integer")
friedmanTable$addColumnInfo(name="pF", title=gettext("p<sub>F</sub>"), type="pvalue")
}
}
rows[[withinTermIndex]] <- row
} else {
friedmanTable$setError("Specified ANOVA design is not balanced.")
return()
}
}
friedmanTable$setData(as.data.frame(do.call(rbind,rows)))
return()
}
.rmAnovaConoverTable <- function(rmAnovaContainer, longData, options, ready) {
if (!is.null(rmAnovaContainer[["nonparametricContainer"]][["conoverContainer"]]) || options$conoverTest == FALSE)
return ()
conoverContainer <- createJaspContainer(gettext("Conover Test"))
rmAnovaContainer[["nonparametricContainer"]][["conoverContainer"]] <- conoverContainer
conoverContainer$dependOn("conoverTest")
createConoverTable <- function(myTitle) {
conoverTable <- createJaspTable(title = gettextf("Conover's Post Hoc Comparisons - %s", myTitle))
conoverTable$addColumnInfo(name="(I)", title="", type="string", combine=TRUE)
conoverTable$addColumnInfo(name="(J)", title="", type="string")
conoverTable$addColumnInfo(name="t", title=gettext("T-Stat"), type="number")
conoverTable$addColumnInfo(name="df", title=gettext("df"), type="integer")
conoverTable$addColumnInfo(name="wA", title=gettext("W<sub>i</sub>"), type="number")
conoverTable$addColumnInfo(name="wB", title=gettext("W<sub>j</sub>"), type="number")
conoverTable$addColumnInfo(name="rbs", title=gettext("r<sub>rb</sub>"), type="number")
conoverTable$addColumnInfo(name="pval", title=gettext("p"), type="pvalue")
conoverTable$addColumnInfo(name="bonferroni", title=gettext("p<sub>bonf</sub>"), type="pvalue")
conoverTable$addColumnInfo(name="holm", title=gettext("p<sub>holm</sub>"), type="pvalue")
return(conoverTable)
}
if (!ready || rmAnovaContainer$getError())
return()
groupingVariables <- unlist(options$friedmanWithinFactor)
blockingVar <- ifelse( identical(options$friedmanBetweenFactor, ""), .BANOVAsubjectName, .v(options$friedmanBetweenFactor))
y <- longData[, .BANOVAdependentName]
for (groupingVar in groupingVariables) {
conoverTable <- createConoverTable(groupingVar)
noteBlockName <- ifelse(blockingVar == .BANOVAsubjectName, "subject", blockingVar)
conoverTable$addFootnote(gettextf("Grouped by %s.", noteBlockName))
conoverTable$addFootnote(message = gettext("Rank-biserial correlation based on individual signed-rank tests."))
rows <- list()
groups <- as.factor(longData[, .v(groupingVar)])
blocks <- as.factor(longData[, blockingVar])
groupNames <- levels(groups)
## Code from PMCMRplus
t <- nlevels(groups)
b <- nlevels(blocks)
r <- unique(table(groups))
k <- unique(table(blocks))
rij <- unlist(tapply(y, blocks, rank))
Rj <- unname(tapply(rij, groups, sum))
df <- b * k - b - t + 1
S2 <- 1 / ( 1 * t -1 ) * (sum(rij^2) - t * b * ( t + 1)^2 / 4)
T2 <- 1 / S2 * (sum((Rj - b * (t + 1) / 2)^2))
A <- S2 * (2 * b * (t - 1)) / ( b * t - t - b + 1)
B <- 1 - T2 / (b * (t- 1))
denom <- sqrt(A) * sqrt(B)
for (i in 1:(t - 1)) {
for (j in (i + 1):t) {
row <- list("(I)"=groupNames[[i]], "(J)"=groupNames[[j]])
diff <- abs(Rj[i] - Rj[j])
tval <- diff / denom
pval <- 2 * pt(q = abs(tval), df = df, lower.tail=FALSE)
# Rank biserial correlation calculation
y1 <- y[groups == levels(groups)[i]]
y2 <- y[groups == levels(groups)[j]]
res <- stats::wilcox.test(y1, y2, paired = TRUE, conf.int = FALSE)$statistic
nd <- sum(y1 - y2 != 0)
maxw <- (nd * (nd + 1)) / 2
rbs <- as.numeric((res / maxw) * 2 - 1)
# rbs <- (Rj[i] - n_i * (n_i + 1) / 2 - (Rj[j] - n_j * (n_j + 1) / 2)) / (n_i * n_j)
row[["t"]] <- tval
row[["wA"]] <- Rj[i]
row[["wB"]] <- Rj[j]
row[["pval"]] <- pval
row[["bonferroni"]] <- pval
row[["holm"]] <- pval
row[["df"]] <- df
row[["rbs"]] <- rbs
rows[[length(rows)+1]] <- row
}
row[[".isNewGroup"]] <- length(rows) == 0
}
allP <- unlist(lapply(rows, function(x) x$p))
allBonf <- p.adjust(allP, method = "bonferroni")
allHolm <- p.adjust(allP, method = "holm")
for (p in 1:length(rows)) {
rows[[p]][['bonferroni']] <- allBonf[p]
rows[[p]][['holm']] <- allHolm[p]
}
conoverTable$setData(as.data.frame(do.call(rbind, rows)))
rmAnovaContainer[["nonparametricContainer"]][["conoverContainer"]][[groupingVar]] <- conoverTable
}
return()
}
.rmAnovaSimpleEffects <- function(rmAnovaContainer, dataset, longData, options, ready) {
if (!is.null(rmAnovaContainer[["simpleEffectsContainer"]]) || identical(options$simpleMainEffectFactor, "") ||
identical(options$simpleMainEffectModeratorFactorOne, ""))
return()
rmAnovaContainer[["simpleEffectsContainer"]] <- createJaspContainer(title = gettext("Simple Main Effects"),
dependencies = c("simpleMainEffectFactor",
"simpleMainEffectModeratorFactorOne",
"simpleMainEffectModeratorFactorTwo",
"simpleMainEffectErrorTermPooled"))
simpleEffectsTable <- createJaspTable(title = gettextf("Simple Main Effects - %s", options$simpleMainEffectFactor))
rmAnovaContainer[["simpleEffectsContainer"]][["simpleEffectsTable"]] <- simpleEffectsTable
# the simple factor must appear in the model terms, but the moderator factors may be missing.
withinModelTerms <- unique(unlist(lapply(options[["withinModelTerms"]], `[[`, "components"), use.names = FALSE))
betweenModelTerms <- unique(unlist(lapply(options[["betweenModelTerms"]], `[[`, "components"), use.names = FALSE))
if (!(options[["simpleMainEffectFactor"]] %in% c(withinModelTerms, betweenModelTerms))) {
rmAnovaContainer[["simpleEffectsContainer"]]$setError(gettext("Moderator factors must also appear in the model terms!"))
return()
}
moderatorTerms <- c(options$simpleMainEffectModeratorFactorOne, options$simpleMainEffectModeratorFactorTwo[!identical(options$simpleMainEffectModeratorFactorTwo, "")])
nMods <- length(moderatorTerms)
simpleMainEffectFactor <- options[['simpleMainEffectFactor']]
simpleMainEffectFactorBase64 <- simpleMainEffectFactor
simpleEffectsTable[["title"]] <- gettextf("Simple Main Effects - %s", options$simpleMainEffectFactor)
simpleEffectsTable$addColumnInfo(name = "modOne", title = gettextf("Level of %s", moderatorTerms[1]),
type = "string", combine = TRUE)
if (nMods == 2)
simpleEffectsTable$addColumnInfo(name = "modTwo", title = gettextf("Level of %s", moderatorTerms[2]),
type = "string", combine = TRUE)
simpleEffectsTable$addColumnInfo(name = "SumSq", type = "number", title = gettext("Sum of Squares"))
simpleEffectsTable$addColumnInfo(name = "Df", type = "integer", title = gettext("df"))
simpleEffectsTable$addColumnInfo(name = "MeanSq", type = "number", title = gettext("Mean Square"))
simpleEffectsTable$addColumnInfo(name = "F", type = "number", title = gettext("F"))
simpleEffectsTable$addColumnInfo(name = "p", type = "pvalue", title = gettext("p"))
simpleEffectsTable$showSpecifiedColumnsOnly <- TRUE
.addSumSquaresFootnote(simpleEffectsTable, options)
simpleEffectsTable$addCitation("Howell, D. C. (2002). Statistical Methods for Psychology (8th. ed.). Pacific Grove, CA: Duxberry. ")
if (!ready || rmAnovaContainer$getError())
return()
lvls <- list()
factors <- list()
for (variable in moderatorTerms) {
factor <- longData[[ .v(variable) ]]
factors[[length(factors)+1]] <- factor
lvls[[variable]] <- levels(factor)
}
simpleEffectResult <- rev(expand.grid(rev(lvls), stringsAsFactors = FALSE))
colnames(simpleEffectResult) <- c("modOne", "modTwo")[1:nMods]
simpleEffectResult[[".isNewGroup"]] <- FALSE
emptyCaseIndices <- emptyCases <- NULL
allSimpleModels <- list()
fullResidualTable <- rmAnovaContainer[["anovaResult"]][["object"]][["residualTable"]][["None"]]
isMixedAnova <- length(options[['betweenModelTerms']]) > 0
isSimpleMainEffectFactorWithin <- !simpleMainEffectFactor %in% unlist(options[['betweenModelTerms']] )
isModeratorOneWithin <- !moderatorTerms[1] %in% unlist(options[['betweenModelTerms']] )
isModeratorTwoWithin <- !moderatorTerms[2] %in% unlist(options[['betweenModelTerms']] )
if (isMixedAnova && !isSimpleMainEffectFactorWithin) {
fullAnovaMS <- fullResidualTable["BetweenResidualResults", "Mean Sq"]
fullAnovaDf <- fullResidualTable["BetweenResidualResults", "num Df"]
} else {
fullAnovaMS <- fullResidualTable[simpleMainEffectFactorBase64, "Mean Sq"]
fullAnovaDf <- fullResidualTable[simpleMainEffectFactorBase64, "num Df"]
}
# Remove moderator factors from model terms
simpleOptions <- options
simpleOptions$betweenModelTerms <- options$betweenModelTerms[!(grepl(moderatorTerms[1], options$betweenModelTerms) |
grepl(moderatorTerms[nMods], options$betweenModelTerms))]
simpleOptions$withinModelTerms <- options$withinModelTerms[!(grepl(moderatorTerms[1], options$withinModelTerms) |
grepl(moderatorTerms[nMods], options$withinModelTerms))]
performBetweenAnova <- length(simpleOptions$withinModelTerms) == 0
if (performBetweenAnova) {
simpleOptions[["fixedFactors"]] <- simpleOptions[['betweenSubjectFactors']]
simpleOptions[["modelTerms"]] <- simpleOptions[['betweenModelTerms']]
simpleOptions[["dependent"]] <- .BANOVAdependentName
simpleOptions[["homogeneityCorrectionBrown"]] <- simpleOptions[["homogeneityCorrectionWelch"]] <- FALSE
simpleOptions[["homogeneityCorrectionNone"]] <- TRUE
}
emptyCaseIndices <- emptyCases <- NULL
for (i in 1:nrow(simpleEffectResult)) {
subsetStatement <- eval(parse(text=paste("longData$", .v(moderatorTerms), " == \"",
simpleEffectResult[i, 1:nMods],
"\"", sep = "", collapse = " & ")))
simpleDataset <- base::subset(longData, subsetStatement)
if (simpleEffectResult[i, nMods] == lvls[[ nMods ]][1])
simpleEffectResult[[i, ".isNewGroup"]] <- TRUE
if (nrow(simpleDataset) < 2 ||
nrow(unique(simpleDataset[simpleMainEffectFactorBase64])) < nrow(unique(longData[simpleMainEffectFactorBase64])) ||
length(unique(simpleDataset[["JaspColumn_.subject._Encoded"]])) == 1) {
emptyCaseIndices <- c(emptyCaseIndices, i)
emptyCases <- c(emptyCases, paste(simpleEffectResult[i, 1:nMods], collapse = ", "))
thisRow <- rep(NA, 5)
} else if (performBetweenAnova) {
.anovaModelContainer(rmAnovaContainer[["simpleEffectsContainer"]], simpleDataset, simpleOptions, TRUE)
.anovaResult(rmAnovaContainer[["simpleEffectsContainer"]], options = simpleOptions)
anovaResult <- rmAnovaContainer[["simpleEffectsContainer"]][["anovaResult"]]$object$result
rmAnovaContainer[["simpleEffectsContainer"]][["model"]] <- NULL
if (!options$simpleMainEffectErrorTermPooled) {
fullAnovaMS <- anovaResult["Residuals", "Mean Sq"]
fullAnovaDf <- anovaResult["Residuals", "Df"]
}
anovaResult <- anovaResult[simpleMainEffectFactorBase64, ]
df <- anovaResult[["Df"]]
MS <- anovaResult[["Mean Sq"]] <- anovaResult[["Sum Sq"]] / df
fStat <- MS / fullAnovaMS
p <- pf(fStat, df, fullAnovaDf, lower.tail = FALSE)
thisRow <- c(anovaResult[["Sum Sq"]], MS, df, fStat, p)
} else {
anovaResult <- .rmAnovaComputeResults(simpleDataset, simpleOptions, returnResultsEarly = TRUE)$model[simpleMainEffectFactorBase64, ]
if (!options$simpleMainEffectErrorTermPooled) {
fullAnovaMS <- anovaResult[["Error SS"]] / anovaResult[["den Df"]]
fullAnovaDf <- anovaResult[["den Df"]]
}
df <- anovaResult[["num Df"]]
MS <- anovaResult[["Mean Sq"]] <- anovaResult[["Sum Sq"]] / df
fStat <- MS / fullAnovaMS
p <- pf(fStat, df, fullAnovaDf, lower.tail = FALSE)
thisRow <- c(anovaResult[["Sum Sq"]], MS, df, fStat, p)
}
simpleEffectResult[i, c("SumSq", "MeanSq", "Df", "F", "p")] <- thisRow
}
if (!is.null(emptyCaseIndices)) {
simpleEffectsTable$addFootnote(gettextf("Not enough observations in cells %s.",
paste0(" (", emptyCases, ")", collapse = ",")))
}
simpleEffectsTable$setData(simpleEffectResult)
return()
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.