"import as" in R
Asked Answered
O

7

41

Is there a way to import a package with another name in R, the way you might with import as in Python, e.g. import numpy as np? I've been starting to use package::function lately to avoid conflicts between, say, Hmisc::summarize and plyr::summarize.

I'd like to be able to instead write h::summarize and p::summarize, respectively. Is this possible in R?

Omidyar answered 24/6, 2014 at 15:58 Comment(12)
Is that for use in a package?Carlile
Nope, I just don't want carpal tunnel (expression() is my new favorite function) and I also don't like unpredictable errors. Although eventually I do plan to work on packages so if there are separate considerations in that case I'd like to hear about them.Omidyar
Are you really running into conflicts that often that something like this would be necessary? Or is it just a few of your favorite functions in a couple of packages?Bahadur
It's just an issue I've run into more than once. I also tend to write lots of masking and wrapper functions for personal use that sometimes result in unforseen conflicts down the road.Omidyar
These are really the most predictable errors you could come across. For summarize, you could use summarise in plyr and summarize in Hmisc. Saving yourself three or four characters to avoid explicitly Hmisc::'ing is silly. A better way would be to use expr<TAB> to fill in the rest which also will show you the package in which they reside. I will enjoy my carpal tunnel and write package::function a couple times.Pozzy
To the best of my knowledge, there is no way of renaming packages when loading them in R.Carlile
@Carlile please put that as an answer so I can mark it then.Omidyar
I’m working on getting modules to work with conventional packages. Once that’s completed, it will solve this problem in a non-hacky way (e.g. it will work with documentation, unlike Thomas’ solution posted further down).Bonefish
@KonradRudolph I'm so glad that's a thing! Is it just environments underneath? How will it interact with "real" packages?Omidyar
@ssdecontrol At the end everything is just an environment underneath in R. However, package environments have lots of metadata attached, which needs to happen here as well. In that regard, this method is equivalent to normal package loading. Where it differs is that normal package loading attaches them – this doesn’t happen here.Bonefish
@wordsforthewise not really. In fact I now believe this should be possible with the info here.Omidyar
@ssdecontrol The 4 lines of code to do the same thing as import numpy as np in Python look pretty confusing at first glance.Samuele
C
14

Use the namespace package to generate another namespace aliased to the one you are interested in.

library(namespace)
registerNamespace('ggp', loadNamespace('ggplot2'))
data(iris)
ggp::ggplot(iris, ggp::aes(x = Petal.Length, y = Sepal.Length)) + ggp::geom_point()

Note this has the disadvantage of making the package versioning/installation requirements more opaque for scripts.

Clareclarence answered 18/10, 2018 at 21:14 Comment(2)
Nice! import_as <- function(pkg, alias) invisible(namespace::registerNamespace(alias, loadNamespace(pkg)))Omidyar
This solution does not work any more since the ‘namespace’ package has been archived from CRAN. It could be made to work again (the functionality used here is trivial to replicate) but it unfortunately has global side-effects (since it modifies the R namespace registry) so it isn’t a terribly great solution (otherwise ‘box’ would probably use it to register loaded modules).Bonefish
P
27

This is not quite what you want because it involves changing from :: notation to $ notation, but if you load a package namespace (without attaching it), you can then refer to it by its environment name:

h <- loadNamespace('Hmisc')
p <- loadNamespace('plyr')

> summarize(iris$Sepal.Length, iris$Species, FUN=mean)
Error: could not find function "summarize"

> Hmisc::summarize(iris$Sepal.Length, iris$Species, FUN=mean)
  iris$Species iris$Sepal.Length
1       setosa             5.006
2   versicolor             5.936
3    virginica             6.588

> h$summarize(iris$Sepal.Length, iris$Species, FUN=mean)
  iris$Species iris$Sepal.Length
1       setosa             5.006
2   versicolor             5.936
3    virginica             6.588

> summarise(iris, x = mean(Sepal.Length))
Error: could not find function "summarise"

> plyr::summarise(iris, x = mean(Sepal.Length))
         x
1 5.843333

> p$summarise(iris, x = mean(Sepal.Length))
         x
1 5.843333

Note, however, that you do lose access to documentation files using the standard ? notation (e.g., ? p$summarise does not work). So, it will serve you well as shorthand, but may not be great for interactive use since you'll still have to resort to ? plyr::summarise for that.

Note also that you do not have access to the data objects stored in the package using this approach.

Perigordian answered 24/6, 2014 at 17:33 Comment(2)
This does not seem to make package data available through the $ operator.Appressed
@Appressed If you’re looking for a unified approach that also works for package data, the dev version of ‘box’ now supports this.Bonefish
G
19

Here's a solution that should only be used for interactive mode. You modify :: so that it can accept character package names, then write a function to register the aliases.

`::` <- function(pkg, name) {
    sym <- as.character(substitute(pkg))
    pkg <- tryCatch(get(sym, envir=.GlobalEnv), error=function(e) sym)
    name <- as.character(substitute(name))
    getExportedValue(pkg, name)
}

pkg.alias <- function(alias, package) {
    assign(alias, package, .GlobalEnv)
    lockBinding(alias, .GlobalEnv)
}

