Skip to content

Commit a119ebd

Browse files
committed
Implement suggestions from review
1 parent 81eff75 commit a119ebd

File tree

1 file changed

+41
-47
lines changed

1 file changed

+41
-47
lines changed

vignettes/ggplot2-in-packages.Rmd

Lines changed: 41 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ This vignette is intended for package developers who use ggplot2 within their pa
1616

1717
## Referring to ggplot2 functions
1818

19-
As with any function from another package, you will have to list ggplot2 in your `DESCRIPTION` under `Imports` or `Suggests` and refer to its functions using `::` (e.g., `ggplot2::function_name`).
19+
As with any function from another package, you will have to list ggplot2 in your `DESCRIPTION` under `Imports` and refer to its functions using `::` (e.g., `ggplot2::function_name`):
2020

2121
```{r}
2222
mpg_drv_summary <- function() {
@@ -31,7 +31,7 @@ mpg_drv_summary <- function() {
3131
mpg_drv_summary()
3232
```
3333

34-
If you use ggplot2 functions frequently, you may wish to import one or more functions from ggplot2 into your `NAMESPACE`. If you use [roxygen2](https://cran.r-project.org/package=roxygen2), you can include `#' @importFrom ggplot2 <one or more function names>` in any roxygen comment block (this will not work for datasets like `mpg`). If you do this, you will need to put ggplot2 in your `Imports` rather than your `Suggests`.
34+
If you use ggplot2 functions frequently, you may wish to import one or more functions from ggplot2 into your `NAMESPACE`. If you use [roxygen2](https://cran.r-project.org/package=roxygen2), you can include `#' @importFrom ggplot2 <one or more function names>` in any roxygen comment block (this will not work for datasets like `mpg`).
3535

3636
```{r}
3737
#' @importFrom ggplot2 ggplot aes geom_bar coord_flip
@@ -47,36 +47,6 @@ mpg_drv_summary <- function() {
4747
mpg_drv_summary()
4848
```
4949

50-
If you use infix operators from ggplot2 like `%+replace%` and you want to keep ggplot2 in `Suggests`, you can assign the operator within the function before it is used.
51-
52-
```{r}
53-
theme_custom <- function(...) {
54-
`%+replace%` <- ggplot2::`%+replace%`
55-
56-
ggplot2::theme_grey(...) %+replace%
57-
ggplot2::theme(panel.background = ggplot2::element_blank())
58-
}
59-
```
60-
61-
```{r, include=FALSE}
62-
# make sure this function runs!
63-
mpg_drv_summary() + theme_custom()
64-
```
65-
66-
If you have ggplot2 in your `Imports` anyway, it is much easier to import the infix into your namespace.
67-
68-
```{r}
69-
#' @importFrom ggplot2 %+replace%
70-
theme_custom <- function(...) {
71-
ggplot2::theme_grey(...) %+replace%
72-
ggplot2::theme(panel.background = ggplot2::element_blank())
73-
}
74-
```
75-
76-
```{r, include=FALSE}
77-
mpg_drv_summary() + theme_custom()
78-
```
79-
8050
Even if you use many ggplot2 functions in your package, it is unwise to use ggplot2 in `Depends` or import the entire package into your `NAMESPACE`. Using ggplot2 in `Depends` will attach ggplot2 when your package is attached, which includes when your package is tested. This makes it difficult to ensure that others can use the functions in your package without attaching it (i.e., using `::`). Similarly, importing all 450 of ggplot2's exported objects into your namespace makes it difficult to separate the responsibility of your package and the responsibility of ggplot2, in addition to making it difficult for readers of your code to figure out where functions are coming from!
8151

8252
## Using `aes()` and `vars()` in a package function
@@ -99,6 +69,12 @@ N checking R code for possible problems (2.7s)
9969
drv
10070
```
10171

72+
There are three situations in which you will encounter this problem:
73+
74+
- You already know the column name or expression in advance.
75+
- You have the column name as a character vector.
76+
- The user specifies the column name or expression, and you want your function to use the same kind of non-standard evaluation used by `aes()` and `vars()`.
77+
10278
If you already know the mapping in advance (like the above example) you should use the `.data` pronoun from [rlang](https://cran.r-project.org/package=rlang) to make it explicit that you are referring to the `drv` in the data and not some other variable named `drv` (which may or may not exist elsewhere).
10379

10480
```{r}
@@ -110,7 +86,7 @@ mpg_drv_summary <- function() {
11086
}
11187
```
11288

113-
If the user specifies a part of the mapping, you can either specify this as a column name (e.g., `col = "drv"`) or using the same kind of non-standard evaluation used by `aes()` and `vars()` (e.g., `col = drv`). In the first case, use `.data[[col]]`:
89+
If you have the column name as a character vector (e.g., `col = "drv"`), use `.data[[col]]`:
11490

11591
```{r}
11692
#' @importFrom rlang .data
@@ -123,7 +99,7 @@ col_summary <- function(data, col) {
12399
col_summary(mpg, "drv")
124100
```
125101

126-
To use the same kind of non-standard evaluation that `aes()` uses, use `{{ col }}` to pass the unevaluated expression the user typed in `col` to `aes()`.
102+
To use the same kind of non-standard evaluation used by `aes()` and `vars()`, use `{{ col }}` to pass the unevaluated expression the user typed in `col` to `aes()`.
127103

128104
<!-- this uses development rlang, which is not yet released -->
129105

@@ -137,9 +113,9 @@ col_summary <- function(data, col) {
137113
col_summary(mpg, drv)
138114
```
139115

140-
To summarise, if you know the mapping or facet specification is `col` in advance, use `aes(.data$col)` or `vars(.data$col)`. If `col` is a variable that contains the column name as a character scalar, use `aes(.data[[col]]` or `vars(.data[[col]])`. If you would like the behaviour of `col` to look and feel like `aes()` and `vars()`, use `aes({{ col }})` or `vars({{ col }})`.
116+
To summarise, if you know the mapping or facet specification is `col` in advance, use `aes(.data$col)` or `vars(.data$col)`. If `col` is a variable that contains the column name as a character scalar, use `aes(.data[[col]]` or `vars(.data[[col]])`. If you would like the behaviour of `col` to look and feel like `aes()` and `vars()`, use `aes({{ col }})` or `vars({{ col }})`.
141117

142-
You will see a lot of other ways to do this in the wild, but the syntax we use here is the only one we can guarantee will work in the future! In particular, don't use `aes_()` or `aes_string()`, as they are deprecated and may be removed in a future version. Finally, don't skip the step of creating a data frame and a mapping to pass in to `ggplot()` or its layers! You will see other ways of doing this in the wild, but these rely on undocumented behaviour and can fail in unexpected ways.
118+
You will see a lot of other ways to do this in the wild, but the syntax we use here is the only one we can guarantee will work in the future! In particular, don't use `aes_()` or `aes_string()`, as they are deprecated and may be removed in a future version. Finally, don't skip the step of creating a data frame and a mapping to pass in to `ggplot()` or its layers! You will see other ways of doing this in the wild, but these may rely on undocumented behaviour and can fail in unexpected ways.
143119

144120
## Best practices for common tasks
145121

@@ -194,17 +170,7 @@ plot.discrete_distr <- function(x, ...) {
194170
}
195171
```
196172

197-
If you don't use ggplot2 for your visualizations but would like to implement `autoplot()` for users that do, it is possible to register your generics only if ggplot2 is installed using `vctrs::s3_register()`.
198-
199-
```{r, eval=FALSE}
200-
.onLoad <- function(...) {
201-
if (requireNamespace("ggplot2", quietly = TRUE)) {
202-
vctrs::s3_register("ggplot2::autoplot", "discrete_distr")
203-
}
204-
}
205-
```
206-
207-
It is considered bad practice to implement an S3 generic like `plot()`, or `autoplot()` if you don't have any control over the S3 object, as it makes it hard for the package developer who does have control over the S3 to implement the method themselves. This shouldn't stop you from creating your own functions to visualize these objects!
173+
It is considered bad practice to implement an S3 generic like `plot()`, or `autoplot()` if you don't own the S3 object, as it makes it hard for the package developer who does have control over the S3 to implement the method themselves. This shouldn't stop you from creating your own functions to visualize these objects!
208174

209175
### Creating a new theme
210176

@@ -245,3 +211,31 @@ test_that("output of ggplot() is stable", {
245211
vdiffr::expect_doppelganger("A blank plot", ggplot())
246212
})
247213
```
214+
215+
### ggplot2 in `Suggests`
216+
217+
If you use ggplot2 in your package, most likely you will want to list it under `Imports`. If you would like to list ggplot2 in `Suggests` instead, you will not be able to `#' @importFrom ggplot2 ...` (i.e., you must refer to ggplot2 objects using `::`). If you use infix operators from ggplot2 like `%+replace%` and you want to keep ggplot2 in `Suggests`, you can assign the operator within the function before it is used:
218+
219+
```{r}
220+
theme_custom <- function(...) {
221+
`%+replace%` <- ggplot2::`%+replace%`
222+
223+
ggplot2::theme_grey(...) %+replace%
224+
ggplot2::theme(panel.background = ggplot2::element_blank())
225+
}
226+
```
227+
228+
```{r, include=FALSE}
229+
# make sure this function runs!
230+
mpg_drv_summary() + theme_custom()
231+
```
232+
233+
Generally, if you add a method for a ggplot2 generic like `autoplot()`, ggplot2 should be in `Imports`. If for some reason you would like to keep ggplot2 in `Suggests`, it is possible to register your generics only if ggplot2 is installed using `vctrs::s3_register()`. If you do this, you should copy and paste the source of `vctrs::s3_register()` into your own package to avoid adding a [vctrs](https://cran.r-project.org/package=vctrs) dependency.
234+
235+
```{r, eval=FALSE}
236+
.onLoad <- function(...) {
237+
if (requireNamespace("ggplot2", quietly = TRUE)) {
238+
vctrs::s3_register("ggplot2::autoplot", "discrete_distr")
239+
}
240+
}
241+
```

0 commit comments

Comments
 (0)