Function to Simplify Loading and Installing Packages

[This article was first published on Jason.Bryer.org Blog - R, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

One of the more tedious parts of working with R is maintaining my R library. To make my R scripts reproducible and sharable, I will install packages if they are not available. For example, the top of my R scripts tend to look something like this:

if(!require(devtools) | !require(ggplot2) | !require(psych) | !require(lme4) | !require(benchmark)) {
	install.packages(c('devtools','ggplot2','psych','lme4','benchmark'))
}

This has worked fine for some time, but I felt there was a better approach. First, note that if any one package doesn’t load (usually because it is not installed), all the packages are installed. I could separate the if statement so there is one per package, but then I have even more lines in my R script. Instead, I have written a function that will load each package separately and install only those that are not present. And optionally will even update packages using the update parameter. For example, I can now replace the above with one call to package:

> package(c('devtools','ggplot2','psych','lme4','benchmark'))

The output is minimal by default (set quiet=FALSE to get all the messages printed by require and install.packages). Even though verbose=TRUE by default, the only messages it will print is to indicate that a newer version of a package is available or that the package is not available on the repositories. In place of output to the console, a data frame is returned with a summary of what packages have been loaded and/or installed along with the loaded and available versions. Here are the results from the command above:

A newer version of lme4 is available (current=1.0.5; available=1.0.6)

          loaded installed loaded.version available.version
devtools    TRUE     FALSE          1.4.1             1.4.1
ggplot2     TRUE     FALSE        0.9.3.1           0.9.3.1
psych       TRUE     FALSE        1.4.2.3           1.4.2.3
lme4        TRUE     FALSE          1.0.5             1.0.6
benchmark   TRUE      TRUE          0.3.5             0.3.5

Note that if I had specified update=TRUE (it is FALSE by default) the lme4 package would have been automatically updated.

In summary, I have collapsed what usually takes several lines within my R scripts to just one line, or two if you need to source this function. However, I just source this function in my .Rprofile so that it is always available. The only potential downside is that this is not part of base R and requires anyone you share your R scripts with to also have this function available.

The sourse code is on Gist here: https://gist.github.com/jbryer/9112634
With devtools installed you can just source function using:

> source_gist(9112634)
#' Simplified loading and installing of packages
#'
#' This is a wrapper to \code{\link{require}} and \code{\link{install.packages}}.
#' Specifically, this will first try to load the package(s) and if not found
#' it will install then load the packages. Additionally, if the
#' \code{update=TRUE} parameter is specified it will check the currently
#' installed package version with what is available on CRAN (or mirror) and
#' install the newer version.
#'
#' @param pkgs a character vector with the names of the packages to load.
#' @param install if TRUE (default), any packages not already installed will be.
#' @param update if TRUE, this function will install a newer version of the
#' package if available.
#' @param quiet if TRUE (default), package startup messages will be suppressed.
#' @param verbose if TRUE (default), diagnostic messages will be printed.
#' @param ... other parameters passed to \code{\link{require}},
#' \code{\link{install.packages}}, and
#' \code{\link{available.packages}}.
#' @return a data frame with four columns and rownames corresponding to the
#' packages to be loaded. The four columns are: loaded (logical
#' indicating whether the package was successfully loaded), installed
#' (logical indicating that the package was installed or updated),
#' loaded.version (the version string of the installed package), and
#' available.version (the version string of the package currently
#' available on CRAN). Note that this only reflects packages listed in
#' the \code{pkgs} parameter. Other packages may be loaded and/or
#' installed as necessary by \code{install.packages} and \code{require}.
#' If \code{verbose=FALSE} the data frame will be returned using
#' \code{\link{invisible}}.
#' @export
#' @example
#' \dontrun{
#' package(c('devtools','lattice','ggplot2','psych'))
#' }
package <- function(pkgs, install=TRUE, update=FALSE, quiet=TRUE, verbose=TRUE, ...) {
myrequire <- function(package, ...) {
result <- FALSE
if(quiet) {
suppressMessages(suppressWarnings(result <- require(package, ...)))
} else {
result <- suppressWarnings(require(package, ...))
}
return(result)
}
mymessage <- function(msg) {
if(verbose) {
message(msg)
}
}
installedpkgs <- installed.packages()
availpkgs <- available.packages()[,c('Package','Version')]
if(nrow(availpkgs) == 0) {
warning(paste0('There appear to be no packages available from the ',
'repositories. Perhaps you are not connected to the ',
'Internet?'))
}
# It appears that hyphens (-) will be replaced with dots (.) in version
# numbers by the packageVersion function
availpkgs[,'Version'] <- gsub('-', '.', availpkgs[,'Version'])
results <- data.frame(loaded=rep(FALSE, length(pkgs)),
installed=rep(FALSE, length(pkgs)),
loaded.version=rep(as.character(NA), length(pkgs)),
available.version=rep(as.character(NA), length(pkgs)),
stringsAsFactors=FALSE)
row.names(results) <- pkgs
for(i in pkgs) {
loadedPkgs <- search()
needInstall <- FALSE
if(i %in% row.names(installedpkgs)) {
v <- as.character(packageVersion(i))
if(i %in% row.names(availpkgs)) {
if(v != availpkgs[i,'Version']) {
if(!update) {
mymessage(paste0('A newer version of ', i,
' is available ', '(current=', v,
'; available=',
availpkgs[i,'Version'], ')'))
}
needInstall <- update
}
results[i,]$available.version <- availpkgs[i,'Version']
} else {
mymessage(paste0(i, ' is not available on the repositories.'))
}
} else {
if(i %in% row.names(availpkgs)) {
needInstall <- TRUE & install
results[i,]$available.version <- availpkgs[i,'Version']
} else {
warning(paste0(i, ' is not available on the repositories and ',
'is not installed locally'))
}
}
if(needInstall | !myrequire(i, character.only=TRUE)) {
install.packages(pkgs=i, quiet=quiet)
if(!myrequire(i, character.only=TRUE, ...)) {
warning(paste0('Error loading package: ', i))
} else {
results[i,]$installed <- TRUE
results[i,]$loaded <- TRUE
results[i,]$loaded.version <- as.character(packageVersion(i))
}
} else {
results[i,]$loaded <- TRUE
results[i,]$loaded.version <- as.character(packageVersion(i))
}
loadedPkgs2 <- search()
for(j in loadedPkgs2[!loadedPkgs2 %in% loadedPkgs]) {
try(detach(j, character.only=TRUE), silent=TRUE)
}
}
if(verbose) {
return(results)
} else {
invisible(results)
}
}
view raw package.R hosted with ❤ by GitHub

To leave a comment for the author, please follow the link and comment on their blog: Jason.Bryer.org Blog - R.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)