Tuesday, 5 March 2013

Create an R package from a single R file with roxyPackage

Documenting code can be a bit of a pain. Yet, the older (and wiser?) I get, the more I realise how important it is. When I was younger I said 'documentation is for people without talent'. Well, I am clearly loosing my talent, as I sometimes struggle to understand what I programmed years ago. Thus, anything that soothes the pain of writing and maintaining documentation must be good and should help me to better understand my 'old me' in the future.

Ideally I want my R code and documentation in as few files as possible. A good start to achieve this is using roxygen2, an R package which has been around for some time. It allows me to tie R code together with the documentation in the same file and helps considerably in maintaining R packages.

The roxyPackage by Meik Michalke goes a step further, building on roxygen2. Meik presented his package at the Cologne R user group meeting a few weeks ago and I was intrigued by it. As I said in my notes to the meeting, with roxyPackage I can create a package from a single file of R code and documentation.

Here is an example of one R file to build a package using roxyPackage. For my toy example I wrote a doughnut plot function in R, something which is clearly missing ;)


I took the basic pie chart function and amended it to plot another white disc in the middle. On top of the function code I wrote the help file documentation using the roxygen2 syntax.


In my next step I use roxyPackage to turn this file into its own standalone Doughnut package. Following the roxyPackage vignette I have to pass a data frame with the details of a typical DESCRIPTION file to roxyPackage for it to turn the R file into a package.

The arguments pck.source.dir, R.libs, repo.root specify the folders in which I store my R file, my R libraries and output package respectively.


Job done. After executing the above commands I end up with my package build and installed on my system. Additionally, roxyPackage created binary packages for Windows and Mac in my very own repository (my own little CRAN). In the future I only have to update the version number in the above data frame, rather than DESCRIPTION file, package.Rd help file and NEW/ChangeLog. Fantastic!

Closing remarks

This post is dedicated to my colleagues, who I have pestered with end user computing (EUC) standards over the last months. I believe with roxygen2 and roxyPackage it won't be too burdensome anymore to comply with those standards.

Indeed, I think roxygen2 and roxyPackage can help novice R users and those who start programming to think a little bit like professional IT guys (I know, I haven't even mentioned testing or version control yet). Often users just want to get their job done and this is were the above approach can help them to document their work without too much pain.

Session Info

R version 2.15.2 (2012-10-26)
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] tools     stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] roxyPackage_0.03-1 XiMpLe_0.03-16     roxygen2_2.2.2     digest_0.6.1      

loaded via a namespace (and not attached):
[1] brew_1.0-6    stringr_0.6.2

6 comments:

  1. Neat. Can you explain which text you had to type yourself and which you just copied from the existing function and documentation. It looks like you added this:

    ## Add white disc

    Pin <- t2xy(seq.int(0, 1, length.out = n*nx),

    inner.radius)

    polygon(Pin$x, Pin$y, density = density[i],

    angle = angle[i], border = border[i],

    col = "white", lty = lty[i])



    in the R code. But what were you able to take from the existing documentation of pie() and what did you have to add yourself?

    ReplyDelete
  2. Very nice! It would be great if the myDescription info could be encoded as roxygen in the original file as well.

    ReplyDelete
  3. Indeed, I added the code you mentioned above. Additionally, type 'pie' into the R console and you will find the original code. You will notice that I split the argument radius into inner.radius and outer.radius. Hence, I could recycle most of the original content of the pie help file as well.

    ReplyDelete
  4. hi joe,

    i'm not involved in the roxygen development myself, so i'm afraid i cannot help much here. but what i usually do is to include the data.frame in the package itself, as a non-exported object, and reference it as myPackage:::myDescription in the roxy.package() call. (which of course only works after the package was installed for the first time)


    internally, the package uses write.dcf() to write the DESCRIPTION file, so it should be in data.frame format sooner or later. i split the contents of that file into two parts: firstly, version number and date, and secondly, everything else ;-)


    this is because the "everything else" part usually doesn't change so often, but version number and release date change all time. therefore those two are supplied as direct arguments to the roxy.package() function call, whereas all the rest can be put into one rather static object in the package.

    ReplyDelete
  5. Thanks, I saw this on the R-code, but what about the roxygen material, lines 1-57 in your example?

    ReplyDelete
  6. Hi

    Thanks for this post. It was a very useful post for new users like me. I have created my package. I would also like to know, how to include other packages into my package. For example, inside my package, I would like to call a funciton called "hcluster" which is inside the package "amap". Right now, I am loading it through "library(amap)" code written inside my R script. Is this the right way, if not, kindly let me know of the formal way to include other packages into my package.

    Thanks,
    Yogalakshmi

    ReplyDelete