Section author: Jonathon Love
7. Adding Plots¶
In this section, we’ll add a plot to the t-test analysis we’ve been developing in this series. A plot is another item to appear in the results, so we’ll add another entry into our
ttest
.r.yaml
-file:- name: ttest title: Independent Samples T-Test jrs: "1.1" items: - name: ttest title: Independent Samples T-Test type: Table rows: 1 columns: - name: var title: "" type: text - name: t type: number - name: df type: integer - name: p type: number format: zto,pvalue - name: plot title: Descriptives Plot type: Image width: 400 height: 300 renderFun: .plotSame as before, we define an item with a
name
,title
and atype
; in this case the type isImage
. Additionally, we definerenderFun
which is the name of the function responsible for rendering the image. Whatever we specify as the render function, we must add as a private member function tottestClass
inttest.b.R
:#' @export ttestClass <- R6::R6Class( "ttestClass", inherit = ttestBase, private = list( .run = function() { formula <- paste(self$options$dep, '~', self$options$group) formula <- as.formula(formula) results <- t.test(formula, self$data) table <- self$results$ttest table$setRow(rowNo=1, values=list( var=self$options$dep, t=results$statistic, df=results$parameter, p=results$p.value )) }, .plot=function(image, ...) { # <-- the plot function }) )
7.1. Adding ggplot2¶
We’re going to use
ggplot2
for plotting, so make sure you have that installed. To useggplot2
in this package / module, we need to add some entries into theDESCRIPTION
- andNAMESPACE
-files. We addggplot2
to the imports line in theDESCRIPTION
-file, so it reads:Imports: jmvcore, R6, ggplot2and we’ll add the following line into
NAMESPACE
:import(ggplot2)These entries are standard for using R code from other packages in a package. More information is available in Writing R Extensions.
Now we have
ggplot2
ready, we can proceed with using it in our analysis.
7.2. Implementing Plots¶
In jamovi modules, plotting occurs in two stages; first the data for the plot is prepared, then the plot is rendered. The two stages mean that if the image is resized, or the user requests a different file format, only the rendering needs to be performed again — the data preparation needs only to occur once.
For the t-test, we’re going to plot a mean for each of the groups, and the standard errors. In
ggplot2
, we’re required to assemble these ‘plot points’ into a data frame, which we will do as follows:means <- aggregate(formula, self$data, mean)[,2] ses <- aggregate(formula, self$data, function(x) sd(x)/sqrt(length(x)))[,2] sel <- means - ses # standard error lower bound seu <- means + ses # standard error upper bound levels <- base::levels(self$data[[self$options$group]]) plotData <- data.frame(level=levels, mean=means, sel=sel, seu=seu) ## level mean sel seu ## 1 OJ 20.66333 19.45733 21.86934 ## 2 VC 16.96333 15.45417 18.47250This plot data we assign to the image using the
setState()
function:image <- self$results$plot image$setState(plotData)``Next, we’ll add the plotting code into the
.plot()
function we created:.plot=function(image, ...) { plotData <- image$state plot <- ggplot(plotData, aes(x=level, y=mean)) + geom_errorbar(aes(ymin=sel, ymax=seu, width=.1)) + geom_point(aes(x=level, y=mean)) + labs(title=self$options$dep) print(plot) TRUE }The plot function accepts an argument
image
, which corresponds to the image object we calledsetState()
on. We can retrieve the state object from this image withimage$state
, which we can see is being assigned toplotData
.Following this are a number of calls to
ggplot2
functions. A full discussion of how to useggplot2
is well and truly beyond the scope of this document, but there are many excellent resources available online.Next we explicitly print the
ggplot
-object. When using ggplot interactively in an R session, callingggplot()
leads to the creation of the plot, however, when callingggplot
from inside a function, it is necessary to explicitly callprint()
.The final statement is
TRUE
which is the return value. Don’t forget this! Returning true notifies the rendering system that you have plotted something. If you don’t return true, your plot will not appear. There are situations where the user may not have specified enough information for plotting, in which case the function should returnFALSE
.So this is our final
ttest.b.R
-file:#' @export ttestClass <- R6::R6Class("ttestClass", inherit = ttestBase, private = list( .run = function() { formula <- paste(self$options$dep, '~', self$options$group) formula <- as.formula(formula) results <- t.test(formula, self$data) table <- self$results$ttest table$setRow(rowNo=1, values=list( var=self$options$dep, t=results$statistic, df=results$parameter, p=results$p.value )) means <- aggregate(formula, self$data, mean)[,2] ses <- aggregate(formula, self$data, function(x) sd(x)/sqrt(length(x)))[,2] sel <- means - ses # standard error lower bound seu <- means + ses # standard error upper bound levels <- base::levels(self$data[[self$options$group]]) plotData <- data.frame(level=levels, mean=means, sel=sel, seu=seu) image <- self$results$plot image$setState(plotData) }, .plot=function(image, ...) { plotData <- image$state plot <- ggplot(plotData, aes(x=level, y=mean)) + geom_errorbar(aes(ymin=sel, ymax=seu, width=.1)) + geom_point(aes(x=level, y=mean)) + labs(title=self$options$dep) print(plot) TRUE }) )And these are our final results, including the plot: