forked from hadley/r-pkgs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
r.rmd
428 lines (299 loc) · 18.1 KB
/
r.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
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
---
title: R code
layout: default
output: oldbookdown::html_chapter
---
# R code {#r}
The first principle of using a package is that all R code goes in `R/`. In this chapter, you'll learn about the `R/` directory, my recommendations for organising your functions into files, and some general tips on good style. You'll also learn about some important differences between functions in scripts and functions in packages.
## R code workflow {#r-workflow}
The first practical advantage to using a package is that it's easy to re-load your code. You can either run `devtools::load_all()`, or in RStudio press __Ctrl/Cmd + Shift + L__, which also saves all open files, saving you a keystroke.
This keyboard shortcut leads to a fluid development workflow:
1. Edit an R file.
1. Press Ctrl/Cmd + Shift + L.
1. Explore the code in the console.
1. Rinse and repeat.
Congratulations! You've learned your first package development workflow. Even if you learn nothing else from this book, you'll have gained a useful workflow for editing and reloading R code.
## Organising your functions {#r-organising}
While you're free to arrange functions into files as you wish, the two extremes are bad: don't put all functions into one file and don't put each function into its own separate file. (It's OK if some files only contain one function, particularly if the function is large or has a lot of documentation.). File names should be meaningful and end in `.R`.
```{r, eval = FALSE}
# Good
fit_models.R
utility_functions.R
# Bad
foo.r
stuff.r
```
Pay attention to capitalization, since you, or some of your collaborators, might be using an operating system with a case-insensitive file system (e.g., Microsoft Windows or OS X). Avoid problems by never using filenames that differ only in capitalisation.
My rule of thumb is that if I can't remember the name of the file where a function lives, I need to either separate the functions into more files or give the file a better name. (Unfortunately you can't use subdirectories inside `R/`. The next best thing is to use a common prefix, e.g., `abc-*.R`.).
The arrangement of functions within files is less important if you master two important RStudio keyboard shortcuts that let you jump to the definition of a function:
* Click a function name in code and press __F2__.
* Press __Ctrl + .__ then start typing the name:
```{r, echo = FALSE}
oldbookdown::screenshot("screenshots/file-finder.png", dpi = 220)
```
After navigating to a function using one of these tools, you can go back to where you were by clicking the back arrow at the top-left of the editor (`r oldbookdown::screenshot("screenshots/arrows.png", dpi = 240)`), or by pressing Ctrl/Cmd-F9.
## Code style {#style}
Good coding style is like using correct punctuation. You can manage without it, but it sure makes things easier to read. As with styles of punctuation, there are many possible variations. The following guide describes the style that I use (in this book and elsewhere). It is based on Google's [R style guide](https://google-styleguide.googlecode.com/svn/trunk/Rguide.xml), with a few tweaks.
You don't have to use my style, but I strongly recommend that you use a consistent style and you document it. If you're working on someone else's code, don't impose your own style. Instead, read their style documentation and follow it as closely as possible.
Good style is important because while your code only has one author, it will usually have multiple readers. This is especially true when you're writing code with others. In that case, it's a good idea to agree on a common style up-front. Since no style is strictly better than another, working with others may mean that you'll need to sacrifice some preferred aspects of your style.
The formatR package, by Yihui Xie, makes it easier to clean up poorly formatted code. It can't do everything, but it can quickly get your code from terrible to pretty good. Make sure to read [the notes on the website](http://yihui.name/formatR/) before using it. It's as easy as:
```{r, eval = FALSE}
install.packages("formatR")
formatR::tidy_dir("R")
```
A complementary approach is to use a code __linter__. Rather than automatically fixing problems, a linter just warns you about them. The lintr package by Jim Hester checks for compliance with this style guide and lets you know where you've missed something. Compared to formatR, it picks up more potential problems (because it doesn't need to also fix them), but you will still see false positives.
```{r, eval = FALSE}
install.packages("lintr")
lintr::lint_package()
```
### Object names
Variable and function names should be lowercase. Use an underscore (`_`) to separate words within a name (reserve `.` for S3 methods). Camel case is a legitimate alternative, but be consistent! Generally, variable names should be nouns and function names should be verbs. Strive for names that are concise and meaningful (this is not easy!).
```{r, eval = FALSE}
# Good
day_one
day_1
# Bad
first_day_of_the_month
DayOne
dayone
djm1
```
Where possible, avoid using names of existing functions and variables. This will cause confusion for the readers of your code.
```{r, eval = FALSE}
# Bad
T <- FALSE
c <- 10
mean <- function(x) sum(x)
```
### Spacing
Place spaces around all infix operators (`=`, `+`, `-`, `<-`, etc.). The same rule applies when using `=` in function calls. Always put a space after a comma, and never before (just like in regular English).
```{r, eval = FALSE}
# Good
average <- mean(feet / 12 + inches, na.rm = TRUE)
# Bad
average<-mean(feet/12+inches,na.rm=TRUE)
```
There's a small exception to this rule: `:`, `::` and `:::` don't need spaces around them. (If you haven't seen `::` or `:::` before, don't worry - you'll learn all about them in [namespaces](#namespace).)
```{r, eval = FALSE}
# Good
x <- 1:10
base::get
# Bad
x <- 1 : 10
base :: get
```
Place a space before left parentheses, except in a function call.
```{r, eval = FALSE}
# Good
if (debug) do(x)
plot(x, y)
# Bad
if(debug)do(x)
plot (x, y)
```
Extra spacing (i.e., more than one space in a row) is ok if it improves alignment of equal signs or assignments (`<-`).
```{r, eval = FALSE}
list(
total = a + b + c,
mean = (a + b + c) / n
)
```
Do not place spaces around code in parentheses or square brackets (unless there's a comma, in which case see above).
```{r, eval = FALSE}
# Good
if (debug) do(x)
diamonds[5, ]
# Bad
if ( debug ) do(x) # No spaces around debug
x[1,] # Needs a space after the comma
x[1 ,] # Space goes after comma not before
```
### Curly braces
An opening curly brace should never go on its own line and should always be followed by a new line. A closing curly brace should always go on its own line, unless it's followed by `else`.
Always indent the code inside curly braces.
```{r, eval = FALSE}
# Good
if (y < 0 && debug) {
message("Y is negative")
}
if (y == 0) {
log(x)
} else {
y ^ x
}
# Bad
if (y < 0 && debug)
message("Y is negative")
if (y == 0) {
log(x)
}
else {
y ^ x
}
```
It's ok to leave very short statements on the same line:
```{r, eval = FALSE}
if (y < 0 && debug) message("Y is negative")
```
### Line length
Strive to limit your code to 80 characters per line. This fits comfortably on a printed page with a reasonably sized font. If you find yourself running out of room, this is a good indication that you should encapsulate some of the work in a separate function.
### Indentation
When indenting your code, use two spaces. Never use tabs or mix tabs and spaces. Change these options in the code preferences pane:
```{r, echo = FALSE}
oldbookdown::screenshot("screenshots/style-options.png", dpi = 220)
```
The only exception is if a function definition runs over multiple lines. In that case, indent the second line to where the definition starts:
```{r, eval = FALSE}
long_function_name <- function(a = "a long argument",
b = "another argument",
c = "another long argument") {
# As usual code is indented by two spaces.
}
```
### Assignment
Use `<-`, not `=`, for assignment.
```{r}
# Good
x <- 5
# Bad
x = 5
```
### Commenting guidelines
Comment your code. Each line of a comment should begin with the comment symbol and a single space: `# `. Comments should explain the why, not the what. \index{comments}
Use commented lines of `-` and `=` to break up your file into easily readable chunks.
```{r, eval = FALSE}
# Load data ---------------------------
# Plot data ---------------------------
```
## Top-level code {#r-differences}
Up until now, you've probably been writing __scripts__, R code saved in a file that you load with `source()`. There are two main differences between code in scripts and packages:
* In a script, code is run when it is loaded. In a package, code is run when it
is built. This means your package code should only create objects, the
vast majority of which will be functions.
* Functions in your package will be used in situations that you didn't imagine.
This means your functions need to be thoughtful in the way that they
interact with the outside world.
The next two sections expand on these important differences.
### Loading code
When you load a script with `source()`, every line of code is executed and the results are immediately made available. Things are different in a package, because it is loaded in two steps. When the package is built (e.g. by CRAN) all the code in `R/` is executed and the results are saved. When you load a package, with `library()` or `require()`, the cached results are made available to you. If you loaded scripts in the same way as packages, your code would look like this:
```{r, eval = FALSE}
# Load a script into a new environment and save it
env <- new.env(parent = emptyenv())
source("my-script.R", local = env)
save(envir = env, "my-script.Rdata")
# Later, in another R session
load("my-script.Rdata")
```
For example, take `x <- Sys.time()`. If you put this in a script, `x` would tell you when the script was `source()`d. But if you put that same code in a package, `x` would tell you when the package was _built_.
This means that you should never run code at the top-level of a package: package code should only create objects, mostly functions. For example, imagine your foo package contains this code:
```{r, eval = FALSE}
library(ggplot2)
show_mtcars <- function() {
qplot(mpg, wt, data = mtcars)
}
```
If someone tries to use it:
```{r, eval = FALSE}
library(foo)
show_mtcars()
```
The code won't work because ggplot2's `qplot()` function won't be available: `library(foo)` doesn't re-execute `library(ggplot2)`. The top-level R code in a package is only executed when the package is built, not when it's loaded.
To get around this problem you might be tempted to do:
```{r, eval = FALSE}
show_mtcars <- function() {
library(ggplot2)
qplot(mpg, wt, data = mtcars)
}
```
That's also problematic, as you'll see below. Instead, describe the packages your code needs in the `DESCRIPTION` file, as you'll learn in [package dependencies](#dependencies).
### The R landscape
Another big difference between a script and a package is that other people are going to use your package, and they're going to use it in situations that you never imagined. This means you need to pay attention to the R landscape, which includes not just the available functions and objects, but all the global settings. You have changed the R landscape if you've loaded a package with `library()`, or changed a global option with `options()`, or modified the working directory with `setwd()`. If the behaviour of _other_ functions differs before and after running your function, you've modified the landscape. Changing the landscape is bad because it makes code much harder to understand.
There are some functions that modify global settings that you should never use because there are better alternatives:
* __Don't use `library()` or `require()`__. These modify the search path,
affecting what functions are available from the global environment.
It's better to use the `DESCRIPTION` to specify your package's requirements,
as described in the next chapter. This also makes sure those packages are
installed when your package is installed.
* __Never use `source()`__ to load code from a file. `source()` modifies the
current environment, inserting the results of executing the code. Instead, rely
on `devtools::load_all()` which automatically sources all files in `R/`.
If you're using `source()` to create a dataset, instead switch to `data/`
as described in [datasets](#data).
Other functions need to be used with caution. If you use them, make sure to clean up after yourself with `on.exit()`:
* If you modify global `options()` or graphics `par()`, save the old values
and reset when you're done:
```{r, eval = FALSE}
old <- options(stringsAsFactors = FALSE)
on.exit(options(old), add = TRUE)
```
* Avoid modifying the working directory. If you do have to change it, make sure
to change it back when you're done:
```{r, eval = FALSE}
old <- setwd(tempdir())
on.exit(setwd(old), add = TRUE)
```
* Creating plots and printing output to the console are two other ways of
affecting the global R environment. Often you can't avoid these (because
they're important!) but it's good practice to isolate them in functions that
__only__ produce output. This also makes it easier for other people to
repurpose your work for new uses. For example, if you separate data preparation
and plotting into two functions, others can use your data prep work (which
is often the hardest part!) to create new visualisations.
The flip side of the coin is that you should avoid relying on the user's landscape, which might be different to yours. For example, functions like `read.csv()` are dangerous because the value of `stringsAsFactors` argument comes from the global option `stringsAsFactors`. If you expect it to be `TRUE` (the default), and the user has set it to be `FALSE`, your code might fail.
### When you __do__ need side-effects
Occasionally, packages do need side-effects. This is most common if your package talks to an external system --- you might need to do some initial setup when the package loads. To do that, you can use two special functions: `.onLoad()` and `.onAttach()`. These are called when the package is loaded and attached. You'll learn about the distinction between the two in [Namespaces](#namespace). For now, you should always use `.onLoad()` unless explicitly directed otherwise.
Some common uses of `.onLoad()` and `.onAttach()` are:
* To display an informative message when the package loads. This might make
usage conditions clear, or display useful tips. Startup messages is one
place where you should use `.onAttach()` instead of `.onLoad()`. To display
startup messages, always use `packageStartupMessage()`, and not `message()`.
(This allows `suppressPackageStartupMessages()` to selectively suppress
package startup messages).
```{r, eval = FALSE}
.onAttach <- function(libname, pkgname) {
packageStartupMessage("Welcome to my package")
}
```
* To set custom options for your package with `options()`. To avoid conflicts
with other packages, ensure that you prefix option names with the name
of your package. Also be careful not to override options that the user
has already set.
I use the following code in devtools to set up useful options:
```{r, eval = FALSE}
.onLoad <- function(libname, pkgname) {
op <- options()
op.devtools <- list(
devtools.path = "~/R-dev",
devtools.install.args = "",
devtools.name = "Your name goes here",
devtools.desc.author = '"First Last <[email protected]> [aut, cre]"',
devtools.desc.license = "What license is it under?",
devtools.desc.suggests = NULL,
devtools.desc = list()
)
toset <- !(names(op.devtools) %in% names(op))
if(any(toset)) options(op.devtools[toset])
invisible()
}
```
Then devtools functions can use e.g. `getOption("devtools.name")` to
get the name of the package author, and know that a sensible default value
has already been set.
* To connect R to another programming language. For example, if you use rJava
to talk to a `.jar` file, you need to call `rJava::.jpackage()`. To
make C++ classes available as reference classes in R with Rcpp modules,
you call `Rcpp::loadRcppModules()`.
* To register vignette engines with `tools::vignetteEngine()`.
As you can see in the examples, `.onLoad()` and `.onAttach()` are called with two arguments: `libname` and `pkgname`. They're rarely used (they're a holdover from the days when you needed to use `library.dynam()` to load compiled code). They give the path where the package is installed (the "library"), and the name of the package.
If you use `.onLoad()`, consider using `.onUnload()` to clean up any side effects. By convention, `.onLoad()` and friends are usually saved in a file called `zzz.R`. (Note that `.First.lib()` and `.Last.lib()` are old versions of `.onLoad()` and `.onUnload()` and should no longer be used.)
### S4 classes, generics and methods
Another type of side-effect is defining S4 classes, methods and generics. R packages capture these side-effects so they can be replayed when the package is loaded, but they need to be called in the right order. For example, before you can define a method, you must have defined both the generic and the class. This requires that the R files be sourced in a specific order. This order is controlled by the `Collate` field in the `DESCRIPTION`. This is described in more detail in [documenting S4](#man-s4).
## CRAN notes {#r-cran}
(Each chapter will finish with some hints for submitting your package to CRAN. If you don't plan on submitting your package to CRAN, feel free to ignore them!)
If you're planning on submitting your package to CRAN, you must use only ASCII characters in your `.R` files. You can still include unicode characters in strings, but you need to use the special unicode escape `"\u1234"` format. The easiest way to do that is to use `stringi::stri_escape_unicode()`:
```{r}
x <- "This is a bullet •"
y <- "This is a bullet \u2022"
identical(x, y)
cat(stringi::stri_escape_unicode(x))
```