Using 'as'/coerce method from another package in my package - r

I'm writing an R package, and want to internally use an 'as' function from another package to coerce an an object. But I can't figure out how to import it in the package (with either importFrom or ::) like you would an normally exported function.
How can I import a particular flavour of the 'as' method into my package from another package? Or all 'as' methods from another package?
Specifically: The 'contrast_each_group_to_the_rest' function in my package (celaref) that uses that will only work if you call library(MAST) alongside library(celaref).
The 'as' method I want it to use shows up in showMethods("coerce") as from="SingleCellExperiment", to="SingleCellAssay", when I import the 'MAST' package.
E.g. using demo data
library(celaref) # my package, uses MAST internally
library(SingleCellExperiment) # a dependancy
# library(MAST) # Uncomment this and it works
contrast_each_group_to_the_rest(demo_ref_se, num_cores=1)
Yeilds error:
Error in as(sca, "SingleCellAssay") :
no method or default for coercing “SingleCellExperiment” to “SingleCellAssay”
Thanks.

As suggested by Neal Fultz:
You might try adding importFrom("methods", as) - github.com/cran/Matrix
/blob/master/NAMESPACE#L24 this makes me think that MAST may need to add that
to their NAMESPACE.
This works:
#' #importFrom "methods" as in function header
or
importFrom("methods",as) in NAMESPACE
CORRECTION - actually, this doesn't seem to work as I thought it did. Any further tips appreciated.
Fix - importing MAST in the function header (oops) and using a weird looking 'as' format that I don't quite understand. (Probably dependent on the actual (very similar) data objects in these packages)
#' #import SummarizedExperiment
#' #import MAST
sca <- new("SingleCellAssay")
as(sca, "SingleCellExperiment") <- sce.in

Related

R: How to check if the libraries that I am loading, I use them for my code in R?

