Skip to content

Commit 0aa5e55

Browse files
authored
Add export section for residuals and predictions (#428)
* Add export section for residuals and predictions * saved columns dependent on model terms * fix order in interactions * last tweaks to make it pretty * add option to choose type of residual, remove export from rm anova * fix unit test banova
1 parent f2448c7 commit 0aa5e55

File tree

8 files changed

+143
-8
lines changed

8 files changed

+143
-8
lines changed

R/ancova.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ AncovaInternal <- function(jaspResults, dataset = NULL, options) {
7272

7373
.BANOVAdescriptives(anovaContainer, dataset, options, list(noVariables=FALSE), "ANCOVA", ready)
7474

75+
.anovaExportResiduals(anovaContainer, dataset, options, ready)
76+
77+
.anovaExportPredictions(anovaContainer, dataset, options, ready)
78+
7579
return()
7680
}
7781

R/anovarepeatedmeasures.R

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,16 @@ AnovaRepeatedMeasuresInternal <- function(jaspResults, dataset = NULL, options)
193193
termsRM.base64 <- c()
194194
termsRM.normal <- c()
195195

196+
mainEffects <- unlist(lapply(options$withinModelTerms, function(term) {
197+
if (length(term$components) == 1) term$components
198+
}))
199+
196200
for (term in options$withinModelTerms) {
197201

198202
components <- unlist(term$components)
203+
if (length(components) > 1) # make sure that interaction gets defined in same order as model terms
204+
components <- mainEffects[mainEffects %in% components]
205+
199206
termRM.base64 <- paste(.v(components), collapse=":", sep="")
200207
termRM.normal <- paste(components, collapse=" \u273B ", sep="")
201208

R/commonAnovaFreq.R

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,50 @@
271271
}
272272

273273

274+
.anovaExportResiduals <- function(container, dataset, options, ready) {
275+
276+
if (ready &&
277+
isTRUE(options[["residualsSavedToData"]]) &&
278+
isTRUE(options[["residualsSavedToDataColumn"]] != "")) {
279+
280+
model <- container[["model"]]$object
281+
282+
residuals <- rep(NA, nrow(dataset)) # create vector with MA to account for missinginess
283+
284+
if (options[["residualsSavedToDataType"]] == "raw") {
285+
residuals[as.numeric(rownames(model[["model"]]))] <- model[["residuals"]] # extract residuals
286+
} else if (options[["residualsSavedToDataType"]] == "standard") {
287+
residuals[as.numeric(rownames(model[["model"]]))] <- rstandard(model)
288+
} else if (options[["residualsSavedToDataType"]] == "student") {
289+
residuals[as.numeric(rownames(model[["model"]]))] <- rstudent(model)
290+
}
291+
292+
container[["residualsSavedToDataColumn"]] <- createJaspColumn(columnName = options[["residualsSavedToDataColumn"]])
293+
container[["residualsSavedToDataColumn"]]$dependOn(options = c("residualsSavedToDataColumn", "residualsSavedToData",
294+
"residualsSavedToDataType", "modelTerms"))
295+
container[["residualsSavedToDataColumn"]]$setScale(residuals)
296+
297+
}
298+
299+
}
300+
301+
.anovaExportPredictions <- function(container, dataset, options, ready) {
302+
303+
if (ready &&
304+
isTRUE(options[["predictionsSavedToData"]]) &&
305+
isTRUE(options[["predictionsSavedToDataColumn"]] != "")) {
306+
307+
308+
model <- container[["model"]]$object
309+
310+
predictions <- rep(NA, nrow(dataset)) # create vector with MA to account for missinginess
311+
predictions[as.numeric(rownames(model[["model"]]))] <- model[["fitted.values"]] # extract predictions
312+
313+
container[["predictionsSavedToDataColumn"]] <- createJaspColumn(columnName = options[["predictionsSavedToDataColumn"]])
314+
container[["predictionsSavedToDataColumn"]]$dependOn(options = c("predictionsSavedToDataColumn", "predictionsSavedToData", "modelTerms"))
315+
container[["predictionsSavedToDataColumn"]]$setScale(predictions)
316+
317+
}
318+
319+
}
320+

inst/qml/Ancova.qml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,5 @@ Form
112112
source: ["fixedFactors", "randomFactors"]
113113
}
114114

