Another package is removing my S4 'predict' method from the autocompletion candidates - r

I'm writing an R package where I implement my own 'predict' generic and an S4 method for the signature 'apk', which is an S4 class inside my package as well. I'm using Rstudio as editor and generating the documentation with Roxygen2.
All the problem goes about the autocompletion candidates that Rstudio shows when I type predict once the package is installed and loaded. Just after opening a new R session, if I type predict, I get the following candidates: predict {aPack}, predict {stats}, predict.glm {stats} and predict.lm {stats}. Note that both, the S4 from my package aPack and the one from stats are displayed as candidates. This happens because right before setting the generic, I imported predict from stats as suggested in this SO answer.
The problem: when I load another package with an S4 predict method, I'm no longer able to make my S4 show as autocomplete candidate. For instance, if I load the DiceKriging package and mine in the same session I only get the following candidates: predict {DiceKriging}, predict {stats}, predict.glm {stats}, predict.lm {stats} and predict.km {DiceKriging}. Once I load DiceKriging it is not possible to get my S4 displayed in autocomplete even if I load my package again. The predict method still works without predict {aPack} being listed by the autocompletion system, however, I would like to make it visible so that the user gets directly aware of the availability of my method.
Question: how sould I modify my roxygen documentation so that predict {aPack} gets prompted by the autocompletion system even if I load another package with a predict S4 method?
Minimal reproducible example
#' #title Class: apk model
#' #description To create an apk object, use \link[aPack]{apk}.
#' #slot call Object of class \code{"language"}. User call reminder.
#' #rdname apk-class
#' #import methods
#' #export
setClass("apk", representation(call = "language"), validity = function(object) {T})
#' #title Create an Object of class \code{"apk"}
#' #description Creator function for objects of class \code{"apk"}.
#' #param foo Not used yet.
#' #param ... Not used yet.
#' #export
apk <- function(foo, ...) {
new("apk")
}
#' #name predict
#' #rdname predict-methods
#' #importFrom stats predict
#' #param object An object to predict from.
#' #param ... Further arguments for methods.
#' #export predict
setGeneric(name = "predict", def = function(object, ...) standardGeneric("predict"))
predict.apk <- function(object, bar, ...) {
print("I'm an apk prediction!")
}
#' #title Prediction Method for the apk Class
#' #name predict
#' #rdname predict-methods
#' #aliases predict,apk-method
#' #examples
#' myApk <- apk()
#' predict(myApk)
setMethod("predict", "apk", predict.apk)
GitHub: I also uploaded the project to GitHub so that you can directly make your suggested changes there if you want. The repository is here.

Related

Roxygen documentation for existing generics

I wrote a show() method for the S4 class myclass which I had defined in my package. I generated documentation for class and method with roxygen2. Everything works nicely, except that I get the following warning in R CMD check results:
checking for missing documentation entries ... WARNING
Undocumented S4 methods:
generic 'length' and siglist 'myclass'
How do I go about documenting show generic which I did not write myself?
In the end, using explicit #aliases and #docType tags solved the issue for me. My documentation block now looks like this:
#' Show method for objects of class \code{myclass}.
#'
#' #docType methods
#' #name show-myclass
#' #rdname show-myclass
#' #aliases show-myclass show,myclass-method
#'
#' #param x A \code{myclass} object.
#'
#' #export

S3methods in NAMESPACE not exported

