Reputation: 32986
I seem to be sharing a lot of code with coauthors these days. Many of them are novice/intermediate R users and don't realize that they have to install packages they don't already have.
Is there an elegant way to call installed.packages()
, compare that to the ones I am loading and install if missing?
Upvotes: 457
Views: 266362
Reputation: 2179
NEW ANSWER:
packrat
has been replaced in RStudio by renv
now. See comment below. So my answer is no longer relevant.
ORIGINAL ANSWER:
Use packrat
so that the shared libraries are exactly the same and not changing other's environment.
In terms of elegance and best practice I think you're fundamentally going about it the wrong way. The package packrat
was designed for these issues. It is developed by RStudio by Hadley Wickham. Instead of them having to install dependencies and possibly mess up someone's environment system, packrat
uses its own directory and installs all the dependencies for your programs in there and doesn't touch someone's environment.
Packrat is a dependency management system for R.
R package dependencies can be frustrating. Have you ever had to use trial-and-error to figure out what R packages you need to install to make someone else’s code work–and then been left with those packages globally installed forever, because now you’re not sure whether you need them? Have you ever updated a package to get code in one of your projects to work, only to find that the updated package makes code in another project stop working?
We built packrat to solve these problems. Use packrat to make your R projects more:
- Isolated: Installing a new or updated package for one project won’t break your other projects, and vice versa. That’s because packrat gives each project its own private package library.
- Portable: Easily transport your projects from one computer to another, even across different platforms. Packrat makes it easy to install the packages your project depends on.
- Reproducible: Packrat records the exact package versions you depend on, and ensures those exact versions are the ones that get installed wherever you go.
https://rstudio.github.io/packrat/
Upvotes: 8
Reputation: 1594
I realize this is an older question, but much of the technology in R has been greatly improved in recent months and years to address this exact issue. The generally accepted and modern approach for handling the sharing of code with package dependencies that may or may not be installed on a collaborator's computer or the target computer is to use the renv
package ("renv
" is short for "Reproducible Environment") which was first made available as renv
0.8.0
in late October of 2019. Version 1.0.0
was officially released on July 7, 2023, and, as of this post, the current CRAN release is Version 1.0.2
, which was made available last month on August 15, 2023.
This package greatly aids R collaborators in sharing code, packages (their exact version and their dependencies and their versions), and even the tracking of the exact version of R that was used to build and execute code in an R project.
To use it, typically the initial programmer creates an R project in R Studio/Posit and then initializes renv
within the project using the command renv::init()
. When this is executed, R does the following:
.libPaths()
). As part of this step, renv
also creates an renv
directory which contains some settings and files that most users don't generally bother with as renv
takes care of this for you.Rprofile
file, which is a project-specific R project profile that configures R to use the project specific library created in step 1 when you open the project.renv.lock
in the project home directory, which logs details about all the packages and versions used by the project -- this is what makes using renv
the most attractive option for sharing code with package and package/version dependencies.After initializing the project environment with the init()
command, the initial programmer develops and writes code as typical, installing packages in the usual manner (e.g., by simply entering install.packages("PackageName")
into the console) along they way as they are needed. These packages are stored into the project library, however, rather than any global library. When the initial programmer feels the code is in a satisfactory state to share the entire project, he simply issues the renv::snapshot()
command which simply updates the lockfile created in step 3 above, with the names, version numbers, etc. of all the packages used in the project.
At this point the initial programmer can share the project with other collaborators (ideally this is done through GitHub, Bitbucket, Azure DevOps, etc.). When any new collaborators open the R project, they simply enter the command renv::restore()
at the console. This will install the exact same version of every package used in the project (and it will be restored into the project library rather than the user's global package library).
This is an excellent workflow because it will ensure all users are working with the same version of the packages the initial developer used. Without renv
, it's possible that a collaborator has a totally different version of an R package installed in their global library, and it may function differently from the initial developer's package. This could result in the code breaking, or worse yet, running, but resulting in different output, possibly unbeknownst to the end users.
As perfectly described on the Introduction to renv vignette, renv
benefits users by ensuring package isolation, reproducibility across users and environments, and portability. renv()
also enjoys some other benefits like caching of R packages so that they don't have to be downloaded multiple times when used across projects. It also works with Python and Python packages. If you also want to ensure that every detail of an enviroment—including the operating system and other external (to R/R Studio) dependencies—are in place, you can easily do so too by combining renv
with a Docker image to fully containerize a solution!
One final note: if renv
seems similar to packrat
, that's because it is, somewhat. In fact the deverlopers of rev
first attempted to build a reproducible environment package using packrat
but gave up and "rolled their own" after struggling with its limitations. But it's vastly better too, I'd argue. The good news is, if you're already using packrat
and want to upgrade to this modern tool, you can easily do so with the renv::migrate()
command!
Good luck and happy collaborating!
Upvotes: 2
Reputation: 1001
Is really nobody using librarian
?
# install.packages("librarian") # install if you do not have it
librarian::shelf(Rcpp, tidyverse/ggplot2)
As you can see it even can install from github without quotation marks making it more consistent. Furthermore, it can install bioconductor packages.
Upvotes: 1
Reputation: 34703
TL;DR you can use find.package()
for this.
Almost all the answers here rely on either (1) require()
or (2) installed.packages()
to check if a given package is already installed or not.
I'm adding an answer because these are unsatisfactory for a lightweight approach to answering this question.
require
has the side effect of loading the package's namespace, which may not always be desirableinstalled.packages
is a bazooka to light a candle -- it will check the universe of installed packages first, then we check if our one (or few) package(s) are "in stock" at this library. No need to build a haystack just to find a needle.This answer was also inspired by @ArtemKlevtsov's great answer in a similar spirit on a duplicated version of this question. He noted that system.file(package=x)
can have the desired affect of returning ''
if the package isn't installed, and something with nchar > 1
otherwise.
If we look under the hood of how system.file
accomplishes this, we can see it uses a different base
function, find.package
, which we could use directly:
# a package that exists
find.package('data.table', quiet=TRUE)
# [1] "/Library/Frameworks/R.framework/Versions/4.0/Resources/library/data.table"
# a package that does not
find.package('InstantaneousWorldPeace', quiet=TRUE)
# character(0)
We can also look under the hood at find.package
to see how it works, but this is mainly an instructive exercise -- the only ways to slim down the function that I see would be to skip some robustness checks. But the basic idea is: look in .libPaths()
-- any installed package pkg
will have a DESCRIPTION
file at file.path(.libPaths(), pkg)
, so a quick-and-dirty check is file.exists(file.path(.libPaths(), pkg, 'DESCRIPTION')
.
Upvotes: 38
Reputation: 12819
The current version of RStudio (>=1.2) includes a feature to detect missing packages in library()
and require()
calls, and prompts the user to install them:
Detect missing R packages
Many R scripts open with calls to
library()
andrequire()
to load the packages they need in order to execute. If you open an R script that references packages that you don’t have installed, RStudio will now offer to install all the needed packages in a single click. No more typinginstall.packages()
repeatedly until the errors go away!
https://blog.rstudio.com/2018/11/19/rstudio-1-2-preview-the-little-things/
This seems to address the original concern of OP particularly well:
Many of them are novice/intermediate R users and don't realize that they have to install packages they don't already have.
Upvotes: 7
Reputation: 4644
Standing on the shoulders of @MichaelChirico:
stopifnot(3 == length(find.package(c('foo', 'bar', 'baz'))))
Upvotes: 0
Reputation: 3494
Today, I stumbled on two handy function provided by the rlang package, namely, is_installed()
and check_installed()
.
From the help page (emphasis added):
These functions check that packages are installed with minimal side effects. If installed, the packages will be loaded but not attached.
is_installed()
doesn't interact with the user. It simply returnsTRUE
orFALSE
depending on whether the packages are installed.In interactive sessions,
check_installed()
asks the user whether to install missing packages. If the user accepts, the packages are installed [...]. If the session is non interactive or if the user chooses not to install the packages, the current evaluation is aborted.
interactive()
#> [1] FALSE
rlang::is_installed(c("dplyr"))
#> [1] TRUE
rlang::is_installed(c("foobarbaz"))
#> [1] FALSE
rlang::check_installed(c("dplyr"))
rlang::check_installed(c("foobarbaz"))
#> Error:
#> ! The package `foobarbaz` is required.
Created on 2022-03-25 by the reprex package (v2.0.1)
Upvotes: 6
Reputation: 3388
You can just use the return value of require
:
if(!require(somepackage)){
install.packages("somepackage")
library(somepackage)
}
I use library
after the install because it will throw an exception if the install wasn't successful or the package can't be loaded for some other reason. You make this more robust and reuseable:
dynamic_require <- function(package){
if(eval(parse(text=paste("require(",package,")")))) return(TRUE)
install.packages(package)
return(eval(parse(text=paste("require(",package,")"))))
}
The downside to this method is that you have to pass the package name in quotes, which you don't do for the real require
.
Upvotes: 102
Reputation: 151
There is a new-ish package (I am a codeveloper), Require
, that is intended to be part of a reproducible workflow, meaning the function produces the same output the first time it is run or subsequent times, i.e., the end-state is the same regardless of starting state. The following installs any missing packages (I include require = FALSE
to strictly address the original question... normally I leave this on the default because I will generally want them loaded to the search path).
These two lines are at the top of every script I write (adjusting the package selection as necessary), allowing the script to be used by anybody in any condition (including any or all dependencies missing).
if (!require("Require")) install.packages("Require")
Require::Require(c("ggplot2", "Rcpp"), require = FALSE)
You can thus use this in your script or pass it anyone.
Upvotes: 1
Reputation: 23
pckg=c("shiny","ggplot2","dplyr","leaflet","lubridate","RColorBrewer","plotly","DT","shinythemes")
for(i in 1:length(pckg))
{
print(pckg[i])
if (!is.element(pckg[i], installed.packages()[,1]))
install.packages(pckg[i], dep = TRUE)
require(pckg[i], character.only = TRUE)
}
Upvotes: 0
Reputation: 2063
Let me share a bit of madness:
c("ggplot2","ggsci", "hrbrthemes", "gghighlight", "dplyr") %>% # What will you need to load for this script?
(function (x) ifelse(t =!(x %in% installed.packages()),
install.packages(x[t]),
lapply(x, require)))
Upvotes: 1
Reputation: 196
packages_installed <- function(pkg_list){
pkgs <- unlist(pkg_list)
req <- unlist(lapply(pkgs, require, character.only = TRUE))
not_installed <- pkgs[req == FALSE]
lapply(not_installed, install.packages,
repos = "http://cran.r-project.org")# add lib.loc if needed
lapply(pkgs, library, character.only = TRUE)
}
Upvotes: 0
Reputation: 1956
Using lapply family and anonymous function approach you may:
||
lazy evaluation).Print each package final load status (TRUE
/ FALSE
).
req <- substitute(require(x, character.only = TRUE))
lbs <- c("plyr", "psych", "tm")
sapply(lbs, function(x) eval(req) || {install.packages(x); eval(req)})
plyr psych tm
TRUE TRUE TRUE
Upvotes: 2
Reputation: 4693
In my case, I wanted a one liner that I could run from the commandline (actually via a Makefile). Here is an example installing "VGAM" and "feather" if they are not already installed:
R -e 'for (p in c("VGAM", "feather")) if (!require(p, character.only=TRUE)) install.packages(p, repos="http://cran.us.r-project.org")'
From within R it would just be:
for (p in c("VGAM", "feather")) if (!require(p, character.only=TRUE)) install.packages(p, repos="http://cran.us.r-project.org")
There is nothing here beyond the previous solutions except that:
repos
parameter (to avoid any popups asking about the mirror to use)Also note the important character.only=TRUE
(without it, the require
would try to load the package p
).
Upvotes: 1
Reputation: 1293
source("https://bioconductor.org/biocLite.R")
if (!require("ggsci")) biocLite("ggsci")
Upvotes: 2
Reputation: 4339
A lot of the answers above (and on duplicates of this question) rely on installed.packages
which is bad form. From the documentation:
This can be slow when thousands of packages are installed, so do not use this to find out if a named package is installed (use system.file or find.package) nor to find out if a package is usable (call require and check the return value) nor to find details of a small number of packages (use packageDescription). It needs to read several files per installed package, which will be slow on Windows and on some network-mounted file systems.
So, a better approach is to attempt to load the package using require
and and install if loading fails (require
will return FALSE
if it isn't found). I prefer this implementation:
using<-function(...) {
libs<-unlist(list(...))
req<-unlist(lapply(libs,require,character.only=TRUE))
need<-libs[req==FALSE]
if(length(need)>0){
install.packages(need)
lapply(need,require,character.only=TRUE)
}
}
which can be used like this:
using("RCurl","ggplot2","jsonlite","magrittr")
This way it loads all the packages, then goes back and installs all the missing packages (which if you want, is a handy place to insert a prompt to ask if the user wants to install packages). Instead of calling install.packages
separately for each package it passes the whole vector of uninstalled packages just once.
Here's the same function but with a windows dialog that asks if the user wants to install the missing packages
using<-function(...) {
libs<-unlist(list(...))
req<-unlist(lapply(libs,require,character.only=TRUE))
need<-libs[req==FALSE]
n<-length(need)
if(n>0){
libsmsg<-if(n>2) paste(paste(need[1:(n-1)],collapse=", "),",",sep="") else need[1]
print(libsmsg)
if(n>1){
libsmsg<-paste(libsmsg," and ", need[n],sep="")
}
libsmsg<-paste("The following packages could not be found: ",libsmsg,"\n\r\n\rInstall missing packages?",collapse="")
if(winDialog(type = c("yesno"), libsmsg)=="YES"){
install.packages(need)
lapply(need,require,character.only=TRUE)
}
}
}
Upvotes: 58
Reputation: 21
library <- function(x){
x = toString(substitute(x))
if(!require(x,character.only=TRUE)){
install.packages(x)
base::library(x,character.only=TRUE)
}}
This works with unquoted package names and is fairly elegant (cf. GeoObserver's answer)
Upvotes: 1
Reputation: 401
if (!require('ggplot2')) install.packages('ggplot2'); library('ggplot2')
"ggplot2" is the package. It checks to see if the package is installed, if it is not it installs it. It then loads the package regardless of which branch it took.
Upvotes: 40
Reputation: 12613
You can simply use the setdiff
function to get the packages that aren't installed and then install them. In the sample below, we check if the ggplot2
and Rcpp
packages are installed before installing them.
unavailable <- setdiff(c("ggplot2", "Rcpp"), rownames(installed.packages()))
install.packages(unavailable)
In one line, the above can be written as:
install.packages(setdiff(c("ggplot2", "Rcpp"), rownames(installed.packages())))
Upvotes: 8
Reputation: 2438
Thought I'd contribute the one I use:
testin <- function(package){if (!package %in% installed.packages())
install.packages(package)}
testin("packagename")
Upvotes: 4
Reputation: 16697
Quite basic one.
pkgs = c("pacman","data.table")
if(length(new.pkgs <- setdiff(pkgs, rownames(installed.packages())))) install.packages(new.pkgs)
Upvotes: 3
Reputation: 49
48 lapply_install_and_load <- function (package1, ...)
49 {
50 #
51 # convert arguments to vector
52 #
53 packages <- c(package1, ...)
54 #
55 # check if loaded and installed
56 #
57 loaded <- packages %in% (.packages())
58 names(loaded) <- packages
59 #
60 installed <- packages %in% rownames(installed.packages())
61 names(installed) <- packages
62 #
63 # start loop to determine if each package is installed
64 #
65 load_it <- function (p, loaded, installed)
66 {
67 if (loaded[p])
68 {
69 print(paste(p, "loaded"))
70 }
71 else
72 {
73 print(paste(p, "not loaded"))
74 if (installed[p])
75 {
76 print(paste(p, "installed"))
77 do.call("library", list(p))
78 }
79 else
80 {
81 print(paste(p, "not installed"))
82 install.packages(p)
83 do.call("library", list(p))
84 }
85 }
86 }
87 #
88 lapply(packages, load_it, loaded, installed)
89 }
Upvotes: 2
Reputation: 723
I use following function to install package if require("<package>")
exits with package not found error. It will query both - CRAN and Bioconductor repositories for missing package.
Adapted from the original work by Joshua Wiley, http://r.789695.n4.nabble.com/Install-package-automatically-if-not-there-td2267532.html
install.packages.auto <- function(x) {
x <- as.character(substitute(x))
if(isTRUE(x %in% .packages(all.available=TRUE))) {
eval(parse(text = sprintf("require(\"%s\")", x)))
} else {
#update.packages(ask= FALSE) #update installed packages.
eval(parse(text = sprintf("install.packages(\"%s\", dependencies = TRUE)", x)))
}
if(isTRUE(x %in% .packages(all.available=TRUE))) {
eval(parse(text = sprintf("require(\"%s\")", x)))
} else {
source("http://bioconductor.org/biocLite.R")
#biocLite(character(), ask=FALSE) #update installed packages.
eval(parse(text = sprintf("biocLite(\"%s\")", x)))
eval(parse(text = sprintf("require(\"%s\")", x)))
}
}
Example:
install.packages.auto(qvalue) # from bioconductor
install.packages.auto(rNMF) # from CRAN
PS: update.packages(ask = FALSE)
& biocLite(character(), ask=FALSE)
will update all installed packages on the system. This can take a long time and consider it as a full R upgrade which may not be warranted all the time!
Upvotes: 5
Reputation: 808
Here's my code for it:
packages <- c("dplyr", "gridBase", "gridExtra")
package_loader <- function(x){
for (i in 1:length(x)){
if (!identical((x[i], installed.packages()[x[i],1])){
install.packages(x[i], dep = TRUE)
} else {
require(x[i], character.only = TRUE)
}
}
}
package_loader(packages)
Upvotes: 1
Reputation: 3753
I have implemented the function to install and load required R packages silently. Hope might help. Here is the code:
# Function to Install and Load R Packages
Install_And_Load <- function(Required_Packages)
{
Remaining_Packages <- Required_Packages[!(Required_Packages %in% installed.packages()[,"Package"])];
if(length(Remaining_Packages))
{
install.packages(Remaining_Packages);
}
for(package_name in Required_Packages)
{
library(package_name,character.only=TRUE,quietly=TRUE);
}
}
# Specify the list of required packages to be installed and load
Required_Packages=c("ggplot2", "Rcpp");
# Call the Function
Install_And_Load(Required_Packages);
Upvotes: 3
Reputation: 389
The following simple function works like a charm:
usePackage<-function(p){
# load a package if installed, else load after installation.
# Args:
# p: package name in quotes
if (!is.element(p, installed.packages()[,1])){
print(paste('Package:',p,'Not found, Installing Now...'))
install.packages(p, dep = TRUE)}
print(paste('Loading Package :',p))
require(p, character.only = TRUE)
}
(not mine, found this on the web some time back and had been using it since then. not sure of the original source)
Upvotes: 5
Reputation: 109844
Dason K. and I have the pacman package that can do this nicely. The function p_load
in the package does this. The first line is just to ensure that pacman is installed.
if (!require("pacman")) install.packages("pacman")
pacman::p_load(package1, package2, package_n)
Upvotes: 328
Reputation: 1050
# List of packages for session
.packages = c("ggplot2", "plyr", "rms")
# Install CRAN packages (if not already installed)
.inst <- .packages %in% installed.packages()
if(length(.packages[!.inst]) > 0) install.packages(.packages[!.inst])
# Load packages into session
lapply(.packages, require, character.only=TRUE)
Upvotes: 14
Reputation: 148
Regarding your main objective " to install libraries they don't already have. " and regardless of using " instllaed.packages() ". The following function mask the original function of require. It tries to load and check the named package "x" , if it's not installed, install it directly including dependencies; and lastly load it normaly. you rename the function name from 'require' to 'library' to maintain integrity . The only limitation is packages names should be quoted.
require <- function(x) {
if (!base::require(x, character.only = TRUE)) {
install.packages(x, dep = TRUE) ;
base::require(x, character.only = TRUE)
}
}
So you can load and installed package the old fashion way of R. require ("ggplot2") require ("Rcpp")
Upvotes: 2
Reputation: 59970
This solution will take a character vector of package names and attempt to load them, or install them if loading fails. It relies on the return behaviour of require
to do this because...
require
returns (invisibly) a logical indicating whether the required package is available
Therefore we can simply see if we were able to load the required package and if not, install it with dependencies. So given a character vector of packages you wish to load...
foo <- function(x){
for( i in x ){
# require returns TRUE invisibly if it was able to load package
if( ! require( i , character.only = TRUE ) ){
# If package was not able to be loaded then re-install
install.packages( i , dependencies = TRUE )
# Load package after installing
require( i , character.only = TRUE )
}
}
}
# Then try/install packages...
foo( c("ggplot2" , "reshape2" , "data.table" ) )
Upvotes: 24