I am developing a package, which depends on zoo (listed in Imports, not Depends in DESCRIPTION).
In a function, I need to subset two-dimensional zoo object by [ operator. However, as long as zoo package is not loaded, R uses base [, which returns numeric instead of zoo.
A standard solution would be to use a namespace of zoo (like zoo::`[.zoo`()). However, when I try to execute this in the function R throws an error Error: '[.zoo' is not an exported object from 'namespace:zoo', so I conclude that this operator is not exported in zoo namespace (even though I could see it in https://github.com/rforge/zoo/blob/master/pkg/zoo/NAMESPACE).
Solution 1: It is possible to use ::: operator to use a non-exported function from a package. Probably it is not a best practice.
Solution 2: I can create a new zoo using coredata and index, which are explicitly exported, i.e.:
zoo_new <- zoo::zoo(x = zoo::coredata(zoo_old), order.by = zoo::index(zoo_old))
which is not very elegant.
Solution 3: Move zoo from Imports to Depends in DESCRIPTION. Also it is not the best practice.
I think you are right, using ::: is not best practice and not allowed for CRAN packages. According to this post http://kbroman.org/pkg_primer/pages/depends.html you should either contact the authors of zoo or use the code of their function directly inside your package.
Related
I have a problem with package stars when trying to use split function to split a stars raster object. I use the stars version 0.5-4 (that one is automatically installed when asking for installing stars). According to the package documentation, there should be a function split (in fact, it's probably a method for the generic R function split) for splitting stars object. However, when I try to use it, my R says there is no such function in the stars namespace. Is the function really missing and the documentation wrong? Or could that be caused by some other problem with namespaces?
> library(stars)
> split.stars
Error: object 'split.stars' not found
> stars::split
Error: 'split' is not an exported object from 'namespace:stars'
But when I type
> ?stars
I get the help for the split function from stars. Can someone explain that?
Thanks in advance!
Edit: It works now, not sure what the problem was.
split is a base R function. It’s an S3 generic, and the ‘stars’ package provides its own method implementation for a custom S3 class.
To use the method, use it as any other, regular function from base R. That is, write split. Don’t write split.stars, nor stars::split.
We are creating a package where one of our functions uses functions of the data.table package.
Instead of importing entire packages through our roxygen header, we try to use :: as much as possible in our code.
For a function, this is easy. For example:
data.table::setkey(our_data_1, our_variable)
Yet, we do not know how to do this for a method. For example:
our_data_3 <- our_data_1[our_data_2, roll = "nearest"]
where [ has a specific method for data.tables, which is indicated by:
methods(`[`)
I have tried multiple approaches. Multiple combinations, using #importFrom, failed. For example, adding the following line to our roxygen header...
#importFrom data.table `[.data.table`
...returned the following when running devtools::document():
Warning message:
object ‘[.data.table’ is not exported by 'namespace:data.table'
I have also tried things like [.data.table within our code, but those failed as well...
Importing the entire data.table package in our roxygen header worked (#import data.table), but this is not preferred since we want to refer to the package of each function within our code (or at least use #importFrom).
Is there a way to use the [ method of data.table within the code of a function without importing the entire data.table package? Or is it at least possible to only import the method, for example through using #importFrom in our roxygen header?
Thank you in advance!
There is no need to import S3 methods, they are automatically dispatched by class of an object.
In case of [ data.table method, there is a trick which we use to ensure that data.table passed to a library that expects data.frame, will be handled properly, as a data.frame. This handling is decided based on NAMESPACE file. If you don't import data.table in NAMESPACE then data.table method assumes you want to use it as data.frame.
You can state your intent explicitly by using extra variable .datatable.aware=TRUE in any of you R script files.
You should read Importing data.table vignette where this is well described.
I also put example package which you can run and debug from there if for some reason your code will still not work: https://gitlab.com/jangorecki/useDTmethod
I think you don't need to import S3 method or to use :: like we do on functions.
In my opinion you just need to add data.table as a dependency in DESCRIPTION and it should be working.
R will know that you are applying [ to a data.table object and will use the correct method.
Writing an R-package I use name spaces to use functions from existing packages, e.g. raster::writeRaster(...).
However, I am wondering if functions from the base package have also be used like this, e.g. base::sum(...). This might end up in very confusing code parts:
foo[base::which(base::sapply(bar, function())]
No you don't need to reference base packages like this. You only need to reference non-base packages to ensure they are loaded into the function environment when functions from your package are run, either by using :: or #import in the Roxegen notes at the top of your script. See why you don't need to reference base packages below:
http://adv-r.had.co.nz/Environments.html
"Package namespaces keep packages independent. For example, if package A uses the base mean() function, what happens if package B creates its own mean() function? Namespaces ensure that package A continues to use the base mean() function, and that package A is not affected by package B (unless explicitly asked for)."(Hadley Wickham)
The only time you need to reference base:: is if the namespace for your package contains a package that has an alternative function of the same name.
I'm creating an R package that will use a single function from plyr. According to this roxygen2 vignette:
If you are using just a few functions from another package, the
recommended option is to note the package name in the Imports: field
of the DESCRIPTION file and call the function(s) explicitly using ::,
e.g., pkg::fun().
That sounds good. I'm using plyr::ldply() - the full call with :: - so I list plyr in Imports: in my DESCRIPTION file. However, when I use devtools::check() I get this:
* checking dependencies in R code ... NOTE
All declared Imports should be used:
‘plyr’
All declared Imports should be used.
Why do I get this note?
I am able to avoid the note by adding #importFrom dplyr ldply in the file that is using plyr, but then I end but having ldply in my package namespace. Which I do not want, and should not need as I am using plyr::ldply() the single time I use the function.
Any pointers would be appreciated!
(This question might be relevant.)
If ldply() is important for your package's functionality, then you do want it in your package namespace. That is the point of namespace imports. Functions that you need, should be in the package namespace because this is where R will look first for the definition of functions, before then traversing the base namespace and the attached packages. It means that no matter what other packages are loaded or unloaded, attached or unattached, your package will always have access to that function. In such cases, use:
#importFrom plyr ldply
And you can just refer to ldply() without the plyr:: prefix just as if it were another function in your package.
If ldply() is not so important - perhaps it is called only once in a not commonly used function - then, Writing R Extensions 1.5.1 gives the following advice:
If a package only needs a few objects from another package it can use a fully qualified variable reference in the code instead of a formal import. A fully qualified reference to the function f in package foo is of the form foo::f. This is slightly less efficient than a formal import and also loses the advantage of recording all dependencies in the NAMESPACE file (but they still need to be recorded in the DESCRIPTION file). Evaluating foo::f will cause package foo to be loaded, but not attached, if it was not loaded already—this can be an advantage in delaying the loading of a rarely used package.
(I think this advice is actually a little outdated because it is implying more separation between DESCRIPTION and NAMESPACE than currently exists.) It implies you should use #import plyr and refer to the function as plyr::ldply(). But in reality, it's actually suggesting something like putting plyr in the Suggests field of DESCRIPTION, which isn't exactly accommodated by roxygen2 markup nor exactly compliant with R CMD check.
In sum, the official line is that Hadley's advice (which you are quoting) is only preferred for rarely used functions from rarely used packages (and/or packages that take a considerable amount of time to load). Otherwise, just do #importFrom like WRE advises:
Using importFrom selectively rather than import is good practice and recommended notably when importing from packages with more than a dozen exports.
I'm working on an R package at present and trying to follow the best practice guidelines provided by Hadley Wickham at http://r-pkgs.had.co.nz. As part of this, I'm aiming to have all of the package dependencies within the Imports section of the DESCRIPTION file rather than the Depends since I agree with the philosophy of not unnecessarily altering the global environment (something that many CRAN and Bioconductor packages don't seem to follow).
I want to use functions within the Bioconductor package rhdf5 within one of my package functions, in particular h5write(). The issue I've now run into is that it doesn't have its S3 methods declared as such in its NAMESPACE. They are declared using (e.g.)
export(h5write.default)
export(h5writeDataset.matrix)
rather than
S3method(h5write, default)
S3method(h5writeDataset, matrix)
The generic h5write is defined as:
h5write <- function(obj, file, name, ...) {
res <- UseMethod("h5write")
invisible(res)
}
In practice, this means that calls to rhdf5::h5write fail because there is no appropriate h5write method registered.
As far as I can see, there are three solutions to this:
Use Depends rather than Imports in the DESCRIPTION file.
Use library("rhdf5") or require("rhdf5") in the code for the relevant function.
Amend the NAMESPACE file for rhdf5 to use S3methods() rather than export().
All of these have disadvantages. Option 1 means that the package is loaded and attached to the global environment even if the relevant function in my package is never called. Option 2 means use of library in a package, which while again attaches the package to the global environment, and is also deprecated per Hadley Wickham's guidelines. Option 3 would mean relying on the other package author to update their package on Bioconductor and also means that the S3 methods are no longer exported which could in turn break other packages which rely on calling them explicitly.
Have I missed another alternative? I've looked elsewhere on StackOverflow and found the following somewhat relevant questions Importing S3 method from another package and
How to export S3 method so it is available in namespace? but nothing that directly addresses my issue. Of note, the key difference from the first of these two is that the generic and the method are both in the same package, but the issue is the use of export rather than S3method.
Sample code to reproduce the error (without needing to create a package):
loadNamespace("rhdf5")
rdhf5::h5write(1:4, "test.h5", "test")
Error in UseMethod("h5write") :
no applicable method for 'h5write' applied to an object of class
"c('integer', 'numeric')
Alternatively, there is a skeleton package at https://github.com/NikNakk/s3issuedemo which provides a single function demonstrateIssue() which reproduces the error message. It can be installed using devtools::install_github("NikNakk/s3issuedemo").
The key here is to import the specific methods in addition to the generic you want to use. Here is how you can get it to work for the default method.
Note: this assumes that the test.h5 file already exists.
#' #importFrom rhdf5 h5write.default
#' #importFrom rhdf5 h5write
#' #export
myFun <- function(){
h5write(1:4, "test.h5", "test")
}
I also have put up my own small package demonstrating this here.