Home » Uncategorized » Developing R packages using roxygen and devtools

Developing R packages using roxygen and devtools

I feel like I’ve arrived late to a party. Idly googling, I discovered roxygen2, a “Doxygen-like in-source documentation system for Rd, collation, and NAMESPACE”. With roxygen2, you can use the functions in devtools to automate lots of the painful parts of writing/updating/testing packages. To convert existing packages to roxygen format, there’s Rd2roxygen .

A common denominator to all these is Hadley Wickham, who is quickly becoming my R hero.

So, how does this work? I decided to take an existing package of mine that needed a little update, and have a go.

After backing up my package directory, I begain by roxygenize-ing an existing package

options(roxygen.comment = "##' ") ## allow ESS style comments
##------ Mon Jan 28 13:59:25 2013 ------##
parsed: snpStatsWriter-package.Rd
unmatched object 'snpStatsWriter-package' written into snpStatsWriter/R/snpStatsWriter-package.R

##------ Mon Jan 28 13:59:25 2013 ------##
parsed: write.simple.Rd
looking for the object 'write.simple' in:
  snpStatsWriter-package.R: not found
  write.R: line 21
snpStatsWriter/R/write.R updated

Then I checked around in the files, played with my documentation a little (easy to do with it being right next to the functions). One function now looks like

##' Write a snpStats object in PHASE/FastPHASE format
##' see \code{\link{write.simple}} for general information
##' @inheritParams write.simple
##' @param pedfile Output file name. 
##'@param trait disease trait (0=missing, 1=control, 2=case)
##'@param gfile,mfile \code{gfile}=genotype file, \code{pedfile}=pedigree file
##' @return No return value, but has the side effect of writing specified output
##' files.
##' @author Chris Wallace
##'@keywords manip
##'A.small <- Autosomes[1:6,1:10]
##'f <- tempfile()
##'## write in suitable format for PHASE
##'nsnps <- ncol(A.small)
##'write.phase(A.small, file=f)
write.phase <- function(X,a1=rep(1,ncol(X)),a2=rep(2,ncol(X)),bp=NULL,file) {
  if(!is.null(bp) && length(bp)!=ncol(X))
    stop("require ncol(X) == length(bp)")
  res <- .C("write_phase", X@.Data, as.character(a1), as.character(a2),
            as.character(file), as.integer(nrow(X)),
            as.integer(ncol(X)), rownames(X), colnames(X),
            as.integer(!is.null(bp)), as.integer(bp), get.eol())
  return(c(nrow(X), ncol(X)))

The @inheritParams flag is great, because it means I only need document common arguments once (in write.simple) and devtools will know to copy it as appropriate. I had documented everything before in a single file with aliases, because I was too lazy to write multiple .Rd files. Now, I’ve used cut and paste and the @inheritParams flag to document each function individually.

Now, to use devtools to build, document and test the package. First, load from the .R files:

> load_all("snpStatsWriter")
Loading snpStatsWriter
Re-compiling snpStatsWriter
'/usr/lib/R/bin/R' --vanilla CMD INSTALL  \
  '/home/chrisw/local/R/packages/snpStatsWriter' --library='/tmp/RtmpWxyxLf'  \
  --no-R --no-data --no-help --no-demo --no-inst --no-docs --no-multiarch  \

* installing *source* package 'snpStatsWriter' ...
** libs
gcc -std=gnu99 -I/usr/share/R/include -DNDEBUG      -fpic  -O3 -pipe  -g  -c writers.c -o writers.o
gcc -std=gnu99 -shared -o snpStatsWriter.so writers.o -L/usr/lib/R/lib -lR
installing to /tmp/RtmpWxyxLf/snpStatsWriter/libs

* DONE (snpStatsWriter)
Loading required namespace: snpStats
Loading required package: snpStats
Loading required package: survival
Loading required package: splines
Loading required package: Matrix
Loading required package: lattice

Then, write .Rd files

> document("snpStatsWriter")
Loading required package: roxygen2
Loading required package: digest
Updating snpStatsWriter documentation
Loading snpStatsWriter
Updating collate directive in  /home/chrisw/local/R/packages/snpStatsWriter/DESCRIPTION 
Updating namespace directives
Writing snpStatsWriter-package.Rd
Writing write.simple.Rd
Writing write.mach.Rd
Writing write.impute.Rd
Writing write.beagle.Rd
Writing write.phase.Rd

and check

> check_doc("snpStatsWriter")
prepare_Rd: write.simple.Rd:75: unknown macro '\n'
prepare_Rd: write.simple.Rd:77: unknown macro '\r'
prepare_Rd: write.simple.Rd:77: unknown macro '\n'
Documented arguments not in \usage in documentation object 'write.beagle':

Documented arguments not in \usage in documentation object 'write.phase':
  ‘pedfile’ ‘trait’ ‘gfile’ ‘mfile’

Documented arguments not in \usage in documentation object 'write.simple':

OK… a little editting and rerunning of the last two steps needed till I got

> check_doc("snpStatsWriter")

Then do the full check

> check("snpStatsWriter")
Updating snpStatsWriter documentation
Loading snpStatsWriter
'/usr/lib/R/bin/R' --vanilla CMD build  \
  '/home/chrisw/local/R/packages/snpStatsWriter' --no-manual --no-resave-data 

* checking for file '/home/chrisw/local/R/packages/snpStatsWriter/DESCRIPTION' ... OK


There were two errors. First, my functions, even though they were exported in the old NAMESPACE file, did not carry the @export tag after Rd2roxygen(), so I had to manually add the line

#' @export

above each function I wanted exported. Second, after doing this, the useDynLib() from NAMESPACE disappeared, and I had to add the line

#' @useDynLib snpStatsWriter

to R/snpStatsWriter-package.R

To reload, I had to do

> load_all("snpStatsWriter",TRUE)
> document("snpStatsWriter")
> check("snpStatsWriter")

till I got the all clear. Finally I built and submitted my package to CRAN using


and this function even wrote the email!

So, that took less than an hour to learn (ish) three new packages, and release a package. I still want to learn testthat though, so I can feel virtuous for doing more testing than simply running example code.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s