I am interested in how to write a function that can be used for an arbitrary number o R packages to
I tried multiple variations of the following:
import <- function(...) {
#' Import R packages. Install them if necessary.
#'
#' @param ... any argument that can be passed to install.packages. Provide
#' package names as character strings.
#' @details The function installs only packages that are missing. Packages
#' are loaded.
#' @examples
#' # Load packages
#' import("dplyr", "MASS", "terra", dependencies = TRUE)
#'
#' @seealso \code{\link[base]{install.packages}}
#' @export
args <- list(...)
packages = args[names(args) == ""]
kwargs = args[names(args) != ""]
for (package in packages) {
if (!requireNamespace(package, quietly = TRUE)) {
do.call(install.packages, c(list(package), kwargs))
}
#base::library(package, character.only = TRUE, pos = .GlobalEnv)
eval(
bquote(library(.(package), character.only = TRUE)),
envir = .GlobalEnv
)
}
}
With this function, when I run
import(
"lubridate", "tidyverse", "ncdf4", "viridis", "terra", "rworldmap", "EFDR",
"RColorBrewer"
)
I cannot use functions directly (e.g., nc_close()
), but I need to specify the package (ncdf4::nc_close()
).
Is there a way to make the functions directly available (without running require
on the packages again, outside the import
function)?
Edit
The problem was not that the package import was done within a function, but rather, the issue was that that function was not robust in handling args and kwargs. Here is a working version:
import <- function(...) {
#' Import R packages. Install them if necessary.
#'
#' @param ... any argument that can be passed to install.packages. Provide
#' package names as character strings.
#' @details The function installs only packages that are missing. Packages
#' are loaded.
#' @examples
#' # Load packages
#' import("dplyr", "MASS", "terra", dependencies = TRUE)
#'
#' @seealso \code{\link[base]{install.packages}}
#' @export
args <- list(...)
arg_names <- names(args)
packages = if (is.null(arg_names)) {
args
} else {
args[arg_names == "" | is.null(arg_names)]
}
kwargs = if (is.null(arg_names)) {
list()
} else {
args[!is.null(arg_names) & arg_names != ""]
}
load <- function(package) {
if (!requireNamespace(package, quietly = TRUE)) {
do.call(install.packages, c(list(package), kwargs))
}
base::library(package, character.only = TRUE, pos = .GlobalEnv)
}
invisible(lapply(packages, load))
}
import(
"lubridate", "tidyverse", "ncdf4", "viridis", "terra", "rworldmap", "EFDR",
"RColorBrewer"
)
I think this does the trick:
import <- function(x) {
if (!require(x, character.only = TRUE, quiet = TRUE)) install.packages(x)
require(x, character.only = TRUE)
}
Example:
> geom_ridgeline()
Error in geom_ridgeline() : could not find function "geom_ridgeline"
> import('ggridges')
Installing package into ‘/home/zeloff/R/4.4’
(as ‘lib’ is unspecified)
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2179k 100 2179k 0 0 1690k 0 0:00:01 0:00:01 --:--:-- 1691k
* installing *source* package ‘ggridges’ ...
** package ‘ggridges’ successfully unpacked and MD5 sums checked
** using staged installation
** R
** data
*** moving datasets to lazyload DB
** inst
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
*** copying figures
** building package indices
** installing vignettes
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (ggridges)
The downloaded source packages are in
‘/tmp/RtmpefUHEC/downloaded_packages’
Loading required package: ggridges
> geom_ridgeline()
geom_ridgeline: na.rm = FALSE
stat_identity: na.rm = FALSE
position_identity
If you want to import multiple packages at once, just wrap the import
function in an lapply
:
import <- function(...) {
imp <- function(x) {
if (!require(x, character.only = TRUE, quiet = TRUE))
install.packages(x)
require(x, character.only = TRUE)
}
x <- list(...)
invisible(lapply(x, imp))
}