检查丢失包并安装它们的优雅方式?

最近我似乎和合作者分享了很多代码。他们中的许多人是新手/中级R用户,并没有意识到他们必须安装他们还没有的包。

是否有一种优雅的方式来调用installed.packages(),将其与我正在加载和安装的内容进行比较?

215626 次浏览

是的。如果你有你的包列表,将其与installed.packages()[,"Package"]的输出进行比较,并安装缺少的包。就像这样:

list.of.packages <- c("ggplot2", "Rcpp")
new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])]
if(length(new.packages)) install.packages(new.packages)

否则:

如果您将代码放在包中并使它们成为依赖项,那么当您安装包时,它们将自动安装。

确定。

您需要比较“已安装的包”和“所需的包”。这与我使用小红莓所做的非常接近,因为我需要比较“存储的已知包”与“当前已知包”,以确定新的和/或更新的包。

就像这样

AP <- available.packages(contrib.url(repos[i,"url"]))   # available t repos[i]

为了获得所有已知的包,模拟调用当前安装的包,并将其与给定的目标包集进行比较。

虽然Shane的答案真的很好,但对于我的一个项目,我需要删除输出消息,警告和安装包自动。我终于设法得到这个脚本:

InstalledPackage <- function(package)
{
available <- suppressMessages(suppressWarnings(sapply(package, require, quietly = TRUE, character.only = TRUE, warn.conflicts = FALSE)))
missing <- package[!available]
if (length(missing) > 0) return(FALSE)
return(TRUE)
}


CRANChoosen <- function()
{
return(getOption("repos")["CRAN"] != "@CRAN@")
}


UsePackage <- function(package, defaultCRANmirror = "http://cran.at.r-project.org")
{
if(!InstalledPackage(package))
{
if(!CRANChoosen())
{
chooseCRANmirror()
if(!CRANChoosen())
{
options(repos = c(CRAN = defaultCRANmirror))
}
}


suppressMessages(suppressWarnings(install.packages(package)))
if(!InstalledPackage(package)) return(FALSE)
}
return(TRUE)
}

使用:

libraries <- c("ReadImages", "ggplot2")
for(library in libraries)
{
if(!UsePackage(library))
{
stop("Error!", library)
}
}

我使用以下将检查包是否安装和依赖项是否更新,然后加载包。

p<-c('ggplot2','Rcpp')
install_package<-function(pack)
{if(!(pack %in% row.names(installed.packages())))
{
update.packages(ask=F)
install.packages(pack,dependencies=T)
}
require(pack,character.only=TRUE)
}
for(pack in p) {install_package(pack)}


completeFun <- function(data, desiredCols) {
completeVec <- complete.cases(data[, desiredCols])
return(data[completeVec, ])
}

这就是rbundler包的目的:提供一种方法来控制为特定项目安装的包。现在,这个包使用devtools功能将包安装到项目的目录中。该功能类似于Ruby的打包机

如果你的项目是一个包(推荐),那么你所要做的就是加载rbundler并捆绑包。bundle函数将查看包的DESCRIPTION文件,以确定要捆绑哪些包。

library(rbundler)
bundle('.', repos="http://cran.us.r-project.org")

现在这些包将安装在. rbundle目录中。

如果你的项目不是一个包,那么你可以通过在项目的根目录中创建一个DESCRIPTION文件来伪造它,其中包含一个Depends字段,列出你想要安装的包(带有可选的版本信息):

Depends: ggplot2 (>= 0.9.2), arm, glmnet

这里是项目的github回购,如果你有兴趣贡献:rbundler

此解决方案将获取包名的字符向量并尝试加载它们,或者在加载失败时安装它们。它依赖于require的返回行为来做到这一点,因为…

require返回(不可见)一个逻辑,指示所需的包是否可用

因此,我们可以简单地查看是否能够加载所需的包,如果不能,则使用依赖项安装它。所以给定一个你想要加载的包的字符向量…

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" ) )

你可以只使用require的返回值:

if(!require(somepackage)){
install.packages("somepackage")
library(somepackage)
}

我在安装之后使用library,因为如果安装不成功或由于其他原因无法加载包,它将抛出异常。您可以使其更加健壮和可重用:

dynamic_require <- function(package){
if(eval(parse(text=paste("require(",package,")")))) return(TRUE)
  

install.packages(package)
return(eval(parse(text=paste("require(",package,")"))))
}

此方法的缺点是必须以引号传递包名,而对于真正的require则不这样做。

Dason K.和我有吃豆子包,可以很好地做到这一点。包中的p_load函数可以做到这一点。第一行代码只是为了确保安装了pacman。

if (!require("pacman")) install.packages("pacman")
pacman::p_load(package1, package2, package_n)

