Roxygen2 - How to Properly Document S3 Methods

Roxygen2 - how to properly document S3 methods

The @method tag generates \method entries in the \usage field in Rd files.

The @S3method tag generates S3method() entries in the NAMESPACE file.

The @export tag generates export() entries in the NAMESPACE file.

Here is my example:

#' A description of MyHappyFunction
#'
#' A details of MyHappyFunction
#'
#' @title MyHappyFunction: The my happy function
#' @param x numeric number
#' @param ... other arguments
#' @examples
#' a <- 1
#' class(a) <- "lm"
#' MyHappyFunction(a)
#'
#' @rdname MyHappyFunction
#' @export MyHappyFunction
MyHappyFunction <- function(x, ...){
UseMethod("MyHappyFunction")
}

#' @return \code{NULL}
#'
#' @rdname MyHappyFunction
#' @method MyHappyFunction lm
#' @S3method MyHappyFunction lm
MyHappyFunction.lm = function(x, ...) {
# do some magic
}

#' @return \code{NULL}
#'
#' @rdname MyHappyFunction
#' @method MyHappyFunction default
#' @S3method MyHappyFunction default
MyHappyFunction.default = function(x, ...) {
# do some magic
}

Sample Image

3 From the wiki page...

I guess that it means "you do not write @S3method generic mymethod myobject."

How to properly document a S3 method of a generic from a different package, using Roxygen?

As of roxygen2 > 3.0.0., you only need @export because roxygen can figure out that print.surveyor is an S3 method. This means that you now only need

#' Prints surveyor object.
#'
#' @param x surveyor object
#' @param ... ignored
#' @export
print.surveyor <- function(x, ...){
cat("Surveyor\n\n")
print.listof(x)
}

However, in this case since the documentation isn't very useful, it'd probably better to just do:

#' @export
print.surveyor <- function(x, ...){
cat("Surveyor\n\n")
print.listof(x)
}

Roxygen2: documenting S3 class used as S4 when overloading R base function (cor)

Rather than set an S4 generic method for cor(), you can redefine it as an S3 generic and define methods for it. To illustrate, I created an R package with only two R files, "buildClass.R", and "cor.R", reproduced below:

buildClass.R:

#' Stranger Class Constructor
#'
#' Put some details about it
#'
#' @param x an object
#'
#' @export
buildClass <- function(x){
class(x) <- c("stranger", class(x))
return(x)
}

cor.R

#' Cor
#'
#' Put some details about it
#'
#' @param x an object
#' @param ... other arguments
#'
#' @rdname cor
#' @export
cor <- function(x, ...) {
UseMethod('cor', x)
}

#' @rdname cor
#' @export
cor.stranger <- function(x, ...) {
return(1)
}

#' @rdname cor
#' @export
cor.default <- function(x, ...) {
return(stats::cor(x, ...))
}

Then, if you load your package (named in my case "anRpackage"), the user will be warned that the package masks stats::cor, but the way cor.default() is defined, stats::cor() is called for objects that are not of class stranger:

library(anRpackage)

Attaching package: ‘anRpackage’

The following object is masked from ‘package:stats’:

cor

set.seed(1234)
regular_mat <- matrix(rnorm(100), nrow = 25)
stranger_mat <- buildClass(regular_mat)
cor(regular_mat)

[,1] [,2] [,3] [,4]
[1,] 1.00000000 0.1531414 -0.01948986 -0.3737424
[2,] 0.15314141 1.0000000 0.17531423 -0.1752925
[3,] -0.01948986 0.1753142 1.00000000 0.4371213
[4,] -0.37374237 -0.1752925 0.43712127 1.0000000

cor(stranger_mat)
[1] 1

When checking the package with devtools::check() using the default cran = TRUE (which checks "using the same settings as CRAN uses"), no errors, warnings, or notes were raised:

> check(current.code)
Updating anRpackage documentation
Loading anRpackage
Setting env vars ----------------------------------------------------------------
CFLAGS : -Wall -pedantic
CXXFLAGS: -Wall -pedantic
Building anRpackage -------------------------------------------------------------
'/usr/lib/R/bin/R' --no-site-file --no-environ --no-save --no-restore --quiet \
CMD build '/home/duckmayr/anRpackage' --no-resave-data --no-manual

* checking for file ‘/home/duckmayr/anRpackage/DESCRIPTION’ ... OK
* preparing ‘anRpackage’:
* checking DESCRIPTION meta-information ... OK
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘anRpackage_1.0.tar.gz’

