From [package] import [function] in R
Asked Answered
V

5

26

Working with data in Python or R, we often load several packages. In some cases, two packages (e.g. foo and bar) might each contain some function (e.g. do_stuff).

The way this is managed in Python to prevent ambiguity or surprises is like:

from foo import do_stuff
from bar import other_function    # (does not load/import do_stuff() from bar)

In R, all the code I see just imports whole packages with multiple library(package_name) statements. I would think this would lead to very difficult-to-catch bugs. For example, see Reordering factor gives different results, depending on which packages are loaded. In fact this occurred even though "there is no masking, since reorder.factor doesn't exist in base."

I expected the general answer to this problem to be something like the from package import function code above, but it wasn't. In fact the accepted (and only) answer just explains why the problem exists (not to downplay that contribution). There's a workaround provided in a comment of the answer, but that workaround is specific to that particular function (reorder).

Is there a general way that I can import only a specific function from a specific package in R? So that I can be deliberate and unambiguous about where all of the function calls in my code come from and ensure that they do what I think they're doing?

Villanueva answered 17/7, 2017 at 18:6 Comment(2)
You can use package::function syntax. If you want skip using that syntax always you could do something like function <- package::function and then use it as is.Kovno
You could also build your own package and only import the functions you need.Flagstad
S
20

You can explicitly tell R which package should be used for a given function using the package::function() construction. You can even use that to call functions from packages that you haven't loaded with library.

library(dplyr) # Has a function called filter()
library(plyr) # Also has a filter() function

dplyr::filter(foo)
plyr::filter(bar)

If you want to make sure you're minimizing the possibility for confusion in your code, I highly recommend the conflicted package, which forces you to explicitly identify the package for all ambiguous function calls: https://www.tidyverse.org/articles/2018/06/conflicted/

Standice answered 17/7, 2017 at 18:12 Comment(2)
One of the reasons to put library(whatever) at the top of your script / markdown document is to make sure that the corresponding package is installed. pkg::function() without library(pkg) in the middle of a script runs the risk of the package not being present... which could be detected much earlier. If you don't want to load it for whatever reason (e.g. you should never load tidyverse as it is way too bloated for generic use), you could still check for the package presence with packageVersion("pkg").Shifflett
@Shifflett tidyverse doesn't take up much RAM and it's helpful to have a lot of tools at hand when you're hacking away at a new project. If you have working code that you're trying to optimize for production, you should be using a package manager that will know/manage what packages are available.Automatism
P
31

As of R 4.0, you can also do this with the main library() function:

library(dplyr, include.only = c("select", "mutate"))
library(dplyr, exclude = c("filter", "lag"))
Province answered 23/6, 2021 at 12:18 Comment(2)
Where the accepted answer is the literal answer to the posed question, and considered by some to be good style, this particular answer enables a user to work around one library depending on another library's functions under the assumption that the dependency is available in the global scope. Incidentally, before R 4.0 mutate <- dplyr::mutate appears to be functionally equivalent.Octamerous
my_fun <- pkg::their_fun is fairly awful code. While it is executed at run time for scripts, it is executed at build time for packages. This should generally be avoided r-pkgs.org/code.html#example-aliasing-a-function.Shifflett
S
20

You can explicitly tell R which package should be used for a given function using the package::function() construction. You can even use that to call functions from packages that you haven't loaded with library.

library(dplyr) # Has a function called filter()
library(plyr) # Also has a filter() function

dplyr::filter(foo)
plyr::filter(bar)

If you want to make sure you're minimizing the possibility for confusion in your code, I highly recommend the conflicted package, which forces you to explicitly identify the package for all ambiguous function calls: https://www.tidyverse.org/articles/2018/06/conflicted/

Standice answered 17/7, 2017 at 18:12 Comment(2)
One of the reasons to put library(whatever) at the top of your script / markdown document is to make sure that the corresponding package is installed. pkg::function() without library(pkg) in the middle of a script runs the risk of the package not being present... which could be detected much earlier. If you don't want to load it for whatever reason (e.g. you should never load tidyverse as it is way too bloated for generic use), you could still check for the package presence with packageVersion("pkg").Shifflett
@Shifflett tidyverse doesn't take up much RAM and it's helpful to have a lot of tools at hand when you're hacking away at a new project. If you have working code that you're trying to optimize for production, you should be using a package manager that will know/manage what packages are available.Automatism
K
14

Although this answer is correct, it doesn't work for infix operators such as magrittr's %>% and %$%. The import package works a treat:

import::from(magrittr, "%$%")

But obviously can be used for any function:

import::from(foo, "do_stuff", "do_other_stuff")

Be aware that "[import] is not intended for use with library. It is named to make calls like import::from(pkg, fun1, fun2) expressive." See https://CRAN.R-project.org/package=import for more details.

Knick answered 16/9, 2018 at 12:0 Comment(1)
One can also simply do `%>%` = magrittr::`%>%`Navarrete
S
8

You're comparing two languages that function differently. First of all, you can easily refer to a function from a package using ::, eg:

fortunes::fortune()

To call the fortune function from the fortunes package.

But you also have to be careful doing so. Because depending on how the package is constructed, you might end up with using a function that's dependent on other (non-exported) functions from the namespace, but that can't be found because the namespace isn't loaded. I've run into that problem myself using plotting functions from eg the mgcv package. This is one reason to attach the packages using library() as Andrew Breza illustrates in his answer. At least the functions you call using the :: construct will work (99.9% of the time) as expected.

But the problem you link to, won't be solved by this. The problem there is "bad design" in the sense that the package author decided he needed an S3 method for reordering a factor instead of a class specific to his package. Doing so registered an S3 method for the entire factor class and hence changed the behaviour of every function that calls reorder() on a factor.

And as that problem is caused by the package developers themselves, there's little you can do as a user apart from hacking into the S3 system yourself to find the actual method you need.

Seise answered 17/7, 2017 at 18:19 Comment(3)
Thanks Joris, your last three paragraphs on the question I linked to helped clear up a lot of confusion. Ultimately I guess the solution to a problem with a bad side effect from an imported package is "use a different package, at least until the package designer fixes the side-effect?"Villanueva
@MaxPower That's what I would do. Python and R really have a different philosophy. Python is a LOT more strict about things than R. I love R because of its freedom, but I fully understand the downside of having all that freedom: other people have it too... ;-)Seise
@MaxPower On a sidenote: The question you link to, is from R 2.15 still. Since R 3.0, the whole namespace mechanism for packages was made obligatory and was made a lot more robust. So many of the problems we used to have, were tackled with the namespaces. There's far less conflicts between packages these days compared to "the old days." But with enough determination... : fortune("foot")Seise
U
1

Building on wjchulme's answer, the import package also lets you import functions or other objects from your own scripts giving R a modular feel, much like python modules.

import::from(your_script.R, some_function, SOME_CONSTANT, and_whatever)

Ref: https://cran.r-project.org/web/packages/import/vignettes/import.html

Unsearchable answered 13/4, 2022 at 17:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.