关于你的主要目标“安装他们还没有的库”。并且不管使用" installed .packages() "”。下面的函数掩码了require的原始函数。它尝试加载和检查命名包“x”,如果它没有安装,直接安装它,包括依赖项;最后正常加载。将函数名从'require'重命名为'library'以保持完整性。唯一的限制是包名应该加引号。

require <- function(x) {
if (!base::require(x, character.only = TRUE)) {
install.packages(x, dep = TRUE) ;
base::require(x, character.only = TRUE)
}
}

所以你可以加载和安装包的老方式R。 要求(“ggplot2”) 要求(“Rcpp”)< / p >

# 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)

我已经实现了无声安装和加载所需R包的功能。希望也许会有所帮助。代码如下:

# 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);

下面这个简单的函数非常好用:

  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)
}

(不是我的,一段时间前在网上找到了这个,从那时起就一直在使用它。不确定原始来源)

下面是我的代码:

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)

我使用以下函数来安装包,如果require("<package>")退出,包未找到错误。它将查询- CRAN和Bioconductor存储库,以查找丢失的包。

改编自Joshua Wiley的原著, http://r.789695.n4.nabble.com/Install-package-automatically-if-not-there-td2267532.html < / p >

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)))
}
}

例子:

install.packages.auto(qvalue) # from bioconductor
install.packages.auto(rNMF) # from CRAN

PS: update.packages(ask = FALSE) &biocLite(character(), ask=FALSE)将更新系统上所有已安装的包。这可能需要很长时间,并将其视为一个完整的R升级,这可能并不总是有保障的!

 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 }

非常基本的一个。

pkgs = c("pacman","data.table")
if(length(new.pkgs <- setdiff(pkgs, rownames(installed.packages())))) install.packages(new.pkgs)

我想贡献一下我用的:

testin <- function(package){if (!package %in% installed.packages())
install.packages(package)}
testin("packagename")

你可以简单地使用setdiff函数来获取未安装的包,然后安装它们。在下面的示例中,我们在安装ggplot2Rcpp包之前检查它们是否已经安装。

unavailable <- setdiff(c("ggplot2", "Rcpp"), rownames(installed.packages()))
install.packages(unavailable)

在一行中,上面的内容可以写成:

install.packages(setdiff(c("ggplot2", "Rcpp"), rownames(installed.packages())))
library <- function(x){
x = toString(substitute(x))
if(!require(x,character.only=TRUE)){
install.packages(x)
base::library(x,character.only=TRUE)
}}

这适用于不带引号的包名,并且相当优雅(参见GeoObserver的答案)

if (!require('ggplot2')) install.packages('ggplot2'); library('ggplot2')

“ggplot2”是包。它检查包是否安装,如果没有安装,就安装它。然后不管它采用哪个分支,它都会加载包。

使用packrat来确保共享库是完全相同的,并且不会改变其他环境。

就优雅和最佳实践而言,我认为你从根本上走错了方向。packrat包就是为这些问题而设计的。它是由RStudio由Hadley Wickham开发的。packrat使用自己的目录并在其中安装你的程序的所有依赖项,而不是必须安装依赖项并可能弄乱某人的环境系统,它不会触及某人的环境。

Packrat是一个基于R的依赖管理系统。

R包依赖关系可能令人沮丧。您是否曾经不得不使用试错法来确定需要安装哪些R包才能使其他人的代码正常工作,然后因为现在您不确定是否需要它们而永远只能安装这些包?您是否曾经更新过一个包以使您的一个项目中的代码能够工作,但却发现更新后的包使另一个项目中的代码停止工作?

我们创建了packrat来解决这些问题。使用packrat使你的R项目更多:

  • 孤立的:为一个项目安装新的或更新的包不会破坏其他项目,反之亦然。这是因为packrat为每个项目提供了自己的私有包库。
  • 便携:可以轻松地将项目从一台计算机传输到另一台计算机,甚至跨不同的平台。Packrat使您可以轻松安装项目所依赖的包。
  • 可重复:Packrat记录您所依赖的确切的包版本,并确保无论您走到哪里,这些确切的版本都将被安装。

< a href = " https://rstudio.github。Io /packrat/" rel="nofollow noreferrer">https://rstudio.github.io/packrat/ .

上面的很多答案(以及这个问题的副本)依赖于installed.packages,这是糟糕的形式。从文档中可以看到:

当安装了数千个包时,这可能会很慢,所以不要使用它来查找是否安装了指定的包(使用system. exe)。或者查找一个包是否可用(调用require并检查返回值),或者查找少量包的详细信息(使用packageDescription)。每个安装的包需要读取几个文件,这在Windows和一些网络挂载的文件系统上会很慢。

因此,更好的方法是尝试使用require加载包,如果加载失败则安装(如果未找到require将返回FALSE)。我更喜欢这样的实现:

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)
}
}

可以这样使用:

using("RCurl","ggplot2","jsonlite","magrittr")

