How to properly document S4 methods using roxygen2

Paul McMurdie picture Paul McMurdie · Sep 9, 2011 · Viewed 9.9k times · Source

I've seen some discussions in SO and other places regarding how this should be or will be done in future versions of Roxygen2. However, I am stuck. How should I go about documenting a S4 generic, as well as its methods, using Roxygen2? A working example for a brand new generic/methods, as well as an example for extending base S4 generic would be incredibly useful. I do not want to have to make separate (mostly) redundant documentation for each S4 method of the same generic.

Due dilligence: I have tracked down a useful example for the "extract" method. However, it appears to be outdated and incomplete for my question. It uses @slot tag in the class documentation, which is not (no longer?) supported. It only shows an extension of a core S4 method "[", rather than a complete Roxygen example including the documentation of the S4 generic.

How to properly document S4 "[" and “[<-“ methods using roxygen?

If I fully document a new S4 generic with title, description @param @return @name @aliases @docType @rdname, and then document the S4 method with just the corresponding @name @aliases @docType @rdname, I get the following R CMD check warning:

* checking for missing documentation entries ... WARNING
Undocumented S4 methods:
<< long list of apparently undocumented methods. E.g. generic 'plot' and siglist 'myClass1,ANY' >>
All user-level objects in a package (including S4 classes and methods)
should have documentation entries.

It looked at first as though none of my S4 methods documented in this fashion with roxygen2 had actually worked. However, so far, I've noticed that my extensions of the core method "show" do not have an associated error, even though they were documented in exactly the same way as the rest. Here is an example of the complete roxygen documentation I included above one of the show methods, that did NOT generate the missing-documentation error:

#' @name show
#' @aliases show,myClass2-method
#' @docType methods
#' @rdname show-methods

Thus, I'm at a loss. As you can see, I've included the convention for aliases for S4 methods described in the S4 documentation section of the R package manual, namely that methods should have an alias with the following name (without space):

generic,signature_list-method.

Somehow, this is not completely being understood by R CMD check.

Finally, after building the documentation using:

library("devtools")
library("roxygen2")
document("mypkgname")
check_doc("mypkgname")

and building the package, I get functioning documentation. Any titles from a specific method's documentation winds up in the 'Description' field, rather awkwardly. So roxygen2 obviously did something with each method's documentation and is on the right track. However, it is not enough to avoid a large and troubling warning from

> R CMD check mypkgname

I've looked at the Rd files, but I know even less about them to quickly see what's the problem is, and I anyway want to know the roxygen2 solution so that I will not have to manipulate the Rd files directly after each documentation revision.

So this is a multipart question:

  1. What is the current recommended approach for both S4 generic and S4 method documentation with roxygen2?

  2. Is there a good example available somewhere that shows the complete details?

  3. What might be the cause, and solution, to the warning that documentation for most S4 methods is missing (even though the methods with "missing" documentation actually have had their doc parsed by roxygen2 and the resulting Rd files are at least good enough to work in the package after R CMD build mypkgname) ?

Answer

Paul McMurdie picture Paul McMurdie · Sep 15, 2011

Official Support, explained in devtools doc

http://r-pkgs.had.co.nz/man.html#man-classes (scroll down to the S4 subsection).

The current short example from that page is reproduced in the following (for convenience):

#' An S4 class to represent a bank account.
#'
#' @slot balance A length-one numeric vector
Account <- setClass("Account",
  slots = list(balance = "numeric")
)

The above link very clearly explains S3, S4, and RC support via roxygen/devtools. The explanation there should be considered to supersede the older answer below, which does work for now, but is less clear and more complicated.

The old answer

Here is an example that should work for most S4 methods.

For documenting S4 generics, I find the following three lines necessary in most of my generic headers:

#' @export
#' @docType methods
#' @rdname helloworld-methods

Where helloworld-methods is replaced with the_name_of_your_generic-methods. This will be the name of the Rd file that holds the documentation for the generic and all its methods. This naming convention is not required, but common and useful. The @export tag is necessary now that a namespace is required for your package and you want this method to be available to users of your package, not just other methods/functions in your package.

For documenting methods, I find that only 2 lines are necessary, providing the @rdname and @aliases tag. The @rdname statement should match exactly the one for the generic. The @aliases tag must adhere to a naming convention described in the official S4 documentation section of Writing R Extensions.

#' @rdname helloworld-methods
#' @aliases helloworld,character,ANY-method

There should be no spaces after the commas in the @aliases name. I don't know the exact rules surrounding when to add ,ANY to the end of the signature list. The pattern seems to be that the number of elements in all @aliases signature lists needs to match the number of elements in the longest signature vector among the methods. In the example below, one of the methods is defined with 2-element signature, and so R CMD check wasn't satisfied with the documentation unless ,ANY was added to the aliases tag of the other methods, even if their signature definition only has one element.

A complete example follows. I built this and it works with no documentation-level warnings from R CMD check testpkg, using a bug-fixed fork of a very recent devel version of roxygen2 (which I have made available). For quick installation of this fork on your system, use library("devtools"); install_github("roxygen", "joey711"). The most recent version of roxygen2 won't work this moment because of a quotation error (described in a separate answer), but I expect this will be incorporated very soon and my fork won't be necessary. For looking at this example in context of the rest of a package, and seeing the resulting documentation (Rd) files, I have made it available as a trivial test package on github called testpkg.

##############################################################
#' The title, in this case: Helloworld-ify the argument.
#'
#' Some additional details about this S4 generic and its methods.
#' The extra blank line between this section and the title is
#' critical for roxygen2 to differentiate the title from the
#' description section.
#'
#' @param x Description of \code{x}. The main argument in this
#'  example. Most often has such and such properties.
#'
#' @param y Description of \code{y}. An argument that is rarely
#'  used by \code{"helloworld"} methods. 
#'
#' @param ... Additional argument list that might not ever
#'  be used.
#'
#' @return A helloworld-ified argument. Oh, you'll see.
#' 
#' @seealso \code{\link{print}} and \code{\link{cat}}
#' 
#' @export
#' @docType methods
#' @rdname helloworld-methods
#'
#' @examples
#' helloworld("thisismystring")
#' helloworld(char2helloworld("thisismystring"))
#' helloworld(matrix(0,3,3))
#' helloworld(list(0,0,0))
#' helloworld(integer(0))
setGeneric("helloworld", function(x, y, ...){
    cat("Hello World!")
    cat("\n")
    standardGeneric("helloworld")
})

#' @rdname helloworld-methods
#' @aliases helloworld,ANY,ANY-method
setMethod("helloworld", "ANY", function(x, y, ...){
    cat(class(x))
})

#' @rdname helloworld-methods
#' @aliases helloworld,character,ANY-method
setMethod("helloworld", "character", function(x){
    show(x)
})

#' @rdname helloworld-methods
#' @aliases helloworld,character,character-method
setMethod("helloworld", c("character", "character"), function(x, y){
    show(x)
})

This example assumes that you don't need separate method-specific documentation because your methods haven't veered wildly from the behavior one would expect based on the generic doc. There are means in roxygen2 to handle that alternative case using the @usage tag, but the details would be better handled by a separate SO question.

So most of your documentation effort goes into the roxygen header above the generic definition. This has some basis in the code, since the generic should include all specific arguments that appear in any of the subsequently defined methods . Note that arguments that are handled as part of the ... in the argument list are not included and shouldn't be documented specifically, but the ... itself should be documented above the generic as well (see the full example below).

For further details on the basics of documenting functions, there is a wiki in progress, the old roxygen vignette, as well as the roxygen2 development site on github. There is also a SO question on R documentation with Roxygen in general.