This is very much work in progress so look for later posts about CECPfuns as well as this.
### this is just the code that creates the "copy to clipboard" function in the code blocks
htmltools::tagList(
xaringanExtra::use_clipboard(
button_text = "<i class=\"fa fa-clone fa-2x\" style=\"color: #301e64\"></i>",
success_text = "<i class=\"fa fa-check fa-2x\" style=\"color: #90BE6D\"></i>",
error_text = "<i class=\"fa fa-times fa-2x\" style=\"color: #F94144\"></i>"
),
rmarkdown::html_dependency_font_awesome()
)
I have been meaning to do this for years but I have still found it one of R’s more tough learning curves even by R’s sometimes challenging standards. The tidyverse is perhaps a higher and better known climb but making this package is K2 to the tidyverse Everest: nastier, colder, more dispiriting and my attempt of the ascent a few years ago hardly got beyond base camp. This time I’m sort at a first support camp level above base camp and I’m trying to document things here.
I would have saved many hours over the last few years had I actually got on top of this when I first started. Why:
There are hugely powerful tools to help the creation of R packages and many pages and PDFs on the web to help you. However, for me finding exactly the information I need, getting its context, being sure the advice isn’t outdated and sometimes just understanding what people have written has not always been easy. That’s partly why I’ve created this.
Please, I will try to remember to amend any mistakes I find in here, or things I discover change or can be done more easily than whatever I say here, but anything here doesn’t work for you, please:
Hm, that I’m writing that probably conveys that this has been a bit tricky.
OK, the first bit is easy: create a new package using a new directory and the “Create R package” option; give your package a name, e.g. “SillyDemoPackage”. There is the option to include some source (i.e. R code for our purposes) files here but I would recommend starting completely cleanly and creating new source files, one per function, and copying and pasting the code you already have into the new file.
That will have created a subdirectory of wherever you were titled named “SillyDemoPackage” and beneath it you have three more subdirectories:
devtools::document()
, create Rd files that in turn create the help for the package and functions)That’s your next step: create a new R script file; if your function is myFunction() then save the script into the R subdirectory that creating the project will have created.
You now have a single source file with a single function in it. (I think you can put more than one function in a single source file but I think it would be making your life more difficult so don’t).
Put your cursor inside the function then go to the Code menu above and select “Insert Roxygen Skeleton”. Let’s say I start with this:
myFunction <- function(x){
return(paste("This is a silly function of", x))
}
Using Code, Insert Roxygen Skeleton changes that to this
#' Title
#'
#' @param x
#'
#' @return
#' @export
#'
#' @examples
myFunction <- function(x){
return(paste("This is a silly function of", x))
}
And you now change that to this:
#' Title
#' This is a very silly function purely for demonstration.
#' @param x can be any printable object
#'
#' @return a string that pastes a silly comment before x
#' @export
#'
#' @examples
#' x <- "stunning silliness"
#' myFunction(x)
myFunction <- function(x){
return(paste("This is a silly function of", x))
}
You see I have given a short description of the function, I have clarified the one parameter (argument) to the function and what the function returns and I have given a suitably inane example of how it might be used.
Now put your cursor in the function and type devtools::document()
. That will (essentially invisibly) create a new file myFunction.Rd in the /man subdirectory I mentioned above. **Remember to rerun devtools::document()
within the function every time you tweak the documentation in those header lines and every time you tweak the function otherwise the help will lag behind what you’ve done (which might or might not be caught at the next stage, but better safe than sorry.)
Now the exciting bit: under the Build menu, pick “Check” and sit back and watch Rstudio and devtools (and perhaps other things for all I know) whiz through many checks on your package (in the top right hand pane of Rstudio in the usual layout, in the Build tab. I don’t think you can miss it. Those checks can flag up erorrs, warnings and notes and you hope to see an all green summary line at the end saying there were none of any of those. If the checks find issues some messages are very clear and helpful, some are more challenging but I have found that searching on the web usually translates them for me.
I would then make sure you set up version control on the project using git and I would also recommend then pushing the package to GitHub if you want others to be able to find it easily.
OK, I lie. That’s it for the SillyDemoPackage and it’s one function, myFunction()
. I think that’s a like a short afternoon stroll out of Kathmandu in summer. When you start to do something remotely useful the gradient goes up a bit and the air gets a little thinner.
This is a huge issue but actually fairly easy to handle. Most useful functions will call on functions from packages outside of the base functions. Where you do this you need to handle declaring these in a way that means that the package will know what comes from where. There are simple and more sophisticated issues here and the Build, Clean error messages are pretty clear and helpful and there are good guides to the subtleties on the web. So far I have stayed with making the function calls explicit so instead of cor(x, y)
I write stats::cor(x, y)
in the function and then I add:
Suggests:
stats
at the bottom of the DESCRIPTION file in the root directory of the package and
importFrom("stats", "cor")
at the bottom of the NAMESPACE file, also in the root directory of the package. I think usethis::use_package()
helps with this but I have done it manually so far.
The other thing you have to do at the head of any such function instead of having a
library(sausages) # I wouldn't have had this for stats as, of course,
### the stats package is launched by default when R starts,
### imagine I am calling sausages::recipe()
### NO! I made that up!
you use:
invisible(stopifnot(requireNamespace("sausages")))
### so a call that doesn't spit out a message but will stop things
### if you don't have the sausages package on your system
### requireNamespace() only checks if you have the package
### it doesn't load the entire package as library() or
### require() would so if you are only going to call one
### or a few functions explicitly with sausages::functionName()
### this is more efficient
That’s the lightest way to do things. If you are going to use lots of functions from a package you may be better with other options but this works for me for now.
Added 28.ii.21: dept to Winston Change! If you’re using M$ Windoze I think it’s best to ignore this section. Because Windoze won’t let anything alter a dll on disc that has been loaded into memory, with the really rather complicated way that R (and Rstudio too) pull things into memory as they launch and run .Rprofile this tends to lead to some package upgrading being blocked, e.g. of cachem which Winston maintains.
I am developing my package on my main Linux laptop. As I can’t really survive without it, I have a near duplicate backup machine and a little, old Windows laptop and Windows in a VM on the main machine and I have R on my web server (serving up this blog, my CORE work https://www.coresystemtrust.org.uk/; my non-CORE work site https://www.psyctc.org/psyctc/; and my personal web site: https://www.psyctc.org/pelerinage2016/. Do go and have a look!)
I wanted to make sure that every time I (or cron) launched R on any of the those machines it would automatically check for an update to the package on GitHub and install it if there were one. That meant putting a call to install it with devtools::install_github("cpsyctc/CECPfuns", build_vignettes = TRUE, build_manual = TRUE)
into .Rprofile.
Added evening 18.ii.21 with input from Clara
### Locating your .Rprofile file
You should find, or create that in locations that are operating system dependent:
* on linux machines it is /home/username/.Rprofile
* on Windows machines it is C:/Users/username/Documents/.Rprofile
* on Macs I am told it is /Users/username/.Rprofile and I am also told that as it is a hidden file, you will need cmd + shift + [.] in order to show the hidden files.
Added evening 10.ii.21, with help from Bill Dunlap via the R-help Email list
However, my original addition to .Rprofile cause R to keep looping when launched. Bill Dunlap confirmed that’s because something, probably invoked by the devtools::install_github("cpsyctc/CECPfuns", build_vignettes = TRUE, build_manual = TRUE)
call, is restarting the R session and so rerunning the .Rprofile, and so on ad infinitum and Bill gave me the answer so my .Rprofile is now:
if (Sys.getenv("INSTALLING_FROM_GITHUB", unset = "no") == "no") {
Sys.setenv(INSTALLING_FROM_GITHUB = "yes")
devtools::install_github("cpsyctc/CECPfuns", build_vignettes = TRUE, build_manual = TRUE)
}
As I understand that code, it checks for an environment variable (i.e. a variable set in the operating system) called “INSTALLING_FROM_GITHUB” and if it finds its value is “no” it runs the the commands inside the brackets, resetting the variable to “yes” and then, the next line, checking if there has been an update of the package on GitHub and installing it if there has been. However, if/when .Rprofile is rerun in that R session the environment variable now has the value “yes” so the looping is prevented. Lovely!
devtools::build_manual()
seems to build at least the typical nice PDF about a package that you see on CRAN, e.g. https://cran.r-project.org/web/packages/boot/boot.pdf but it puts it in the directory above the package directory and the file doesn’t seem to get integrated into the package which seems puzzling and less than entirely helpful to me. I’m sure there must be an answer to that but I haven’t found it yet.This is pretty embarrassing but I will share that this first actual package of mine, probably the only one I’ll ever need to create, is available if you want to see what I’ve managed to create. It will develop into a package mainly of functions I and Clara Paz have found useful (with, I hope, comments and suggestions from Emily) It’s at https://github.com/cpsyctc/CECPfuns and there is a web site for the package at https://cecpfuns.psyctc.org/. You can use git on pretty much any operating system to pull a copy from github if you want to look at the all the raw constituent parts and I think if you do pull that you can see the commit history, i.e. of the changes and updating. (A graph of the commits against date is at https://github.com/cpsyctc/CECPfuns/graphs/commit-activity). I am not opening it to submissions as it’s too early in my learning, I may never reach that place, so, if you have suggestions or corrections and any comments really,
contact me through my work site. I hope this helps someone and encourages them to create their own package. I do wish I’d done it earlier!
You get this if you have loaded the newly updated and clean package locally, perhaps with:
install.packages(file.choose(), repos=NULL)
(That’s a nice way to be able to pick your own, just made, package from your own filesystem.)
Then maybe you try to get help on one of the functions and you get the error message. Something like
Error in fetch(key) : lazy-load database ‘…’ is corrupt
where “…” refers to your package. It seems that this is down to the way that R and devtools
handle the creation of the package and the loading of it. To get round this, all you need to do is to restart the R session. If you are in Rstudio you can do this from the tab menu with Session, Restart R or at the R prompt:
.rs.restartR()
Now I reload the package (I think you have to do that) … and everything is hunky dory again.
14/10/2024 at 18:24
Text and figures are licensed under Creative Commons Attribution CC BY-SA 4.0. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".
For attribution, please cite this work as
Evans (2021, Feb. 10). Chris (Evans) R SAFAQ: Making the CECPfuns package: my own usable package. Retrieved from https://www.psyctc.org/R_blog/posts/2021-02-10-making-my-first-usable-package/
BibTeX citation
@misc{evans2021making, author = {Evans, Chris}, title = {Chris (Evans) R SAFAQ: Making the CECPfuns package: my own usable package}, url = {https://www.psyctc.org/R_blog/posts/2021-02-10-making-my-first-usable-package/}, year = {2021} }