通过这种方式,它加载所有的包,然后返回并安装所有丢失的包(如果您愿意,可以在这里插入提示,询问用户是否想要安装包)。它没有为每个包分别调用install.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)
}
}
}
source("https://bioconductor.org/biocLite.R")
if (!require("ggsci")) biocLite("ggsci")

在我的例子中,我想要一个可以从命令行(实际上是通过Makefile)运行的一行程序。下面是一个安装“VGAM”和“feather”的例子,如果它们还没有安装:

R -e 'for (p in c("VGAM", "feather")) if (!require(p, character.only=TRUE)) install.packages(p, repos="http://cran.us.r-project.org")'

在R内部,它就是:

for (p in c("VGAM", "feather")) if (!require(p, character.only=TRUE)) install.packages(p, repos="http://cran.us.r-project.org")

除了前面的解之外,这里没有别的解:

  • 我只说一句
  • 我硬编码repos参数(以避免任何弹出窗口询问要使用的镜像)
  • 我不想费心定义一个在其他地方使用的函数

还要注意重要的character.only=TRUE(如果没有它,require将尝试加载包p)。

使用lapply族和匿名函数方法,您可以:

  1. 尝试附加所有列出的包。
  2. 仅安装缺失(使用||惰性求值)。
  3. 尝试再次连接那些在第1步中丢失并在第2步中安装的组件。
  4. 打印每个包最终加载状态(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
    

当前版本的RStudio (>=1.2)包含一个功能,用于检测library()require()调用中缺失的包,并提示用户安装它们:

检测丢失的R包

许多R脚本打开时调用library()require()来加载执行所需的包。如果您打开一个R脚本,该脚本引用了您没有安装的包,RStudio现在将提供一次单击即可安装所有所需的包。不再重复输入install.packages(),直到错误消失!
https://blog.rstudio.com/2018/11/19/rstudio-1-2-preview-the-little-things/ < / p >

这似乎很好地解决了OP最初的问题:

他们中的许多人是新手/中级R用户,并没有意识到他们必须安装他们还没有的包。

  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)
}

让我分享一点疯狂:

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)))

你可以使用find.package()来实现。

这里几乎所有的答案都依赖于(1)require()或(2)installed.packages()来检查给定的包是否已经安装。

我添加了一个答案,因为这些对于回答这个问题的轻量级方法来说是不令人满意的。

  • require具有加载包的命名空间的副作用,这可能并不总是可取的
  • installed.packages是一个点燃蜡烛的火箭筒——它将首先检查已安装包的宇宙,然后检查我们的一个(或几个)包是否“有货”。在这个图书馆。没有必要为了找一根针而堆一堆干草堆。

这个答案也受到@ArtemKlevtsov的回答很好的启发,以类似的精神讨论这个问题的复制版本。他指出,如果包没有安装,system.file(package=x)可以有返回''的预期效果,否则返回nchar > 1

如果我们深入了解system.file是如何实现这一点的,我们可以看到它使用了一个不同的base函数find.package,我们可以直接使用:

# 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)

我们也可以在find.package中查看它是如何工作的,但这主要是一个有指导意义的练习——我看到的唯一简化函数的方法是跳过一些健壮性检查。但是基本的想法是:在.libPaths()中查找——任何已安装的包pkg都将在file.path(.libPaths(), pkg)处有一个DESCRIPTION文件,因此快速检查是file.exists(file.path(.libPaths(), pkg, 'DESCRIPTION')

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)
}

有一个新的包(我是一个共同开发人员),Require,它旨在成为可复制工作流的一部分,这意味着该函数在第一次运行或后续运行时产生相同的输出,即无论开始状态如何,最终状态都是相同的。下面安装所有缺失的包(我包含require = FALSE以严格解决最初的问题……通常我把这个设置为默认值,因为我通常希望它们加载到搜索路径)。

这两行位于我编写的每个脚本的顶部(根据需要调整包的选择),允许任何人在任何条件下(包括任何或所有依赖项缺失)使用脚本。

if (!require("Require")) install.packages("Require")
Require::Require(c("ggplot2", "Rcpp"), require = FALSE)

因此,您可以在脚本中使用它或将其传递给任何人。

今天,我偶然发现了rlang包提供的两个方便的函数,即is_installed()check_installed()

来自帮助页面(强调添加):

这些函数检查包的安装是否有最小的副作用。如果安装,包将被装载但不附加。

is_installed()不与用户交互。它只是返回TRUEFALSE,这取决于软件包是否安装。

交互式会话中,check_installed() 询问用户是否安装丢失的包。如果用户接受,则安装包[…]。如果会话是非交互式的,或者如果用户选择不安装包,则当前的计算将被中止。

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.

reprex包 (v2.0.1)创建于2022-03-25

站在@MichaelChirico的肩膀上:

stopifnot(3 == length(find.package(c('foo', 'bar', 'baz'))))