Lesson 5 - Biome evolution
We’re all busy, stressed-out people. Why should we put in the effort to package code, when we could be starting another project. Or collecting data on another paper? I’d argue that there are tree main points:
blogo File > New Project > R Package
Choose to initialize a revision management repository with the package.
When we do this in RStudio, R initializes a package. It was several components:
You can see that our package initialized with one function, hello.R
. In it, you’ll notice one function. If we type Cmd + Shift + B
, this will build and install the package. Once we do this, we can call the hello()
function. This also includes hello()
into the standard R documentation system. If you type ?hello
, for example you will see the usual help window display.
We can also Check or Test our package. Try Cmd + Shift + E
. What is this output you see?
How about Cmd + Shift + T
? Right - we don’t have a test directory yet!
Let’s look briefly at an R package and see what one looks like under the hood. Instructors will walk through an R package of their own.
Now, we will return to our own package. We have one function, and no tests. People can mean different things when they say “testing”. We will talk today about runtime testing, and unit testing.
Unit tests can be thought of as automated checking to make sure your code works. For example, let’s write a function that calculates the area of a square. Save it into your R folder, and rebuild the package. Run your function. Did it work? How do you know?
Manually checking code works great for easy values. Any amount of complexity, though, rapidly descreases our ability to test by eye. Unit testing tests the code itself. Are you getting the expected output from a set of pre-defined in and outputs? If not, that could indicate a problem. Perhaps the interface to a function on which your code relies has changed, and now you’re providing the wrong inputs. Or between versions, the default precision changes. You want to capture these changes to make sure you’re on track.
Let’s start by making a tests
folder. We’ll use the R package testthat
to write some tests. Make a subfolder called testthat
. Type testthat::
and use tab to cycle through the options. Are there any options that would be good for testing something about our calculation? The format of a test will look like this:
test_that("Test square_calc(known data)", {
Condition
})
Let’s make a quick function that squares a number. We’d like to know it works, eh? We could check that we get the expected result, like so:
square_calc <- function(x){
sq <- x*x
return(sq)
}
library(testthat)
test_that("Test square_calc()", {
expect_equal(6.0025, square_calc(2.45))
})
Discuss with a partner:
Where unit testing is about catching errors in the code, errors caused by dependency changes, and similar errors, runtime testing tests the behavior of the user and data. For example, if your method cannot handle missing data values, you need to make sure your user are not trying to use data with missing characters. Every run time check you write is an email you don’t get later.
What is something that could break our function? For example, my idea is to make sure the side of the square is a numeric type:
library(assertthat)
square_calc <- function(side_length){
side_length %has_attr% "numeric"
area <- side_length^2
return(area)
}
Try to break it! This is often referred to as a precondition
- that is, if this is not true prior to the start of the function, the function will not execute. We can also test post conditions
, which are attributes we need to be true at the end of the function.
library(assertthat)
square_calc <- function(side_length){
side_length %has_attr% "numeric"
area <- side_length^2
assert_that(sqrt(area) == side_length)
return(area)
}
We can also write our own error messages. Try the below with both object types:
tree <- phytools::pbtree(n = 10)
t <- c("a", "b", "c")
if (!inherits(ENTER HERE, "phylo")){
stop("tree must be of class 'phylo'")
}
Go ahead and open up the .R file for one of your. We’ll now add some documentation. That typically takes this format:
#' Short description of what the function does.
#'
#' Longer description of what a function does. Do you need certain inputs? Specify them
#' here. Do you calculate the likelihood of data given a specific formulation of a model? #' Let people know which here!
#'
#' @param Inputs to function
#' @export
When you have written the documentation you would like to have, you can run
devtools::document()
This function will build manuals and online help pages (.Rd files) from your headers. These are important components of usable documentation!
Create a directory called Vignettes, and an RMarkdown document within it. Let’s walk through some core components of a tutorial.
Once your package is on GitHub, it can be installed by others with either
devtools::install_github("githubusername/Repo")
# or
remotes::install_github("githubusername/Repo")
One of the trickiest things about package development is a lot of the help we get as novices sort of falls away.
ROpenScience has an R Package guide, which is chock full of wisdom, literate programming advice, and information on testing and other intermediate skills. They also operate a forum for developers, users, and other people who dig software, I guess? The culture of it is very collegial.
```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) knitr::opts_chunk$set(class.source=’fold-show’) knitr::opts_chunk$set(collapse = TRUE) library...