1 Building an package
Here we present how to efficiently create and build a package using RStudio IDE, the devtools and usethis packages.
More details are provided in the reference material on this subject: the book R packages1 by Hadley Wickham & Jennifer Bryan, freely available online.
1.1 Initializing a package
A simple way, built into RStudio, to initialize a package is to follow those steps:
š Your turn (already)!
Create a new project (top left
Filedrop down menu in RStudio)Choose āNew Directoryā
Choose āR package using devtoolsā (if it is not available, choose āR packageā, the difference being that with āR packageā you will have to delete unnecessary files that are automatically created but not useful)
Give a name to your package, for example
mypkgr.
NB: if you are using Posit Cloud and not your local computer, you need to rather execute the following command into the console: usethis::create_package("mypkgr") and accept the potential proposed overwrites.
From that, we get the minimal structure for an package, namely:
a DESCRIPTION file whose
Title,Version,Authors@RandDescriptionare to be edited (other parts can be edited or even added automatically, see below)a NAMESPACE file which will be later edited automatically (so hands off for now)
a folder R/ in which we will add
.Rscript files
devtools also adds three optional files:
.gitignore, relative to
git, a version control tool that we will see in detail in the following section ongit& GitHubmypkgr.Rproj which is a specific file of RStudio and allows to define the characteristics and preferences of the project we just created
.Rbuildignore which allows to ignore some files when we build the package down the road (for example, the
mypkgr.Rprojfile should not be included in the package)
1.2 Adding a function: common theme example
We first invite you to code the following function, which we will use throughout the course.
We want to compute the value of the density of a multivariate normal distribution on at points. Our function must be applicable for any multivariate normal distribution (i.e. any mean vector in and variance-covariance matrix of size ) and we wish to compute all the values of the density (eventually in log-scale) evaluated at the points in a single call of the function.
As a reminder, the density function for a multivariate Gaussian distribution is:
So you need to create a function mvnpdf() in a file named mvnpdf.R in the R/ folder of your package, which :
-
Takes as arguments:
xa matrix, with columns (the observations) and rowsmeana vector of meansvarcovMa variance-covariance matrixLoga logical parameter, with default value toTRUE.
Returns a list containing the matrix
x, and a vector of length of the multivariate normal distribution density values at those points (eventually in log-scale).
š Your turn!
Here is a function proposal that you can download here. ā ļø WARNING! if you click too quickly on this link, it will invalidate your participation in the class!
For advice on writing code, see the R code section from Wickham & Bryan R packages (2023) by Wickham & Bryan.
1.3 Documenting a function
It is important to properly document your code. Every project has at least two developers:
yourself
yourself in 6 months.
For the sake of your future self, do yourself a favor and take the time to document your code!
We strongly advise you to use the roxygen2 package to document your packages. The main advantage being to have the help of a function in the same file as the code defining this function.
š Your turn!
Start by inserting the skeleton of the help with āInsert Roxygen Skeletonā located in the āCodeā menu or the Magic Wand sub-menu in the script window.
Complete the documentation by filling in:
the title of the function (first line)
the description of what the function does (second paragraph)
if you fill in a third paragraph, this part will go in the āDetailsā section of the help page
the meaning of the parameters
the output, after the
@returntagGenerate the documentation using āDocumentā in the āMoreā menu of the āBuildā tab (or alternatively run
devtools::document()or useCtrl+Shift+D). The effect of this command is multiple:
a
manfolder is created and inside it, amvnpdf.Rdfile contains the help information about the functionmvnpdf()the
NAMESPACEfile is modified
In case of problems OR out of curiosity but only once you are done, you can have a look at this proposal.
For more details on package documentation and roxygen2 tags, see the Object documentation section from Wickham & Bryan R packages (2023)
Let us finish by mentioning a function from the usethis package which initializes a home help page for the whole package:
usethis::use_package_doc()The generated help page will then be accessible, once the package is installed, with the following command:
?mypkgr1.4 Interactively test the package
To test the package, you have to load it in using āLoad Allā (Ctrl+Shift+L) in the āMoreā menu from the āBuildā tab, or alternatively devtools::load_all().
You can then use your package directly in : look at the function help page with ?mvnpdf and for example execute the commands given in the example section of this help page.
?mvndpfDuring code development, you can thus:
Add/Modify the code
Re-load the package
Ctrl+Shift+LExperiment with it in the console
And so onā¦
1.5 Install your package
For the moment, the package exists only in the environment associated with the RStudio project we have created. To be able to use it anywhere in (on your computer), you have to install it (like a CRAN package for example).
To do this, click on āInstallā (Ctrl+Shift+B) from the āBuildā tab, or alternatively you can run devtools::install().
And finally, you can configure RStudioās behavior so that at the time of installation, it simultaneously documents the package: go to the āMoreā menu from the āBuildā tab, then āConfigure Build Toolsā¦ā. Then click on āConfigureā next to āGenerate documentation with Roxygenā, and check the box āInstall and Restartā.
1.6 Automatically test your package
To initialize automatic testing capabilities in your package, execute the following command:
usethis::use_testthat()This command creates a tests folder which includes a testthat.R file ā not to be modified ā and a testthat/ folder in which we will add our automated tests. This tool is based on the theory of unit tests.
For example, here is the content of a file, containing two tests, that should be named test-mvnpdf.R and be put into the testthat folder (instead of doing this by hand, you can simply use the helper function usethis::use_test() that will create the file at the right place for you):
test_that("correct result for univariate gaussian", {
expect_equal(mvnpdf(x=matrix(1.96), Log=FALSE)$y, dnorm(1.96))
expect_equal(mvnpdf(x=matrix(c(1.96, -0.5), ncol = 2), Log=FALSE)$y,
dnorm(c(1.96, -0.5)))
})
test_that("correct results for bivariate gaussian", {
expect_equal(mvnpdf(x=matrix(rep(1.96,2), nrow=2, ncol=1), Log=FALSE)$y,
mvtnorm::dmvnorm(rep(1.96, 2)))
})To execute these tests, you can click on āTest packageā (Ctrl+Shift+T) in the āMoreā menu from the āBuildā tab, or run devtools::test().
The advantage of these automatic tests is that they will be run every time you check the package (see just below).
A good practice is to add a unit test each time a bug is identified and fixed, so that we can immediately identify and prevent the same error from happening again in the future.
1.7 Check your package
Running a check means ensuring that everything in your package is correct and will work as expected, and that it can be installed properly under various OS. āPassing R CMD CHECKā is mandatory for successfully uploading your package on CRAN.
To perform R CMD CHECK on your package, you can click on āCheckā (Ctrl+Shift+E) from the āBuildā tab, or run devtools::check().
During the check, the automated unit tests that we have developed previously are executed. This is the advantage of having written these tests, we donāt need to worry about it, but simply react and adjust the code when errors are returned and flagged.
Appendix 1.1: add an S3 method
In most packages, we often have to implement so called S3 methods so that, from a result object res, one can run print(res), summary(res), plot(res), ā¦
Here is an example of a plot() method that we can add to our package:
#' Plot of the mvnpdf function
#'
#' @param x an object of class \code{mvnpdf} resulting from a call of the
#' \code{mnvpdf()} function.
#' @param ... graphical parameters passed to \code{plot()} function.
#'
#' @return Nothing is returned, only a plot is given.
#' @export
#'
#' @examples
#' pdfvalues <- mvnpdf(x=matrix(seq(-3, 3, by = 0.1), nrow = 1), Log=FALSE)
#' plot(pdfvalues)
plot.mvnpdf <- function(x, ...) {
plot(x$x, x$y, type = "l", ...)
}ā ļø WARNING! In order for this S3 method to do what we want it to do when we apply it to the result of our function mvnpdf(), we have to declare that mvnpdf() returns a result of class mvnpdf.
Test this function, by executing the example.
Donāt forget to re-install the package (āInstallā or Ctrl+Shift+B).
Look at the contents of the man folder and the changes that have been made to NAMESPACE.
Here is a proposed solution: the file contains the complete code of the mvnpdf() function and the associated plot() method.
Appendix 1.2: submit oneās package on CRAN
Run the two following commands: devtools::release().
For more details, see Wickham & Bryanās recommended pipeline in their book R packages.