--- title: "Using a custom plot layout within ``superb``" bibliography: "../inst/REFERENCES.bib" csl: "../inst/apa-6th.csl" output: rmarkdown::html_vignette description: > superbPlot can generate either line plots, barcharts, or point plots. However, additional layout can be programmed. In this vignette, we show how to add custom layouts in superbPlot. As an example, we show how to implement additional layouts. vignette: > %\VignetteIndexEntry{Using a custom plot layout within ``superb``} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r, echo = FALSE, warning=FALSE, message = FALSE, results = 'hide'} cat("this will be hidden; use for general initializations.\n") library(superb) library(ggplot2) options(superb.feedback = 'none') ``` ``superbPlot()`` comes with seven built-in layouts for plotting your data. However, it is possible to add additional, custom-made layouts. In this vignette, we present rapidly the existing layouts, then show how to supplement ``superb`` with your own layouts. ## The built-in plot layouts When calling ``superbPlot()``, you use the ``plotStyle = "layout"`` option to indicate which layout you wish to use. Internally, ``superbPlot()`` is calling a function whose name is ``superbPlot."layout"()``. For example, with ``plotStyle = "line"``, the plot is actually performed by the function ``superbPlot.line()``. The seven layout available in ``superbPlot`` package are : * ``superbPlot.line()`` : shows the results as points and lines, * ``superbPlot.lineBand()``: Shows the confidence intervals as a semi-transparent band rather than with bars. * ``superbPlot.point()``: shows the results as points only, * ``superbPlot.bar()`` : shows the results using bars, * ``superbPlot.pointjitter()``: shows the results with points, and the raw data with jittered points, * ``superbPlot.pointjitterviolin()``: also shows violin plot behind the jitter points, and * ``superbPlot.pointindividualline()``: show the results with fat points, and individual results with thin lines, * ``superbPlot.raincloud()``: Shows the results with distribution and jitter. To determine if a certain function is ``superbPlot``-compatible, use the following function: ```{r} superb:::is.superbPlot.function("superbPlot.line") ``` where you put between quote the name of a function. When devising your own, custom-made function, it is a good thing to check that it is ``superbPlot``-compatible. ## Illustrating some of the built-in layouts To get a sense of the currently available layouts, we first generate a dataset composed of randomly generated scores mimicking a 3 $\times$ 2 design with three degrees of Difficulties (as a between-group factor) and two days of testing (as a within-subject factor). It is believed (and simulated) that all two factors have main effets on the scores. ```{r} testdata <- GRD( RenameDV = "score", SubjectsPerGroup = 25, BSFactors = "Difficulty(3)", WSFactors = "Day(day1, day2)", Population = list(mean = 65,stddev = 12,rho = 0.5), Effects = list("Day" = slope(-5), "Difficulty" = slope(3) ) ) head(testdata) ``` For simplicity, we define a function whose arguments are the dataset and the layout: ```{r} mp <- function(data, style, ...) { superb( cbind(score.day1, score.day2) ~ Difficulty, data, WSFactors = "Day(2)", adjustments = list(purpose="difference", decorrelation="CA"), plotStyle = style, ... )+labs(title = paste("Layout is ''",style,"''",sep="")) } ``` Lets compute the plots will the first six built-in layouts and show them ```{r, fig.width= 7, fig.height = 7, fig.cap = "**Figure 1a**. Look of the six built-in layouts on the same random dataset"} p1 <- mp(testdata, "bar") p2 <- mp(testdata, "point") p3 <- mp(testdata, "line") p4 <- mp(testdata, "pointjitter" ) p5 <- mp(testdata, "pointjitterviolin") p6 <- mp(testdata, "pointindividualline") library(gridExtra) grid.arrange(p1,p2,p3,p4,p5,p6,ncol=2) ``` A cool format, a ``raincloud`` plot [@pw21], is better seen with coordinates flipped over: ```{r, fig.width =7, fig.height = 3.5, fig.cap = "**Figure 1b**. The seventh layout, the raincloud"} mp(testdata, "raincloud") + coord_flip() ``` Finally, the following shows the latest layout, a box plot, better for showing median and confidence interval of the median, obtained with ```{r, fig.width= 5, fig.height = 3, fig.cap = "**Figure 1c**. Box plot of the data"} mp(testdata, "boxplot", statistic = "median", pointParams = list(color="orange")) ``` For more controls, you can manually set the colors, the fills and/or the shapes, as done here in a list: ```{r, fig.width= 7, fig.height = 7, fig.cap = "**Figure 2a**. The six built-in template with ornamental styling added."} ornate = list( scale_colour_manual( name = "Difference", labels = c("Easy", "Hard", "Unthinkable"), values = c("blue", "black", "purple")) , scale_fill_manual( name = "Difference", labels = c("Easy", "Hard", "Unthinkable"), values = c("blue", "black", "purple")) , scale_shape_manual( name = "Difference", labels = c("Easy", "Hard", "Unthinkable") , values = c(0, 10, 13)) , theme_bw(base_size = 9) , labs(x = "Days of test", y = "Score in points" ), scale_x_discrete(labels=c("1" = "Former day", "2" = "Latter day")) ) library(gridExtra) grid.arrange( p1+ornate, p2+ornate, p3+ornate, p4+ornate, p5+ornate, p6+ornate, ncol=2) ``` These are just a few examples. However, if these layouts do not fit yours needs, it is possible to devise a custom-made layout and inform ``superbPlot`` to use it. To that end, see the instructions below. ## Devising a custom-made plot layout In a nutshell, the purpose of ``superbPlot()`` is to * compile the summary information (location of the summary statistic, upper width and lower width of the interval) and that, for each level of the factors; * applies all the adjustments needed in producing the summary; * and finally, calls a plot function accepting pre-defined arguments In devising your own plot function, it is important that (i) the function name begins with ``superbPlot.``; (ii) the function accept very specific arguments with very precise names. Here is the header for a function corresponding to a plot style called, say, *foo* (``plotStyle = "foo"``): ```{r} superbPlot.foo <- function( summarydata, xfactor, groupingfactor, addfactors, rawdata # any optional argument you wish ) { plot <- ggplot() ## ggplot instructions... return(plot) } ``` In what follow, it is assumed that one factor is placed on the horizontal axis (``xfactor``), another one is used to group the point (``groupingfactor``), and up to two additional factors will results in columns and rows of panels (``addfactors``; of course, in devising your own template, you may use different placement). ``superbPlot()`` is restricted to a maximum of four factors. The arguments are: * ``summarydata``: this data frame will contain the column ``center`` indicating the statistic's value, ``lowerwidth`` and ``upperwidth`` indicating how many units below and above ``center`` the error bar extends. The data frame will also have columns for all the factors, and there will be as many lines as there are combinations of factors. * ``xfactor`` is the factor to put on the horizontal axis; * ``groupingfactor`` is the factor used to create groups of points; * ``addfactors`` are up to two additional factors to create the rows and columns of panels. ``addfactors`` is formatted for facetting (e.g., for factors "A" and "B", ``addfactors`` would be "A~B"); * ``rawdata`` : this data.frame contains the raw data with factors being transformed ``as.factor`` and the dependent column being renamed ``DV``. When the data are in wide format, ``rawdata`` is reshaped to long format. * ``{optional arguments}`` can be used. They must be named here; when calling ``superbPlot()``, any argument whose name match your optional argument will be transmitted to your custom-made function. ## The simplest example What follow is a simple example that will design a template that we will call ``simple``. This layout will display the descriptive statistics and error bars. Everything will be black and white (no color instruction) and superimposed (no grouping instruction). The result will be: ```{r, message=FALSE, echo=FALSE, fig.height=3, fig.width=4, fig.cap="**Figure 3**. Mean score with 95% confidence interval using the ``simple`` plot layout."} superbPlot.simple <- function( summarydata, xfactor, groupingfactor, addfactors, rawdata ) { plot <- ggplot( data = summarydata, mapping = aes( x = !!sym(xfactor), y = center, group= !!sym(groupingfactor)) ) + geom_point( ) + geom_errorbar( mapping = aes(ymin = center + lowerwidth, ymax = center + upperwidth) )+ facet_grid( addfactors ) return(plot) } superb( crange(T1,T7) ~ Condition, TMB1964r, WSFactors = "T(7)", plotStyle = "simple" ) ``` To make this plot, we design a function ``superbPlot.simple`` as: ```{r} superbPlot.simple <- function( summarydata, xfactor, groupingfactor, addfactors, rawdata ) { plot <- ggplot( data = summarydata, mapping = aes( x = !!sym(xfactor), y = center, group = !!sym(groupingfactor) ) ) + geom_point( ) + geom_errorbar( mapping = aes(ymin = center + lowerwidth, ymax = center + upperwidth) )+ facet_grid( addfactors ) return(plot) } ``` The first instruction, ``ggplot`` defines the source data to be ``summarydata`` with horizontal axis being in the string ``xfactor`` (the piece of instructions ``!!sym( )`` converts the string into a variable). The position of the descriptive statistics is automatically computed and stored in a column called ``center``. The second instruction put points for each ``center``, and the third instruction places error bars. In that case, the ``ymin`` and ``ymax`` information are contained in ``center+lowerwidth`` and ``center+upperwidth`` where ``lowerwidth`` and ``upperwidth`` are automaticall computed and stored in the ``summarydata`` dataframe. The last instructions generates distinct panels for each level of the remaining factors. You can check that this function is ``superbPlot``-compatible with: ```{r} superb:::is.superbPlot.function("superbPlot.simple") ``` If ``TRUE``, then we are ready to use this layout, here with the demo dataset ``TMB1964r`` whose result was shown above in Figure 3: ```{r, message=FALSE, echo=TRUE, results='hide', fig.show='hide'} superb( crange(T1,T7) ~ Condition, TMB1964r, WSFactors = "T(7)", plotStyle = "simple" ) ``` ## Optional arguments The above ``simple`` layout does not accept optional arguments. To integrate optional arguments, one method is to insert graphic directives inside the layers, e.g., inside ``geom_point``. A convenient method is with ``do.call`` and ``modifyList``, for example: ```{r, eval = FALSE, message=FALSE, echo=TRUE, error=FALSE, results='hide'} do.call( geom_point, modifyList( list( size= 3 ##etc., the default directives## ), myownParams )) ``` A full example it therefore ```{r} superbPlot.simpleWithOptions <- function( summarydata, xfactor, groupingfactor, addfactors, rawdata, myownpointParams = list(), ## will be used to add the optional arguments to the function myownebParams = list() ## will be used to add the optional arguments to the function ) { plot <- ggplot( data = summarydata, mapping = aes( x = !!sym(xfactor), y = center, group=Condition) ) + do.call( geom_point, modifyList( list( color ="black" ), myownpointParams )) + do.call( geom_errorbar, modifyList( list( mapping = aes(ymin = center + lowerwidth, ymax = center + upperwidth) ), myownebParams )) + facet_grid( addfactors ) return(plot) } superb:::is.superbPlot.function("superbPlot.simpleWithOptions") ``` ```{r, message=FALSE, eval=FALSE, echo=TRUE, results='hide', fig.show='show', fig.cap="**Figure 4**. A simple figure with optional arguments"} superb( crange(T1,T7) ~ Condition, TMB1964r, WSFactors = "T(7)", plotStyle = "simpleWithOptions", ## here goes the optional arguments myownpointParams = list(size=1, color="purple", position = position_dodge(width = 0.3) ), myownebParams = list(linewidth=1, color="purple", position = position_dodge(width = 0.3) ) ) ``` In that example, the same parameters are sent to ``geom_point`` and to ``geom_errorbar``. It is left as an exercice to the reader to use two distinct sets of optional parameters, one for the points, the other for the error bars. ## Getting feedback information It is sometimes useful to extract variables out of the function when debugging the code. A useful function is to use ``runDebug()``. This function (shipped with ``suberb``) will display text and transfer any variables you want into the global environment. ```{r, eval=TRUE, message=TRUE, echo=TRUE} options(superb.feedback = 'all') runDebug( 'where are we?', "Text to show when we get there", c("variable1", "variable2", "etc"), list( "var1InTheFct", "var2InTheFct", "varetcInTheFct") ) ``` For example, the following will get the dataframes: ```{r, eval=TRUE, message=TRUE, echo=TRUE, fig.show='hide'} superbPlot.empty <- function( summarydata, xfactor, groupingfactor, addfactors, rawdata ) { runDebug( 'inempty', "Dumping the two dataframes", c("summary","raw"), list(summarydata, rawdata)) plot <- ggplot() # an empty plot return(plot) } options(superb.feedback = 'inempty') ## turn on feedback when reaching 'inempty' superb( crange(T1,T7) ~ Condition, TMB1964r, WSFactors = "T(7)", plotStyle = "empty" ) ``` You see ``Dumping the two dataframes`` followed by ``summary`` and ``raw``. These two variables are now in the global environment and you can manipulate them. You can also use them in testing your plotting functions, for example ```{r, message=FALSE, echo=TRUE, results='hide', fig.show='hide'} superbPlot.simple(summary, "T", "Condition", ".~.", raw) ``` ## An example In what follow, we create a toy example where the raw data will be shown with smileys. Note that this example may not work in Rstudio (see "limitation" on emojifont [page](https://CRAN.R-project.org/package=emojifont) ) We first need the ``emojifont`` library ```{r, message=FALSE, echo=TRUE, warning=FALSE} # install.packages("emojifont") library(emojifont) ``` Then we define a ``"smiley"`` layout where the emojis are shown with ``geom_text`` layer: ```{r} superbPlot.smiley <- function( summarydata, xfactor, groupingfactor, addfactors, rawdata ) { # the early part bears on summary data with variable "center" plot <- ggplot( data = summarydata, mapping = aes( x = !!sym(xfactor), y = center, fill = !!sym(groupingfactor), shape = !!sym(groupingfactor), colour = !!sym(groupingfactor) ) ) + geom_point(position = position_dodge(width = .95)) + geom_errorbar( width = .6, position = position_dodge(.95), mapping = aes(ymin = center + lowerwidth, ymax = center + upperwidth) )+ # this part bears on the rawdata only with variable "DV" geom_text(data = rawdata, position = position_jitter(0.5), family = "EmojiOne", label = emoji("smile"), size = 6, mapping = aes(x = !!sym(xfactor), y = DV, group = !!sym(groupingfactor) ) ) + facet_grid( addfactors ) return(plot) } ``` We check that it is a ``superbPlot``-compatible function: ```{r} superb:::is.superbPlot.function("superbPlot.smiley") ``` It is all we need! This layout can be inserted in a ``superbPlot`` call: ```{r, fig.width= 4, fig.height = 3, fig.cap = "**Figure 5**. smile!"} superb( crange(T1,T7) ~ Condition, TMB1964r, WSFactors = "T(7)", plotStyle = "smiley" ) ``` # References