This repository has been archived by the owner on Jan 6, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 29
/
wk07_package.Rmd
359 lines (257 loc) · 12.4 KB
/
wk07_package.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
---
title: "Quick Intro to Package Development with `devtools`"
author: "Ben Best"
date: "`r Sys.Date()`"
output: html_document
---
```{r init, include=F}
knitr::opts_chunk$set(echo = T, eval=F)
library(htmltools)
```
## Overview
Packages are the preferred way to bundle up functions, so others can easily use them. You use packages all the time, such as every time you load them with the `library()` function.
The following quick introduction is based on distilling the most minimal example from the following:
- [book: R packages](http://r-pkgs.had.co.nz/)
- [cheat sheet: Package Development with `devtools`](./refs/cheatsheets/devtools-cheatsheet.pdf)
The R code below walks you through creation of an R package called `mypackage` having the following top level file and folder (`*/`) structure:
```
mypackage
├── DESCRIPTION
├── NAMESPACE
├── data/
├── man/
├── R/
├── tests/
└── vignettes/
```
These files and folders correspond to the following characteristic offerings of an R package:
- **functions** in `R/` scripts. Functions are written here with input arguments and return objects.
- **documentation** in `man/`. Documentation is automatically generated from the `roxygen2` comments in the R scripts. Help is then available from the console via `?add` (eg for `add()` function).
- **data** in `data/`. Data can then be loaded via `data(x)` (eg for `x` data).
- **tests** in `tests/`. Tests ensure checking for consistent behavior as your functions grow.
- **vignettes** in `vignettes/`. Vignettes are a long form of documentation that allow you to walk the user through a larger amount of analysis, especially useful for showing relatedness between functions. Since they are rendered from Rmarkdown to HTML, you can show plots, tables and more richly formatted documentation.
- **sharing** via `install_github()`. By pushing your code to Github, you can then share a single line of code using the `devtools::install_github()` function so others can install and use your function immediately.
## Create repo
Start by [creating a repository](https://help.github.com/articles/create-a-repo/) in Github. For this tutorial, I recommend:
- Owner: **<user>** (for me: bbest), Repository: **mypackage**
- Description: **R package to do some basic math**
- YES, **Initialize this repository with a README**
Now git clone this repository to your machine from within RStudio's menu File > New Project > Version Control > Git:
- Repository URL: **https://github.com/<user>/mypackage.git** (for me: https://github.com/bbest/mypackage.git)
- Project directory name: **mypackage**
- Create project as subdirectory of: **~/github** (or wherever you like)
```{r, echo=F, eval=F}
setwd('~/github/mypackage')
```
## Load devtools
The [`devtools`](https://github.com/hadley/devtools) package makes R packages easy. All the functions below are from this library. Thank you [\@hadley](https://github.com/hadley)!
```{r devtools}
# load devtools package
if (!require('devtools')) install.packages('devtools')
library(devtools)
```
## Setup the package
Setup the existing folder to be a package. (Alternatively you can use `devtools::create()` to create from scratch.)
```{r setup}
setup()
```
Notice how this function creates the `DESCRIPTION` and `NAMESPACE` files, and `R` folder.
```
Creating package 'mypackage' in '/Users/bbest/github/mypackage'
No DESCRIPTION found. Creating with values:
Package: mypackage
Title: What the Package Does (one line, title case)
Version: 0.0.0.9000
Authors@R: person("First", "Last", email = "[email protected]", role = c("aut", "cre"))
Description: What the package does (one paragraph).
Depends: R (>= 3.2.3)
License: What license is it under?
LazyData: true
```
Notice how if you re-open RStudio to `mypackage.Rproj` an extra **Build** tab appears, per the [rstudio-IDE-cheatsheet](.refs/cheatsheets/rstudio-IDE-cheatsheet.pdf):
![](./wk07_program2/img/rstudio-IDE-cheatsheet_package-writing.png)
See more:
- [devtools-cheatsheet](../refs/cheatsheets/devtools-cheatsheet.pdf): Package Structure, Setup, Write code in R/
- [Package structure · R packages](http://r-pkgs.had.co.nz/package.html)
## Functions in R/
Now create an R file with a custom `add()` function, load the package with `devtools::load_all()` and try out the function.
```{r functions}
library(devtools)
# write add function to add.R file in R/ folder
cat("add <- function(x, y){ x+ y }", file='R/add.R')
# load the library from source
load_all()
# try function
add(1,2)
```
Note the function **`cat()`** which "concatenates", or joins together, R objects and prints. This is a programmer's way of creating a file with content. Normally you would just create this new file in the RStudio editor.
## Document to man/
Add documentation to the custom `add()` function using [roxygen2](https://cran.r-project.org/web/packages/roxygen2/vignettes/roxygen2.html) style, generate documentation with `devtools::document()`, reload the package, and try out the help documentation.
```{r document}
# write add function with documentation
cat(
"#' Add together two numbers.
#'
#' @param x A number.
#' @param y A number.
#' @return The sum of \\code{x} and \\code{y}.
#' @examples
#' add(1, 1)
#' @export
add <- function(x, y){
x+ y
}", file='R/add.R')
# update R documentation (*.Rd) in manual folder man/
document()
# load the library from source
load_all()
# try function
add(1,2)
# check documentation
?add
```
Notice how `document()` creates the [`man/add.Rd`](https://github.com/bbest/bbest.github.io/blob/master/mypackage/R/add.R) for the `add()` function (and would other `man/*.Rd` files for more functions) and updates `DESCRIPTION` and `NAMESPACE` files.
```
Updating roxygen version in ~/bbest.github.io/mypackage/DESCRIPTION
Writing NAMESPACE
Writing add.Rd
```
See more:
- [devtools-cheatsheet](../refs/cheatsheets/devtools-cheatsheet.pdf): Write code in R/, Document in man/, Organize in NAMESPACE, Setup in DESCRIPTION
- [R code · R packages](http://r-pkgs.had.co.nz/r.html)
- [Package basics · R packages](http://r-pkgs.had.co.nz/description.html)
- [Object documentation · R packages](http://r-pkgs.had.co.nz/man.html)
## Add data to data/
Add custom data with `devtools::use_data()`, and load the data with base function `data()`.
```{r data}
# create data
x = 1:10
y = 1:100
# store in data/*.rda
use_data(x, y)
# use data in example
load_all()
data(x,y)
add(x,y)
```
Notice how `use_data()` creates the `data/*.rda` files.
```
Saving x, y as x.rda, y.rda to ~/bbest.github.io/mypackage/data
```
See more:
- [devtools-cheatsheet](../refs/cheatsheets/devtools-cheatsheet.pdf): Add data in data/
- [Data · R packages](http://r-pkgs.had.co.nz/data.html)
## Test in tests/
Create test infrastructure with `devtools::use_testthat()`, create some tests in `test_add.R`, and run tests with `devtools::test()`.
```{r test}
# setup for tests (one time only)
use_testthat()
# write test for add function
cat(
"context('add')
test_that('add works', {
expect_equal(add(1,1), 2)
expect_equal(add(1,2), 3)
expect_equal(add(1,100), 101)
})
", file = 'tests/testthat/test_add.R')
# run tests
test()
```
The `use_testthat()` creates the `test/testthat` folder and `tests/testthat.R` main script. Adding [`tests/testthat/test_add.R`](https://github.com/bbest/bbest.github.io/blob/master/mypackage/tests/testthat/test_add.R) adds 3 tests, each of which get executed when running `test()` which reports success with a single `.` and finishes with `DONE`.
```
Loading mypackage
Loading required package: testthat
Testing mypackage
add : ...
DONE
```
See more:
- [devtools-cheatsheet](../refs/cheatsheets/devtools-cheatsheet.pdf): Test in tests/
- [Testing · R packages](http://r-pkgs.had.co.nz/tests.html)
## Check with Travis
Once you have tests built, wouldn't it be nice to automatically run the tests every time a new commit is made to Github? This is called "continuous integration" in developer circles, and the idea is to continuously integrate tests with changes in code to ensure all is happy as you develop. If any of the tests fail, you get emailed and can fix the problem sooner than later. There's a great free service by [Travis CI](https://travis-ci.org) that can be setup to run any tests on any Github repository at every new commit that gets pushed or merged.
Before you run the next command to add the Travis CI infrastructure, I recommend running a git commit and push.
```{r}
use_travis()
```
This creates the special file `.travis.yml` containing the test instructions.
```
Adding .travis.yml to mypackage. Next:
* Turn on travis for this repo at https://travis-ci.org/profile
* Add a travis shield to your README.md:
[![Travis-CI Build Status](https://travis-ci.org/bbest/mypackage.svg?branch=master)](https://travis-ci.org/bbest/mypackage)
```
Per the instructions, visit <https://travis-ci.org/profile> to create a profile to link to your Github account and turn on Travis with a little slider to your `mypackage`.
Let's add the "travis shield" line above to the README.md (replace with your Github `<user>`, not `bbest`). Note the `append=T` argument.
```{r}
cat('
[![Travis-CI Build Status](https://travis-ci.org/bbest/mypackage.svg?branch=master)](https://travis-ci.org/bbest/mypackage)
', file='README.md', append=T)
```
Do another git commit and push. Now visit [travis-ci.org/USER/mypackage](https://travis-ci.org/USER/mypackage) (for me [travis-ci.org/bbest/mypackage](https://travis-ci.org/bbest/mypackage)). I can see that based on the Build History, I get the following "build failing" based on 2 warnings converted to errors:
```
Checking package
checking DESCRIPTION meta-information ... WARNING
Non-standard license specification:
What license is it under?
Standardizable: FALSE checking for missing documentation entries ... WARNING
Undocumented code objects:
‘x’ ‘y’
Undocumented data sets:
‘x’ ‘y’
All user-level objects in a package should have documentation entries.
See chapter ‘Writing R documentation files’ in the ‘Writing R
Extensions’ manual. DONE
Status: 2 WARNINGs
R CMD check logs
Found warnings, treating as errors (as requested).
```
You can also see this "build failing" status at [github.com/USER/mypackage](https://github.com/USER/mypackage) (for me [github.com/bbest/mypackage](https://github.com/bbest/mypackage)) which shows the README.md at the bottom with the "travis shield":
`r img(src='./wk07_program2/img/travis-ci_build-failing.png', width=100)`
Let's fix these warnings to get the build to pass:
- use permissive MIT license. See [Choosing an Open Source License](https://github.com/blog/1530-choosing-an-open-source-license).
- add [documentation to the data](http://r-pkgs.had.co.nz/data.html#documenting-data)
```{r}
# fix license
use_mit_license()
# add data documentation
cat("
#' 1 thru 10
#'
#' A simple dataset
'x'
#' 1 thru 100
#'
#' Another simple dataset
#'
'y'
", file = 'R/data.R')
document()
```
Now git commit and push again, to see if that fixes your build to build passing:
`r img(src='./wk07_program2/img/travis-ci_build-passing.png', width=100)`
See more:
- [Checking a package · R packages](http://r-pkgs.had.co.nz/check.html#travis)
## Teach with vignettes/
Finally, you can teach with vignettes, which are a longer form than just a single function or data help. Better yet, you can write them as an Rmarkdown file so they can render tables, images, etc.
```{r vignette}
# create a draft vignette
use_vignette('math')
# build vignettes, install locally
tgz = build()
install_local(tgz)
# try vignette
library(mypackage)
vignette('math')
```
Notice how `use_vignette('math')` creates `vignettes/math.Rmd`, an Rmarkdown file which you can freely edit. The default draft with title "Vignette Title" by "Vignette Author" should be customized, but offers some helpful tips on how to craft a vignette. See this example as rendered [math.html](http://bbest.github.io/mypackage/vignettes/math.html).
See more:
- [devtools-cheatsheet](../refs/cheatsheets/devtools-cheatsheet.pdf): Teach in vignettes/
- [Vignettes · R packages](http://r-pkgs.had.co.nz/vignettes.html)
## Share via `github_install()`
Now that you've created a complete R package, it's time to run a git **Commit**, and **Push** to your Github repository. Then you can share a single command for anyone else to install the R package. For my R package, files at [github.com/bbest/mypackage](https://github.com/bbest/mypackage), it would be:
```{r install_github}
devtools::install_github('bbest/mypackage', build_vignettes=T)
```
Notice how the first argument is of the form `<owner>/<repository>`.