I am developing an R package using devtools::document() to create the NAMESPACE file. Several of the functions are S3methods for summary, predict, plot, print where the generic is in base or stats.
I am using #export as Hadley recommends and that leads to the correct S3method entry in the NAMESPACE, and the package passes all checks -as-cran. The functions are not exported in the NAMESPACE, however, so calling print.myclass is not found (I understand that is the desired behavior to avoid cluttering up the NAMESPACE). However, calling the function by Mypackage::print.myclass also leads to an error that the function is not an exported object from Mypackage.
Question: is that the correct behavior? Or are there other steps needed to have the function exported? I have tried adding both #method print Myclass and #export but with no luck. Using R 3.4.2 with devtools 1.13.3 under MAC OS X 10.12.6
Thanks! Merlise
Edited: updated to have the code that will add/export method and export function
Simple example - build a skeleton package in RStudio with function:
#' test for export of S3 methods
#'
#' #title "print hello world for any object"
#' #param x object
#' #param digits optional number specifying the number of digits to display
#' #param ... other parameters to be passed to \code{print.default}
#' #export print.hello
#' #export
print.hello = function (x, digits = max(3, getOption("digits") - 3), ...)
{
cat("\n Hello World \n")
invisible()
}
The NAMESPACE now has
# Generated by roxygen2: do not edit by hand
S3method(print,hello)
export(print.hello)
Using #export with no arguments exports the method while #export print.hello exports the function, but does not add the method to the NAMESPACE (which would lead to an error with the package check). Having both would allow the method and the function to be exported.
First of all, in order to formally define a S3 method and export it properly without manually changing the namespace file (assuming you are using roxygen),
#' test for export of S3 methods
#'
#' #title "print hello world for any object"
#' #param x object
#' #param digits optional number specifying the number of digits to display
#' #param ... other parameters to be passed to \code{print.default}
#'
#' #rdname print
#' #export print
print <- function(x, ...){
UseMethod("print")
}
#' #rdname print
#' #export print.hello
#' #export
print.hello <- function (x, digits = max(3, getOption("digits") - 3), ...)
{
cat("\n Hello World \n")
invisible()
}
This more or less gives you the expected behavior for testPackage::print.hello. The more important thing here is to understand what exactly S3 method is for. It is used for method dispatching in R and the suffix after . should always stand for the class of object you are supposed to put in as the first argument of the function. That is, in this case, if you want to use print.hello with a single call of print, you would have to put a class of hello, try the example below after you successfully build and load the testpackage
a = 1
print(a) # method dispatched as print.default because class of a is numeric
# 1
class(a) <- 'hello'
print(a) # method dispatched as print.hello
# Hello World
This is the correct behaviour for your NAMESPACE file. :: accesses exported variables, so testPackage::print.hello should fail.
::: accesses internal variables, so testPackage:::print.hello should work for you.

Export error on overwriting primitives with S3 in R package