pkg.alias('r', 'reshape2')
r::dcast

But instead of using aliases, you could also redefine :: to find the package that matches your abbreviation:

`::` <- function(pkg, name)  {
    pkg <- as.character(substitute(pkg))
    pkg <- installed.packages()[grepl(paste0('^', pkg), installed.packages())]
    name <- as.character(substitute(name))
    getExportedValue(pkg, name)
}

ggp::ggplot
Gerrald answered 24/6, 2014 at 18:5 Comment(4)
This is what I was looking for. Also a good example of what substitute() actually does.Omidyar
Trying to magically figure out whether an argument should be evaluated or not is a baaaaaaad idea.Valaree
@Valaree I've updated my answer so that it can't be used inside functions.Gerrald
For use from the command line, I would expect the package content names to show up once I enter ::. Unfortunately they don't show up. It works with the accepted answer.Appressed
W
14

Rather than aliasing the package, why not just alias the function?

hsumm <- Hmisc::summarize
dsumm <- dplyr::summarize
psumm <- plyr::summarize

I was starting down an eval(parse()) path, but I ran into trouble and need to get back to work. @Thomas's answer seems to get a similar result in a much smoother way, but here's the non-working draft.

package_alias <- function(package, alias, infix = "..") {
    funs <- ls(paste0("package:", package))
    for (i in seq_along(funs)) {
        assign(paste0(alias, infix, funs[i]),
        value = eval(parse(text = funs[i])), envir = .GlobalEnv)
    }
}

With the idea that you could do something like

package_alias("plyr", "p")

to create p..ddply, etc.

Wonacott answered 24/6, 2014 at 17:9 Comment(3)
Because I don't want to. See my comment to the other answer; it still fails to be a robust general solution. That said, I'm not looking for a workaround.Omidyar
If it's the punctuation in your function call that you miss, you could go with h..suummarize <- Hmisc::summarize ;)Wonacott
I just want to say that I came back to this question and I like this answer a lot more than when I first saw it. +1 for practicality.Omidyar
C
14

Use the namespace package to generate another namespace aliased to the one you are interested in.

library(namespace)
registerNamespace('ggp', loadNamespace('ggplot2'))
data(iris)
ggp::ggplot(iris, ggp::aes(x = Petal.Length, y = Sepal.Length)) + ggp::geom_point()

Note this has the disadvantage of making the package versioning/installation requirements more opaque for scripts.

Clareclarence answered 18/10, 2018 at 21:14 Comment(2)
Nice! import_as <- function(pkg, alias) invisible(namespace::registerNamespace(alias, loadNamespace(pkg)))Omidyar
This solution does not work any more since the ‘namespace’ package has been archived from CRAN. It could be made to work again (the functionality used here is trivial to replicate) but it unfortunately has global side-effects (since it modifies the R namespace registry) so it isn’t a terribly great solution (otherwise ‘box’ would probably use it to register loaded modules).Bonefish
B
5

I'd like to be able to instead write h::summarize and p::summarize, respectively. Is this possible in R?

You could use box:

box::use(
  h = Hmisc,
  p = plyr,
)

h$summarize(…)
p$summarize(…)

But box::use provides a lot more flexibility than just that. It’s your one-stop shop for all your code reuse needs. For example, you could also decide to import all names from one package and rename some if its symbols; for instance:

box::use(
  h = Hmisc,
  plyr[..., p_summarize = summarize],
)

This allows you to use ‘Hmisc’ names by prefixing them with h$ as before, and all names from ‘plyr’ without prefixing them with anything (they’re attached); except for plyr::summarize, which has been attached under the alias p_summarize.

Note, furthermore, that unlike with library attaching happens locally. For example, you can use the above box::use declaration inside a function. ‘plyr’ is then “attached” only inside the function. Outside the function, code is unaffected.

Bonefish answered 21/5, 2021 at 14:21 Comment(0)
U
4

Too long to fit nicely in comment box, so pseudo-answer:

If it's only a few (or few dozen) functions, how about an override wrapper function, e.g.

summarize<-function(whichone='h',//all variables for either "summarize"// ) {
 switch(whichone,
      'h' = Hmisc::summarize(//all the appropriate variables//),
      'p' = plyr:: summarize(//all the appropriate variables//)
       )
}
Unreflecting answered 24/6, 2014 at 16:57 Comment(1)
Thanks, but I guess the whole point of this question is circumventing the part where you have to know which functions conflict. I know you can use conflicts but that could get annoying, and it doesn't lead to robust code (preventing future overlaps). And FYI this is where the ... argument is really handy.Omidyar
B
0

there is an R package, aptly named import check it out. I believe it offers that Python-like capability.

A link to a vignette of the import package

And another link with some introduction

Finally, some more information about advanced usage

Blanks answered 3/4, 2023 at 12:27 Comment(1)
A link to the package or repo may have been helpful, and no, it is not new: initial commit nine years ago. As one illustration, it is so old that under 'see also' it still refers to modules rather than box.Hellenize

© 2022 - 2024 — McMap. All rights reserved.