How to Import Only One Function from Another Package, Without Loading the Entire Namespace

How to import only one function from another package, without loading the entire namespace

You can't.

If you declare memisc in the Imports: field, the namespace will be loaded when the package is loaded and the exported objects will be findable by your package. (If you specify it in Depends:, the namespace will be loaded and attached to the search path which makes the exported objects findable by any code.)

Part of loading a namespace is registering methods with the generic. (I looked but couldn't find a canonical documentation that says this; I will appeal to the fact that functions are declared as S3 methods in the NAMESPACE file as evidence.) The defined methods are kept with the generic and have the visibility of the generic function (or, perhaps, the generic function's namespace).

Typically, a package will define a method either for a generic it creates or for a class it defines. The S3 object system does not have a mechanism for formally defining an S3 class (or what package created the class), but the general idea is that if the package defines functions which return an object with that class attribute (and is the only package that does), that class is that package's class. If either of these two conditions hold, there will not be a problem. If the generic is defined in the package, it can only be found if the package is attached; if the class is defined in the package, objects of that class would only exist (and therefore be dispatched on) if the package is attached and used.

In the memisc example, neither holds. The aggregate generic is defined in the stats package and the formula object is also defined in the stats package (based on that package defining as.formula, [.formula, etc.) Since it is neither memisc's generic nor memisc's object, the effects can be seen even (and the method dispatched to) if memisc is simply loaded but not attached.

For another example of this problem, but with reorder.factor, see Reordering factor gives different results, depending on which packages are loaded.

In general, it is not good practice to add methods to generics for which the package does not control either the object or the generic; doubly so if it overrides a method in a core package; and egregiously so if it is not a backwards compatible function to the existing function in the core packages.

For your example, you may be better off copying the code for memisc::describe into your package, although that approach has its own problems and caveats.

import all the functions of a package except one when building a package

The NAMESPACE file is somewhat flexible here, as described in Writing R Extensions.

The two main import directives are:

import(PACKAGE)

which imports all objects in the namespace into your package. The second option is to do specific imports using:

importFrom(PACKAGE, foo)

which gives you access to foo() without needing the fully qualified reference PACKAGE::foo().

But these aren't the only two options. You can also use the except argument to exclude just a handful of imports:

import(PACKAGE, except=c(foo,bar))

which gives you everything from PACKAGE's namespace but foo() and bar(). This is useful - as in your case - for avoiding conflicts.

For roxygen, great catch on figuring out that you can do:

#' @rawNamespace import(PACKAGE, except = foo)

to pass a raw NAMESPACE directive through roxygen.

Load a package apart from one function

There are three nice ways to do that, rising in difficulty:

1. conflicted

It checks for name conflicts and will prevent you from using masked or masking functions by throwing an error if you do. But you can declare a session-wide preference, e.g.:

conflict_prefer("filter", "dplyr")
#> [conflicted] Will prefer dplyr::filter over any other package

conflicted on Github

2. import

It allows you to explicitly import specific functions from packages (and give them a custom name, if you like)

import::from(ggplot2, g = ggplot, aes, geom_point)
g(iris, aes(Petal.Width, Petal.Length)) + geom_point()

import on Github

3. modules

It gives you a Python-esque way of importing both modules (written as R source files) and libraries and a more cohesive fashion. The nice (but advanced) thing is that modules, if they have subgroups, can be loaded partially, e.g. str = import('tools/strings'). This does not work for packages, however, as they are written as monoliths. modules has some advantages coding guideline wise, but will force you to write:

dplyr = import_package('dplyr')
cars %>% dplyr$filter(speed > 15)

modules on Github

Import one function in R package (without importFrom)

Questions about the appropriate attribution would probably be best resolved by contacting the package author directly. As noted in the comments above, that package appears to use GPL-3, which should mean that you can include the function in your package but your package must then also be GPL-3 licensed. (As always, probably no one here is a lawyer so that's on you to check...)

The primary downside to copying just the function you need is that then you are responsible for maintaining it. This probably also means maintaining it in a way that keeps it in sync with the original version from plotKML. Depending on the package, surrounding code and how often it is updated that could be fairly simple or it could be horrible.

R with roxygen2: How to use a single function from another package?

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.

How to properly use functions from other packages in a R package

The basic question you need to answer is: "do you want the function to be available to all users of the package without further effort?". If yes, then use imports + the appropriate namespace declarations, if no, then use suggests and print an informative error message if require("psych") returns FALSE.

I don't understand your import related complaint that: "but on a computer that does not has psych installed it gives an error when loading my package". This is also true if your package is in depends!

How to load only specific functions from a package

Sure. Just use an importFrom directive in your NAMESPACE file (as described here in R-exts).

importFrom(gdata, trim)

OP EDIT: As of R 3.2.0 there's now a base function: trimws()



Related Topics



Leave a reply



Submit