I have used several packages of R libraries for my study. All libraries charge together at the beginning of my code. And here is the problem. It turns out that I have done several tests with different functions that were already in the packages of R. However, in the final code I have not implemented all the functions I have tried. Therefore, I am loading libraries that I do not use.
Would there be any way to check the libraries to know if they really are necessary for my code?
Start by restarting R with a fresh environment, no libraries loaded. For this demonstration, I'm going to define two functions:
zoo1 <- function() na.locf(1:10)
zoo2 <- function() zoo::na.locf(1:10)
With no libraries loaded, let's try something:
codetools::checkUsage(zoo1)
# <anonymous>: no visible global function definition for 'na.locf'
codetools::checkUsage(zoo2)
library(zoo)
# Attaching package: 'zoo'
# The following objects are masked from 'package:base':
# as.Date, as.Date.numeric
codetools::checkUsage(zoo1)
Okay, so we know we can check a single function to see if it is abusing scope and/or using non-base functions. Let's assume that you've loaded your script full of functions (but not the calls to require or library), so let's do this process for all of them. Let's first unload zoo, so that we'll see a complaint again about our zoo1 function:
detach("package:zoo", unload=TRUE)
Now let's iterate over all functions:
allfuncs <- Filter(function(a) is.function(get(a)), ls())
str(sapply(allfuncs, function(fn) capture.output(codetools::checkUsage(get(fn))), simplify=FALSE))
# List of 2
# $ zoo1: chr "<anonymous>: no visible global function definition for 'na.locf'"
# $ zoo2: chr(0)
Now you know to look in the function named zoo1 for a call to na.locf. It'll be up to you to find in which not-yet-loaded package this function resides, but that might be more more reasonable, depending on the number of packages you are loading.
Some side-thoughts:
If you have a script file that does not have everything comfortably ensconced in functions, then just wrap all of the global R code into a single function, say bigfunctionfortest <- function() { as the first line and } as the last. Then source the file and run codetools::checkUsage(bigfunctionfortest).
Package developers have to go through a process that uses this, so that the Imports: and Depends: sections of NAMESPACE (another ref: http://r-pkgs.had.co.nz/namespace.html) will be correct. One good trick to do that will prevent "namespace pollution" is loading the namespace but not the package ... and though that may sound confusing, it often results in using zoo::na.locf for all non-base functions. This gets old quickly (especially if you are using dplyr and such, where most of your daily functions are non-base), suggesting those oft-used functions should be directly imported instead of just referenced wholly. If you're familiar with python, then:
# R
library(zoo)
na.locf(c(1,2,NA,3))
is analagous to
# fake-python
from zoo import *
na_locf([1,2,None,3])
(if that package/function exists). Then the non-polluting variant looks like:
# R
zoo::na.locf(c(1,2,NA,3))
# fake-python
import zoo
zoo.na_locf([1,2,None,3])
where the function's package (and/or subdir packaging) must be used explicitly. There is no ambiguity. It is explicit. This is by some/many considered "A Good Thing (tm)".
(Language-philes will likely say that library(zoo) and from zoo import * are not exactly the same ... a better way to describe what is happening is that they bring everything from zoo into the search path of functions, potentially causing masking as we saw in a console message earlier; while the :: functionality only loads the namespace but does not add it to the search path. Lots of things going on in the background.)

How do I load data from another package from within my package

One of the functions in a package that I am developing uses a data set from the acs:: package (the fips.state object). I can load this data into my working environment via
data(fips.state, package = "acs"),
but I do not know the proper way to load this data for my function. I have tried
#importFrom acs fips.state,
but data sets are not exported. I do not want to copy the data and save it to my package because this seems like a poor development practice.
I have looked in http://r-pkgs.had.co.nz/namespace.html, http://kbroman.org/pkg_primer/pages/docs.html, and https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Data-in-packages, but they do not include any information on sharing data sets from one package to another.
Basically, how do I make a data set, that is required by functions in another package available to the functions in my package?
If you don't have control over the acs package, then acs::fips.state seems to be your best bet, as suggested by #paleolimbot.
If you are going to make frequent calls to fips.state, then I'd suggest making a local copy via fips.state <- acs::fips.state, as there is a small cost in looking up objects from other packages that you might do well to avoid incurring multiple times.
But if you are able to influence the acs package (even if you are not, I think this is a useful generalization), then mikefc suggests an alternative solution, which is to set the fips.state object up as internal to the package, and then to export it:
usethis::use_data(fips.state, other.data, internal = FALSE)
And then in NAMESPACE:
export(fips.state)
or if using roxygen2:
#' Fips state
#' #name fips.state
#' #export
"fips.state"
Then in your own package, you can simply #importFrom acs fips.state.
You can always use package::object_name (e.g., dplyr::starwars) anywhere in your package code, without using an import statement.
is_starwars_character <- function(character) {
character %in% dplyr::starwars$name
}
is_starwars_character("Luke Skywalker")
#> [1] TRUE
is_starwars_character("Indiana Jones")
#> [1] FALSE

Function imported from dependency not found, requires library(dependency)

I am trying to create an R package that uses functions from another package (gamlss.tr).
The function I need from the dependency is gamlss.dist::TF (gamlss.dist is loaded alongside gamlss.tr), but it is referenced in my code as simply TF within a call to gamlss.tr::gen.trun.
When I load gamlss.tr manually with library(), this works. However, when I rely on the functions of the dependency automatically being imported by my package through #import, I get an "object not found" error as soon as TF is accessed.
My attempt to be more explicit and reference the function I need as gamlss.dist::TF resulted in a different error ("unexpected '::'").
Any tips on how to use this function in my package would be much appreciated!
The code below reproduces the problem if incorporated into a clean R package (as done in this .zip), built and loaded with document("/path/to/package"):
#' #import gamlss gamlss.tr gamlss.dist
NULL
#' Use GAMLSS
#'
#' Generate a truncated distribution and use it.
#' #export
use_gamlss <- function() {
print("gen.trun():")
gamlss.tr::gen.trun(par=0,family=TF)
#Error in inherits(object, "gamlss.family") : object 'TF' not found
#gamlss.tr::gen.trun(par=0,family=gamlss.dist::TF)
#Error in parse(text = fname) : <text>:1:1: unexpected '::'
y = rTFtr(1000,mu=10,sigma=5, nu=5)
print("trun():")
truncated_dist = gamlss.tr::trun(par=0,family=TF, local=TRUE)
model = gamlss(y~1, family=truncated_dist)
print(model)
}
use_gamlss() will only start working once a user calls library(gamlss.tr).
This is due to bad design of gamlss.tr in particular the trun.x functions (they take character vectors instead of family objects / they evaluate everything in the function environment instead of the calling environment).
To work around this, you have to make sure that gamlss.distr is in the search path of the execution environment of gamlss.tr functions (This is why ## import-ing it in your package does not help: it would need to be #' #import-ed in gamlss.tr).
This can be achieved by adding it to Depends: of your package.
If you want to avoid that attaching your package also attaches gamlss.distr, you could also add the following at the top of use_gamlss:
nsname <-"gamlss.dist"
attname <- paste0("package:", nsname)
if (!(attname %in% search())) {
attachNamespace(nsname)
on.exit(detach(attname, character.only = TRUE))
}
This would temporarily attach gamlss.dist if it is not attached already.
You can read more on namespaces in R in Hadley Wickham's "Advanced R"

How to extend S3 method from another package without loading the package

I am developing a package which has the function forecast.myclass. I want that function to work nicely with forecast package. I.e. when forecast package is loaded the code forecast(object) should call forecast.myclass from my package.
Since I need only generic definition of forecast from the package forecast, and I do not use any other function from the package forecast I am reluctant to include it in the Depends. So I define the generic in my package in the following way:
##'
##' #export
forecast <- function(object,...) UseMethod("forecast")
##' #rdname forecast.midas_r
##' #method forecast midas_r
##' #export
forecast.midas_r <- function(object,newdata=NULL,method=c("static","dynamic"),insample=get_estimation_sample(object),...) {
Now everything works as expected when package forecast is not loaded. But when I load package forecast, then forecast.midas_r is not called, when doing forecast(object) where object is of class midas_r. How should I solve this problem?
I'm not sure there's an easy solution to this. As others have said it's probably easiest to use Depends to get around this, rather than redefining a generic method.
Here's a simple example which works for me. It's largely the same as your solution, but declaring #export means you won't need to manually update the NAMESPACE file.
##' #name mean
##' #export mean.newClass
##'
##' #method mean newClass
##'
##' #title mean for \code{newClass} object
##' #param x A \code{newClass} object
##' #param ... Additional arguments
##'
mean.newClass <- function(x, ...){
stopifnot(class(x)=="newClass")
return(42)
}
Then package.skeleton("newPkg"). Put file mean.R with the above contents in the directory /R of the package.
Ensure you're in the directory 1 level below, then
roxygenize("newPkg", roxygen.dir="newPkg", copy.package=F, unlink.target=F)
Now
library(devtools)
dev_mode(on=TRUE) ### don't want to have to uninstall the package later
install_local("newPkg")
library(newPkg)
x <- c(1,2)
class(x) <- "newClass"
stopifnot(mean(x)==42)
stopifnot(mean(unclass(x))==1.5)
I realize mean is a function in base but I have tested this for modifying generic functions elsewhere to give them a new method, so it should extend to your more general case also.
The problem here is that your definition of the forecast generic is masking the definition from the forecast package, and your method is associated with your generic rather than the forecast package generic; this is just a complicated instance of two packages defining functions of the same name. The solution is to bite the bullet and Depend: on forecast, or when at the command line and both your package and forecast are attached fully resolve the function mypackage::forecast(), or Import: forecast but not make the forecast generic available to the end user except by having them require(forecast) (this might be appropriate if forecast functionality were somehow peripheral to your package, e.g., plotting in 3D when plotting in 2D was sufficient).
For what it's worth, an S4 method in PkgB defined and exported on an imported S4 generic from PkgA implicitly exposes the S4 generic to the user, so the generic is available even if Imports: PkgA is specified in the DESCRIPTION file of PkgB.
One possible solution is to forcefully export forecast.midas_r. This means manually updating NAMESPACE with export(forecast.midas_r) everytime after check("yourpackagename").
What this does is make forecast.midas_r visible for package forecast. If forecast.midas_r is not exported, and exists only in the namespace of the package, then when package forecast is loaded, the generic forecast is overwritten with the identical function from package forecast. So when forecast is invoked on unknown object, R looks up for corresponding methods in package forecast and in general workspace. Since forecast.midas_r is a private method R does not find it and produces an error.
This is not a perfect solution, since you need to manually update NAMESPACE, but it is a solution nevertheless.
This github issue was very helpful for me.
Following the patterns of {sf}:
Don't export your functions in your namespace
Copy the register_s3_method() function definition in a new R script or the same that has your methods
copy the scaffolding for registering all methods and repeat the body for all of the methods you may have in your package
register_all_s3_methods = function() {
register_s3_method("pkg", "function", "class")
}
In a new R script (preferably zzz.R), have an .onLoad() hook containing
.onLoad = function(libname, pkgname) {
register_all_s3_methods()
}
Redocument your package
devtools::load_all()

Importing S3 method from another package

I'm trying to import a S3 method, predict from another package pls. I have a function which uses these predicted values. The problem is, when compiling the package:
Error : object 'predict' is not exported by 'namespace:pls'
I've put together this Gist as a minimal example which highlights my problem and contains the following R file:
#' Test function
#'
#' #importFrom pls predict
#'
#' #export
myfunc <- function(x){
stopifnot(class(x) == "mvr")
predict(x)*2
}
To summarise this as the original (below) is now out-dated and in places erroneous or misleading.
The proximal issue is that there is no function named predict in the pls package; there are some unexported S3 methods for predict but no such predict. So you can't import this. The predict generic lives in the stats package and you'll need to import from there as discussed below.
Your package needs to have Depends: pls in the DESCRIPTION in order for the correct predict method to be available to R. There's nothing in pls that you can specifically import.
You also need to import the predict generic from the stats namespace, so add
#' #importFrom stats predict
as that will import the generic in you packages namespace. You will also want to add Imports: stats to your DESCRIPTION file to indicate that you need the stats package; previously we didn't have to declare dependencies on the set of base packages shipped with R (i.e. the non-Recommended ones that ship with R).
Original
The main issue here is the pls doesn't define a function/method predict. It provides several methods for the predict generic, but not the generic itself.
You need to import the generic from the stats package, if you need it - I'm not sure you do as you aren't creating a function that needs or builds on the generic. At the minimum you'll need
#' #importFrom stats predict
though you may need/want to import the entire stats namespace - depends what your package does beyond the function your are currently working on.
The other issue is that predict.mvr is not exported from the pls namespace
> require(pls)
Loading required package: pls
Attaching package: ‘pls’
The following object(s) are masked from ‘package:stats’:
loadings
> predict.mvr
Error: object 'predict.mvr' not found
> pls::predict.mvr
Error: 'predict.mvr' is not an exported object from 'namespace:pls'
> pls:::predict.mvr
function (object, newdata, ncomp = 1:object$ncomp, comps, type = c("response",
"scores"), na.action = na.pass, ...)
As such you can't just import it. Hence your package needs to have Depends: pls in the DESCRIPTION in order for the correct predict method to be found.

Resources