Appendix A — Analysis Skeleton (R Template)
Copy-paste starting point for a new JASP analysis file. This follows the patterns described in Chapter 5. Assumes preloadData: true (the default) in Description.qml, so the dataset argument is already loaded.
#
# Copyright (C) 2024 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.
#
# Main entry point ----
# The function name must match the `func` field in Description.qml.
# Receives: jaspResults (persistent output container),
# dataset (preloaded data frame),
# options (named list of user selections from QML).
MyAnalysis <- function(jaspResults, dataset, options) {
# 1. Can we compute?
ready <- length(options[["variables"]]) > 0
# 2. Validate
if (ready)
.myAnalysisCheckErrors(dataset, options)
# 3. Compute (stores result in jaspResults)
if (ready)
.myAnalysisComputeResults(jaspResults, dataset, options)
# 4. Create output — each function writes to jaspResults in place
.myAnalysisMainTable(jaspResults, options, ready)
.myAnalysisPlot( jaspResults, options, ready)
return()
}
# Error checking ----
.myAnalysisCheckErrors <- function(dataset, options) {
.hasErrors(dataset,
type = c("observations", "variance", "infinity"),
all.target = options[["variables"]],
observations.amount = "< 2",
exitAnalysisIfErrors = TRUE
)
}
# Compute (cached) ----
.myAnalysisComputeResults <- function(jaspResults, dataset, options) {
# Already cached? Nothing to do.
if (!is.null(jaspResults[["myAnalysisState"]]))
return()
# --- expensive computation goes here ---
results <- list(
model = lm(as.formula(paste(options[["dependent"]], "~ .")),
data = dataset)
)
# Store in jaspResults — invalidated automatically when dependencies change
state <- createJaspState(results)
state$dependOn(c("dependent", "variables"))
jaspResults[["myAnalysisState"]] <- state
}
# Table ----
.myAnalysisMainTable <- function(jaspResults, options, ready) {
# 1. Skip if already cached
if (!is.null(jaspResults[["mainTable"]])) return()
# 2. Create & configure
table <- createJaspTable(title = gettext("Results"))
table$dependOn(c("variables", "dependent"))
# 3. Define columns
table$addColumnInfo(name = "term", title = gettext("Term"), type = "string")
table$addColumnInfo(name = "estimate", title = gettext("Estimate"), type = "number")
table$addColumnInfo(name = "se", title = gettext("Std. Error"), type = "number")
table$addColumnInfo(name = "p", title = "p", type = "pvalue")
# 4. Attach — displays an empty placeholder immediately
jaspResults[["mainTable"]] <- table
# 5. Not ready yet? Show the placeholder (dots)
if (!ready) return()
# 6. Retrieve cached results and fill
results <- jaspResults[["myAnalysisState"]]$object
coefTab <- summary(results$model)$coefficients
for (i in seq_len(nrow(coefTab))) {
table$addRows(list(
term = rownames(coefTab)[i],
estimate = coefTab[i, "Estimate"],
se = coefTab[i, "Std. Error"],
p = coefTab[i, "Pr(>|t|)"]
))
}
}
# Plot ----
.myAnalysisPlot <- function(jaspResults, options, ready) {
# Only create when the user enables the checkbox
if (!options[["residualPlot"]]) return()
if (!is.null(jaspResults[["residualPlot"]])) return()
plot <- createJaspPlot(title = gettext("Residuals vs. Fitted"),
width = 480, height = 320)
plot$dependOn(c("variables", "dependent", "residualPlot"))
jaspResults[["residualPlot"]] <- plot
if (!ready) return()
# Retrieve cached results
results <- jaspResults[["myAnalysisState"]]$object
plotData <- data.frame(
fitted = fitted(results$model),
residuals = residuals(results$model)
)
plot$plotObject <- ggplot2::ggplot(plotData,
ggplot2::aes(x = .data[["fitted"]], y = .data[["residuals"]])) +
ggplot2::geom_point() +
ggplot2::geom_hline(yintercept = 0, linetype = "dashed") +
ggplot2::labs(x = gettext("Fitted values"), y = gettext("Residuals"))
}A.0.1 Key patterns
| Pattern | Why |
|---|---|
Public function (MyAnalysis) has no . prefix |
Called from QML via Description.qml |
Private helpers start with .myAnalysis |
Hidden from NAMESPACE; prefix avoids name collisions across modules |
ready flag checked first |
Lets output functions show empty placeholders before input is complete |
dataset provided by JASP |
preloadData: true in Description.qml — data arrives ready to use, no read function needed |
.myAnalysisComputeResults() stores model in jaspResults |
Called once from main function; table and plot retrieve it via jaspResults[["myAnalysisState"]]$object |
if (!is.null(...)) return() at start of output functions |
Avoids recreating cached output |
| Table attached before filling it | User sees a placeholder immediately; data fills in after computation |
$dependOn(...) on every element |
Tells JASP when to invalidate and recompute |
gettext() on all user-visible strings |
Enables translation (see Chapter 12) |
options[["x"]] (double brackets) |
Avoids partial-matching bugs from $ or [ |