I'm trying to create a S3 method in my package called dimnames. This is a primitive in R, but there should be an S3 in my package with the same name.
I've got the following file dimnames.r
#' S3 overwriting primitive
#'
#' #param x object
#' #export
dimnames = function(x) {
UseMethod("dimnames")
}
#' title
#'
#' #export
dimnames.data.frame = function(x) {
dimnames.default(x)
}
#' title
#'
#' #export
dimnames.list = function(x) {
lapply(x, dimnames)
}
#' title
#'
#' #export
dimnames.default = function(x) {
message("in S3 method")
base::dimnames(x)
}
I then create a package from it (in R=3.3.2):
> package.skeleton("rpkg", code_files="dimnames.r")
> setwd("rpkg")
> devtools::document() # version 1.12.0
And then check the package
R CMD build rpkg
R CMD check rpkg_1.0.tar.gz
I get the following output (among other messages):
Warning: declared S3 method 'dimnames.default' not found
Warning: declared S3 method 'dimnames.list' not found
Loading the package and checking its contents, dimnames.data.frame is exported while dimnames.default and dimnames.list are not. This does not make sense to me. As far as I understand, I declared the exports correctly. Also, the NAMESPACE file looks good to me:
S3method(dimnames,data.frame)
S3method(dimnames,default)
S3method(dimnames,list)
export(dimnames)
Why does this not work, and how to fix it?
(Bonus points for: why do I need #' title in the S3 implementations when they should not be needed with roxygen=5.0.1?)
S3 methods are only exported if it is desired that the user be able to access them directly. If they are always to be invoked via the generic then there is no need to export them.
The problem with R CMD check is likely due to defining your own generic for dimnames. Normally one just defines methods and leverages off the primitive generic already in R. Remove the dimnames generic from dimnames.r.
There should be no problem in adding methods for new classes but you may have problems trying to override the functionality of dimnames for existing classes that R's dimnames handles itself.

S4 class cross-references - what is appropriate syntax?

I'm writing an R package and currently running R CMD check via devtools::check()
I have the following roxygen2 in two of my functions that reference an S4 class (both functions return objects of this class):
#' #return An object of class \code{\link{mod_imputeMulti}}
#' #aliases mod_imputeMulti
#' #seealso \code{\link{multinomial_em}}, \code{\link{multinomial_impute}}
Yet I get the following WARNING:
Rd files with duplicated alias 'mod_imputeMulti':
'multinomial_data_aug.Rd' 'multinomial_em.Rd'
If I don't use second line -- the #aliases tag, I get this WARNING, despite the fact that I do have a link:
checking Rd cross-references ... WARNING
Missing link or links in documentation object 'multinomial_data_aug.Rd':
'mod_imputeMulti'
Missing link or links in documentation object 'multinomial_em.Rd':
'mod_imputeMulti'
See section 'Cross-references' in the 'Writing R Extensions' manual.
Note: adding \code{\link{mod_imputeMulti}} to #seealso doesn't change this (second) WARNING
What is the correct syntax?
I've looked at a lot of references but can't find the solution anywhere:
Hadley's book
The R extensions manual-cross references
The roxygen2 manual
this SO answer
I hope this is an easy one for someone--thanks in advance for the help!
Edit:
The S4 class documenation is provided below:
#' Class "mod_imputeMulti"
#'
#' #name mod_imputeMulti-class
#' #description A multivariate multinomial model imputed by EM or Data Augmentation is
#' represented as a \code{\linkS4class{mod_imputeMulti}} object. A complete
#' dataset and model is represented as an \code{\linkS4class{imputeMulti}} object.
#' Slots for \code{mod_imputeMulti} objects include: (1) the modeling method;
#' (2) the call to the estimation function; (3) the number of iterations in estimation;
#' (4) the final log-likelihood; (5) the conjugate prior if any; (6) the MLE estimate of
#' the sufficient statistics and parameters.
#' #docType class
#' #section Objects from the class: Objects are created by calls to
#' \code{\link{multinomial_impute}}, \code{\link{multinomial_em}}, or
#' \code{\link{multinomial_data_aug}}.
#' #seealso \code{\link{multinomial_impute}}, \code{\link{multinomial_em}},
#' \code{\link{multinomial_data_aug}}
#' #export
setClass("mod_imputeMulti",
representation= list(
method= "character",
mle_call= "call",
mle_iter= "numeric",
mle_log_lik= "numeric",
mle_cp= "character",
mle_x_y= "data.frame"),
validity= function(object) {
if (!object#method %in% c("EM", "DA", "NULL")) {
return("Currently only EM and DA methods are defined.")
} else if (object#mle_iter < 0) {
return("A negative iteration was given.")
}
return(TRUE)
}
)
Yes, this was a relatively simple fix.
As you can see in the S4 class documenation at the bottom of the OP, note that the name is mod_imputeMulti-class, not mod_imputeMulti.
I needed to add "-class". The #aliases tag is unneeded.
#' #return An object of class \code{\link{mod_imputeMulti-class}}
Also, thanks to #thomas for pointing out the use of #slot for proper translation of the roxygen2 tagging to man/*.Md files.

New method for plot - how to export?

I am making a package where I want to define a new method for plot. I am using roxygen in-source documentation. This question seems very similar to:
How to properly document a S3 method of a generic from a different package, using Roxygen?
and
Roxygen2 - how to properly document S3 methods
but I still cannot get it to work.
The relevant parts that are causing me trouble are:
#' Generic plot method
#'
#' #param x \dots
#' #param ... \dots
#' #export
plot <- function(x, ...) UseMethod("plot")
#' Default plot method
#'
#' #param x \dots
#' #param ... \dots
#' #importFrom graphics plot
#' #method plot default
#' #S3method plot default
plot.default <- function(x, ...) graphics::plot(x, ...)
#' Plotting function for ABI object
#'
#' Description.
#'
#' #param x ABI object as generated by newABI.
#' #param base Character. Bases to look at.
#' #param ... Other options passed to plot().
#' #return Nothing. Side-effect: plots graphs.
#' #method plot ABI
#' #S3method plot ABI
plot.ABI <- function(x, base, ...) {
#Overly simplified
plot(1, 1, main = base)
}
When I run this and investigate methods(plot), there is no method defined for ABI objects. Accessing the function by ABI:::plot (ABI is the name of the package) does work. Using :: does not.
During the package build check, there is a warning:
* checking S3 generic/method consistency ... WARNING
plot:
function(x)
plot.ABI:
function(x, base, ...)
See section ‘Generic functions and methods’ of the ‘Writing R
Extensions’ manual.
It seems that there is a disagreement in arguments. But I don't understand this, since the generic has arguments x and ... and so does my ABI method (in addition to base).
So there are two problems, which I hope stem from the same issue: The plot.ABI method is not exported and the package check throws a warning.
How do I solve this?
The problem is that your method needs to have the same arguments as the generic. Let's assume you are using the generic supplied with R:
> args(plot)
function (x, y, ...)
NULL
Now plot() is actually a special case as you can essentially ignore that there is an argument y there.
So your method needs to be as you have it:
plot.ABI <- function(x, base, ...)
The issue is that, contrary to your quoted script, you must have redefined the generic as
plot(x)
and as that is missing ..., R CMD check will rightly complain.
So, don't document or provide the generic if it already exists.
A few problems:
Don't include a generic that is already defined elsewhere. Just add your method.
The signatures of every method must at least include every element in the generic, in the same order. It's annoying sometimes, but it's incontrovertible.
Update
I had said, "Your #export needs to list the function name," but apparently this is not correct. See the comments. Note also that listing the method should export it. I seem to recall needing an explicit export in cases where you aren't including the generic in your package's namespace, but I could be wrong (so often am!).

Resources