Setting env vars ----------------------------------------------------------------
_R_CHECK_CRAN_INCOMING_USE_ASPELL_: TRUE
_R_CHECK_CRAN_INCOMING_ : FALSE
_R_CHECK_FORCE_SUGGESTS_ : FALSE
Checking anRpackage -------------------------------------------------------------
'/usr/lib/R/bin/R' --no-site-file --no-environ --no-save --no-restore --quiet \
CMD check '/tmp/RtmpTcdHJ5/anRpackage_1.0.tar.gz' --as-cran --timings \
--no-manual

* using log directory ‘/tmp/RtmpTcdHJ5/anRpackage.Rcheck’
* using R version 3.4.3 (2017-11-30)
* using platform: x86_64-pc-linux-gnu (64-bit)
* using session charset: UTF-8
* using options ‘--no-manual --as-cran’
* checking for file ‘anRpackage/DESCRIPTION’ ... OK
* checking extension type ... Package
* this is package ‘anRpackage’ version ‘1.0’
* checking package namespace information ... OK
* checking package dependencies ... OK
* checking if this is a source package ... OK
* checking if there is a namespace ... OK
* checking for executable files ... OK
* checking for hidden files and directories ... OK
* checking for portable file names ... OK
* checking for sufficient/correct file permissions ... OK
* checking whether package ‘anRpackage’ can be installed ... OK
* checking installed package size ... OK
* checking package directory ... OK
* checking DESCRIPTION meta-information ... OK
* checking top-level files ... OK
* checking for left-over files ... OK
* checking index information ... OK
* checking package subdirectories ... OK
* checking R files for non-ASCII characters ... OK
* checking R files for syntax errors ... OK
* checking whether the package can be loaded ... OK
* checking whether the package can be loaded with stated dependencies ... OK
* checking whether the package can be unloaded cleanly ... OK
* checking whether the namespace can be loaded with stated dependencies ... OK
* checking whether the namespace can be unloaded cleanly ... OK
* checking loading without being on the library search path ... OK
* checking dependencies in R code ... OK
* checking S3 generic/method consistency ... OK
* checking replacement functions ... OK
* checking foreign function calls ... OK
* checking R code for possible problems ... OK
* checking Rd files ... OK
* checking Rd metadata ... OK
* checking Rd line widths ... OK
* checking Rd cross-references ... OK
* checking for missing documentation entries ... OK
* checking for code/documentation mismatches ... OK
* checking Rd \usage sections ... OK
* checking Rd contents ... OK
* checking for unstated dependencies in examples ... OK
* checking examples ... NONE
* DONE

Status: OK

R CMD check results
0 errors | 0 warnings | 0 notes

S3 method help (roxygen2)

The bug you are seeing is mostly likely caused because the list method of the common generic is not being exported, so common.default gets called instead. You can pick up this problem using devtools::missing_s3 - the function is a bit heuristic, so you may get a few false positives (e.g. it can't currently tell that is.list isn't a method). This is an incredibly common problem (it has caught me so many times), and the next iteration of roxygen will do more to prevent it.

Currently, to correctly export S3 methods with roxygen you need to do either:

  • @S3method generic class (and nothing else) if you don't want to document the method
  • @method generic class and @export if you want to export and document it.

You should never have @S3method and @method in the same documentation block.

Update for roxygen2 >3.0.0

Now roxygen automatically figures out if a function is an S3 method so:

  • Never use @S3method or @method
  • Use @export if you want the method to be exported (which you normally do, even if the generic isn't)

Expanding the basic methods for a R class and how to document with roxygen2?

You want to have objects of a new class and have those objects processed by methods of generic functions.

To create a new generic you have to use UseMethod and it's a good idea to create a default method.

#' @title makeThings is the main function
#'
#' @description
#' This is what this function does.
#'
#' @export
makeThings <- function(x) {
res <- list(A = 1:10, B = 40:50)
class(res) <- "things"
return(res)
}

#' @title explain is another, more verbosed, method for showing things.
#'
#' @description
#' use explain( makeThings(43) ) to have more details.
#'
#' @details
#' This is the generic function
explain <- function(x, ...) UseMethod("explain")

#' @method explain default
#' @export
explain.default <- function(x, ...) print(x)

#' @method explain things
#' @export
explain.things <- function(x, ...) {print("The meaning is ...")}

#' @examples
explain("Hello, world!")
#> [1] "Hello, world!"
explain(makeThings(3))
#> [1] "The meaning is ..."

Created on 2022-07-28 by the reprex package (v2.0.1)


Then these functions should be exported from the package's namespace.

roxigen will write the following in the file NAMESPACE And with a message saying not to edit by hand.

S3method(explain, things)
export(explain)
export(makeThings)


Related Topics



Leave a reply



Submit