115+
Classical.Export { id: exportComponent}
115116
}

inst/qml/Anova.qml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,7 @@ Form
108108
{
109109
source: ["fixedFactors", "randomFactors"]
110110
}
111+
112+
Classical.Export { id: exportComponent}
113+
111114
}

inst/qml/AnovaRepeatedMeasures.qml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ Form
7272
label: qsTr("Pool error term for follow-up tests")
7373
checked: false
7474
}
75+
7576
}
7677

7778
Section

inst/qml/common/classical/Export.qml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// Copyright (C) 2013-2022 University of Amsterdam
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public
15+
// License along with this program. If not, see
16+
// <http://www.gnu.org/licenses/>.
17+
//
18+
19+
import QtQuick
20+
import JASP
21+
import JASP.Controls
22+
import "./" as Classical
23+
24+
25+
Section
26+
{
27+
title: qsTr("Export")
28+
29+
Group
30+
{
31+
CheckBox
32+
{
33+
id: residualsSavedToData
34+
name: "residualsSavedToData"
35+
text: qsTr("Append residuals to data")
36+
37+
ComputedColumnField
38+
{
39+
name: "residualsSavedToDataColumn"
40+
text: qsTr("Column name")
41+
placeholderText: qsTr("e.g., residuals")
42+
fieldWidth: 120
43+
enabled: residualsSavedToData.checked
44+
}
45+
46+
RadioButtonGroup
47+
{
48+
title: qsTr("Residuals type")
49+
name: "residualsSavedToDataType"
50+
RadioButton { value: "raw"; label: qsTr("Raw"); checked: true }
51+
RadioButton { value: "standard"; label: qsTr("Studentized") }
52+
RadioButton { value: "student"; label: qsTr("Standardized") }
53+
}
54+
}
55+
56+
CheckBox
57+
{
58+
id: predictionsSavedToData
59+
name: "predictionsSavedToData"
60+
text: qsTr("Append predictions to data")
61+
62+
ComputedColumnField
63+
{
64+
name: "predictionsSavedToDataColumn"
65+
text: qsTr("Column name")
66+
placeholderText: qsTr("e.g., predictions")
67+
fieldWidth: 120
68+
enabled: predictionsSavedToData.checked
69+
}
70+
}
71+
}
72+
}

tests/testthat/test-anovarepeatedmeasuresbayesian.R

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,15 +169,15 @@ test_that("Analysis handles errors", {
169169
test_that("Analysis fails gracefully if some models error", {
170170

171171
options <- initOpts("AnovaRepeatedMeasuresBayesian")
172-
options$covariates = list("contNormal")
173-
options$betweenSubjectFactors = list("contBinom")
172+
options$covariates = "contNormal"
173+
options$betweenSubjectFactors = "contBinom"
174174
options$effects <- TRUE
175-
options$modelTerms = list(list(components = list("RM_FACTOR_1"), isNuisance = FALSE),
176-
list(components = list("contBinom"), isNuisance = FALSE),
177-
list(components = list("contNormal"), isNuisance = FALSE),
178-
list(components = list("RM_FACTOR_1", "contBinom"), isNuisance = FALSE))
179-
options$repeatedMeasuresCells = list("contcor1", "contcor2")
180-
options$repeatedMeasuresFactors = list(list(levels = list("Level 1", "Level 2"), name = "RM_FACTOR_1"))
175+
options$modelTerms = list(list(components = "RM_FACTOR_1", isNuisance = FALSE),
176+
list(components = "contBinom", isNuisance = FALSE),
177+
list(components = "contNormal", isNuisance = FALSE),
178+
list(components = c("RM_FACTOR_1", "contBinom"), isNuisance = FALSE))
179+
options$repeatedMeasuresCells = c("contcor1", "contcor2")
180+
options$repeatedMeasuresFactors = list(list(levels = c("Level 1", "Level 2"), name = "RM_FACTOR_1"))
181181

182182
# NOTE: the option below makes BayesFactor return NaN as BF for models with covariates.
183183
# It's a nice hack to test how gracefully the analysis recovers when some but not all BFs could be computed.

0 commit comments

Comments
 (0)