4

I have figured out how to make a package that contains some color palettes that I frequently use. I modified the code in this blog post to get it to work. The following code is reproducible.

My specific question though is that this only works when I run the functions create_palette() and create_palette_list() below. Those functions, as currently structured to make two objects that have to be available for the functions scale_color_mine() and scale_fill_mine() to work. So, my question is how do I modify the code in the package wlucolors so that those objects are made available the moment the package wlucolors is loaded.

I think this has something to do with making global variables available to some other functions, and maybe assigning something to the global environment, but I'm really not sure.

Thanks for any insights.

remotes::install_github("sjkiss/wlucolors")
library(wlucolors)

library(tidyverse)
create_palette()
create_palette_list()
data("mtcars")
ggplot(mtcars, aes(x = hp, y = mpg, col = cyl)) +
  geom_point() +
  scale_color_mine(palette = "federal", discrete = F)
df <- data.frame(party = c("lib", "con", "bq", "ndp", "green"),
  voteshare = c(12, 15, 15, 16, 18))
ggplot(df, aes(x = party, y = voteshare, fill = party)) +
  geom_col() +
  scale_fill_mine(palette = "federal", discrete = T)
df <- data.frame(party = c("lib", "con", "bq", "ndp", "green"),
  voteshare = c(12, 15, 15, 16, 18))
ggplot(df, aes(x = party, y = voteshare, fill = party)) +
  geom_col() +
  scale_fill_mine(palette = "federal", discrete = T)
Waldi
  • 39,242
  • 6
  • 30
  • 78
spindoctor
  • 1,719
  • 1
  • 18
  • 42
  • Are you looking for `.onLoad` [here](https://stackoverflow.com/questions/20223601/r-how-to-run-some-code-on-load-of-package)? – Rui Barradas May 02 '21 at 06:18

3 Answers3

3

Although .onLoad is one option, I think that it wouldn't be the recommended way to go about this (we can read here for what .onLoad is mainly used).

The question is, do you want my_colors to be available to the users? Or do you just want it to be available as internal data for your functions. From looking at your github repo I think the latter is the case, but lets go through both options. They are both explained in the link above from @Waldi's answer.

  1. Make the colors and palettes available as external data to the user. You would do this with usethis::use_data(my_colors) and then you'd just need to document the data, but do not use @export in the documentation. In the description file you could set LazyData: true so the data is only loaded into memory when actually used. In this case your functions can use the data, but it would also be available to the users.

  2. Alternatively, you could make the colors and palettes available as internal data for your functions. This can be done with the same function from {usethis}, the only difference is we set the internal argument to TRUE: usethis::use_data(my_colors, internal = TRUE). Now you can refer inside your function to the data my_palettes and the function argument can be used to subset my_palettes.

For example:

scale_color_mine(palette = "federal")

would internally do:

scale_color_mine  <- function(palette) {
# do stuff here
cur_palette <- my_palettes[[palette]]
# do stuff here
}

In this case the users won't be able to access the data, but the good thing is that my_colors and my_palettes wouldn't populate the namespace.

TimTeaFan
  • 17,549
  • 4
  • 18
  • 39
  • That's a very neat approach. Another option would be to store your palettes in a vector within a function, as does `RColorBrewer::brewer.pal`. This is the underlying function which allows selection of different palettes in `scale_..._brewer` - it makes use of `switch()`. @spindoctor . I'd check also how ggplot2 handles this palette. It's quite complex. – tjebo May 02 '21 at 09:31
  • @tjebo: That might actually the best option, an internal data generating function. I usually use those in shiny apps but for this problem it might be the best fit. Please suggest it as an answer. – TimTeaFan May 02 '21 at 10:02
  • 1
    After having looked at the source code, I think the OP's problem was more of other, more fundamental nature. See my suggested answer. – tjebo May 03 '21 at 05:14
  • 1
    Thank you. This is all extremely useful. I learned a lot! I'm not going to be submitting this to CRAN at all, at most just sharing this package with colleagues. – spindoctor May 03 '21 at 10:41
  • Could you update the link/reference? (If still relevant). It is currently broken. – Baraliuh Mar 03 '23 at 04:49
2

If you want to make the my_colors vector available without writing it to the current environment using .onLoad as suggested in comments, you can create a my_colors.R file for it in the /R directory of your package, see:

my_colors <- c(
  `liberal`        = "darkred",
  `conservative`   = "darkblue",
  `ndp`            = "orange",
  `bq`             = "cyan",
  `green`          = "darkgreen",
  `laurier1`       = "darkorchid",
  `laurier2`       = "gold")

#' My colors
#'
#' available colors vector
#'
#' @format a vector with the colors I need
#' @export

"my_colors"
Waldi
  • 39,242
  • 6
  • 30
  • 78
2

You have not exported any of your functions into the namespace. I think you are already there, but you can just not "see" the object, and this is OK. The package will be able to see it. It is kept internal.

You should really check how to document packages. - even if it is not for CRAN (which is a whole other story), you will want to properly document the functions for yourself. I personally use roxygen2 for documentation, as probably most people do. Check Hadley's amazing book and I learned also a lot from Karl Broman's blog. I personally also add the #' @keywords internals tag to the function so that it won't show up in the pdf manual which is generated if you should ever upload it to CRAN.

Use #' @export if you want an object to be available to the user (this can be just yourself) on loading a namespace. If you don't want it available, just don't use that tag, but your package will still have those functions available. (you can check that by typing wlucolors:::create_palette which should show your function after installing (not even loading) your package.

Of another note, make sure you always correctly reference other functions, e.g., discrete_scale becomes ggplot2::discrete_scale, and then you also need to tell in the header that you have imported from ggplot2. This is especially true if you write a package for CRAN. (If you don't do that, your package won't be accepted).

Also - don't do global assignments - that's very bad practice. Check R inferno circle 6

tjebo
  • 21,977
  • 7
  • 58
  • 94