Chapter 4 Advanced Results Objects
4.1 Introduction
So far we defined jmvcore::Table
objects. Tables, however, are not the only jmvcore::Results
object available to the module developer. The results will usually go into tables, but the result tables can be organized in a more rational way, or be created programmatically depending on the module needs. Here are a few useful objects.
4.2 Groups (jmvcore::Group)
A useful object is the jmvcore::Group
. Groups allow structuring the result tables into sections. Assume, for instance, we want to put our coefficients
and anova
tables in a Main Results
section of the output, and our effects
table under Additional Output
, to be filled later on with other tables.
We should change the r.yaml
file introducing the Group
object:
---
name: myRegression
title: Regression analysis
jrs: '1.1'
items:
- name: main
type: Group
title: Main Results
items:
- name: coefficients
title: Parameter Estimates (Coefficients)
type: Table
columns:
- name: var
title: "Variable"
type: text
...
## here the remaing of the columns as defined before
- name: anova
title: ANOVA table
type: Table
columns:
- name: var
title: "Variable"
type: text
...
## here the remaing of the columns as defined before
- name: additional
type: Group
title: Additional Results
items:
- name: effects
title: Effect Size Indices
type: Table
columns:
- name: var
title: "Variable"
type: text
...
## here the remaing of the columns as defined before
Notice that the definition of the tables has not changed, they are just organized under a Group
with property title
and items
.
The logic of interfacing R code with the tables via a SmartTable
object in file .b.R
does not change as well, the only difference is that we should change the name of the table. In the self$results
list, tables are now referred to as self$results$groupname$tablename
.
## same code as before here
...= function() {
.init
if (!is.something(self$options$dep) | !is.something(self$options$covs))
return()
# define the runner
$.runner<-Runner$new(self)
private
# set up the coefficients SmartTable
<-SmartTable$new(self$results$main$coefficients,private$.runner)
atable# put the SmartTable in the list
$.tables[[length(private$.tables)+1]]<-atable
private# set up the anova SmartTable
<-SmartTable$new(self$results$main$anova,private$.runner)
atable# put the SmartTable in the list
$.tables[[length(private$.tables)+1]]<-atable
private
<-SmartTable$new(self$results$additional$effects,private$.runner)
atable# put the SmartTable in the list
$.tables[[length(private$.tables)+1]]<-atable
private
# the remaining code does not change
...
Consequently, in the Runner.R
file their associated function should be named run_[groupname]_[tablename]
, because they [groupname]_[tablename]
nickname uniquely identifies each table. In Runner.R
code:
=function() {
run_main_coefficients
<- summary(self$model)
.summary <- .summary$coefficients
coeffs <- as.data.frame(coeffs)
coeffs names(coeffs) <- c("coef","se","t","p")
$var <- rownames(coeffs)
coeffsreturn(coeffs)
},=function() {
run_main_anova
<- as.data.frame(car::Anova(self$model,type=3))
.anova names(.anova) <- c("nothing","df1","test","p")
$df2 <- self$model$df.residual
.anova$var <- rownames(.anova)
.anovareturn(.anova)
},=function() {
run_additional_effects
<- effectsize::epsilon_squared(self$model)
eps <- data.frame(var=eps$Parameter,index="Epsilon^2",value=eps$Epsilon2)
eps_df <- effectsize::eta_squared(self$model)
eta <- data.frame(var=eta$Parameter,index="pEta^2",value=eta$Eta2)
eta_df <- effectsize::omega_squared(self$model)
omega <- data.frame(var=eta$Parameter,index="pOmega^2",value=omega$Omega2)
omega_df
<- rbind(eps_df,eta_df,omega_df)
tab return(tab)
},
You should now have your output organized in sections like this:
4.3 Arrays of tables (jmvcore::Array)
Arrays of tables are complex objects that allow for a lot of flexibility in setting up results tables. They contain tables like groups do, but the number of tables created in an array is flexible and depends on the input or the results. We can distinguish two typical cases. When the number of tables depends on an option defined in the GUI (in the .a.yaml
file), or when the number of tables depends on some calculation done in the R code, so not directly linked to an option. Let’s see the two cases in detail.
4.3.1 Array: known number of tables
Assume we want to produce a table for each covariate selected by the user. Say that for each covariate you want a separate table with the mean and standard deviation of the variable. Obviously, we do not know in advance how many independent variables the user will define, but as soon as the user defines them, jamovi can count the independent variables and know how many tables are needed. In our example, we can link the number of tables (items
) attached to the Array
object to the covs
option. This can be done directly in the yaml
code in .r.yaml
file.
#...#
- name: means
title: Mean and Standard deviation
type: Array
items: (covs)
template:
title: Variable - $key
type: Table
columns:
- name: var
title: "Variable"
type: text
- name: mean
title: "Mean"
type: number
- name: sd
title: "Sd"
type: number
Notice the items
property. We set it to (covs)
to signal jamovi
to take the contents of the option covs
from the input, and create one table for each item found in covs
. Thus, for each covariate selected by the user a Table
is created as specified in the template:
property. In the following example, I get two tables because I selected two covariates.
Notice the individual table title. It is set to the name of a covariate because in the title:
property we use the special key $key
, which means the name of the item found in items:
variable. All this is done automatically by jamovi
interpreter, so it is very easy and smooth to define arrays like this.
Now we can populated each table within the array. First, we go to the .b.R
file and define the array.
## same code as before here
#...#
= function() {
.init
## same code as before here
#...#
# set up the means SmartArray
<-SmartArray$new(self$results$means,private$.runner)
obj# put the SmartArray in the list
$.tables[[length(private$.tables)+1]]<-obj
private
# the remaining code does not change
#...#
Here we use a different jmvScaffold
object, called SmartArray. Its API is equivalent to the one of a
SmartTable, but passes all the defined properties to the children tables.
In the Runner.R
file we should set up a function named run_[arrayname]
to fill it. This function should return a list of data.frame, one for each item selected in the option associated with the array. In our example, the function should return a list of data.frame, one for each covariate selected by the users. Obviously, it is the developer responsibility to return a list of length equal to the number of items in the array.
# ... #
=function() {
run_means
<- lapply(self$analysis$options$covs, function (x) {
tabs <- mean(self$analysis$data[[x]],na.rm=TRUE)
m <- sd(self$analysis$data[[x]],na.rm=TRUE)
s data.frame(var=x,mean=m,sd=s)
})
return(tabs)
}
# ... #
Results should now be nice as expected.
4.3.2 Array: Unknown number of tables
Recall the results in
Assume now, for some silly reason, that we want additional tables with means and standard deviations only for covariates that have a significant effect on the dependent variable. In this case, we do not know how many tables we need until we run the analysis and check the results. jmvScaffold::SmartArray
can handle this case smoothly: It produces and fills as many tables as we pass to it with a list of data.frame. Let us see an example.
In the r.yaml
file, we define a new Array as the one before, but without the items:(covs)
property, because we do not know which of the covs
variable will require a table. We also put the array under a group, because it looks better in the output page.
#...#
- name: sig
type: Group
title: Significant covariates
items:
- name: means
title: ""
type: Array
template:
title: Variable - $key
type: Table
clearWith:
- dep
- covs
columns:
- name: var
title: "Variable"
type: text
- name: mean
title: "Mean"
type: number
- name: sd
title: "Sd"
type: number
In the Runner.R
file, we simply need to define a run_sig_means()
function to select the covariates that we want to display in the new tables.
#...#
=function() {
run_sig_means## select the covs ##
<- as.data.frame(summary(self$model)$coefficients)
results <- results[-1,]
results <- rownames(results[results[,4]<.05,])
whichcovs
## make the tables
<- lapply(whichcovs, function (x) {
tabs <- mean(self$analysis$data[[x]],na.rm=TRUE)
m <- sd(self$analysis$data[[x]],na.rm=TRUE)
s data.frame(var=x,mean=m,sd=s)
})
return(tabs)
}
Finaly, we add the new array to the list of smart objects in the .b.R
files.
## same code as before here
#...#
= function() {
.init
## same code as before here
#...#
# set up the sig_means SmartArray
<-SmartArray$new(self$results$sig$means,private$.runner)
obj# put the SmartArray in the list
$.tables[[length(private$.tables)+1]]<-obj
private
# the remaining code does not change
#...#
and we can check after re-installing that the module is working as intended.
Now pay attention to the tables titles. They are now Variable - 1
and Variable - 2
because we do not have any key defined. No problem, we can pass the keys to the SmartArray
as an attribute of the data we pass to it. Just add to the run_means()
function the line and you have your nice titles.
# ... #
=function() {
run_sig_means## select the covs ##
<- as.data.frame(summary(self$model)$coefficients)
results <- results[-1,]
results <- rownames(results[results[,4]<.05,])
whichcovs
## make the tables
<- lapply(whichcovs, function (x) {
tabs <- mean(self$analysis$data[[x]],na.rm=TRUE)
m <- sd(self$analysis$data[[x]],na.rm=TRUE)
s data.frame(var=x,mean=m,sd=s)
})attr(tabs,"keys")<-whichcovs ## passing the keys to the jamovi table
return(tabs)
}
# ... #
4.4 Other Results objects
jamovi offers other results objects, very important to develop modules such as Image
for diplaying plots, or Html
widget to display text using html
. They are not handled by jmvScaffold
and can easely be filled by accessing to them directly.
Information about these objects can be found in jamovi hub: Results .