簡體   English   中英

檢查丟失的軟件包並安裝它們的優雅方法?

[英]Elegant way to check for missing packages and install them?

這些天,我似乎與合著者共享了很多代碼。 他們中的許多人是新手/中級 R 用戶,並且沒有意識到他們必須安裝他們還沒有的軟件包。

有沒有一種優雅的方式來調用installed.packages() ,將其與我正在加載和安裝的那些進行比較(如果丟失)?

是的。 如果您有軟件包列表,請將其與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)

除此以外:

如果你把你的代碼放在一個包中並使它們依賴,那么當你安裝你的包時它們會自動安裝。

Dason K. 和我有pacman包,可以很好地做到這一點。 包中的函數p_load就是這樣做的。 第一行只是為了確保安裝了 pacman。

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

您可以只使用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這樣做。

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

“ggplot2”是包。 它檢查包是否已安裝,如果未安裝,則安裝它。 然后,無論它采用哪個分支,它都會加載包。

上面的很多答案(以及這個問題的副本)都依賴於installed.packages ,這是錯誤的形式。 從文檔:

當安裝了數千個包時,這可能會很慢,所以不要使用它來確定是否安裝了命名包(使用 system.file 或 find.package),也不要使用它來確定包是否可用(調用 require 並檢查返回值)也找不到少量包的詳細信息(使用 packageDescription)。 它需要為每個安裝的包讀取多個文件,這在 Windows 和某些網絡安裝的文件系統上會很慢。

因此,更好的方法是嘗試使用require和 install 加載包,如果加載失敗(如果找不到,則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 ,而是只傳遞一次已卸載包的整個向量。

這是相同的功能,但帶有一個 Windows 對話框,詢問用戶是否要安裝丟失的軟件包

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

此解決方案將采用包名稱的字符向量並嘗試加載它們,或者在加載失敗時安裝它們。 它依賴於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" ) )

這里幾乎所有的答案都依賴於 (1) require()或 (2) installed.packages()來檢查給定的包是否已經安裝。

我正在添加一個答案,因為這些對於回答這個問題的輕量級方法來說並不令人滿意。

  • require具有加載包命名空間的副作用,這可能並不總是可取的
  • installed.packages是火箭筒光蠟燭-它會首先檢查安裝的軟件包的宇宙,然后我們檢查,如果我們的一個(或幾個)包(S)是“股票”在這個庫。 無需為了找到一根針而建造大海撈針。

這個答案也受到@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')

雖然巴蒂爾的答案是真的好,我的項目之一,我需要去掉輸出中的消息,警告並自動安裝軟件包。 我終於設法得到這個腳本:

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

使用packrat使共享庫完全相同並且不會改變其他人的環境。

在優雅和最佳實踐方面,我認為您從根本上走錯了路。 packrat包就是為這些問題而設計的。 它由 Hadley Wickham 的 RStudio 開發。 他們不必安裝依賴項並可能弄亂某人的環境系統, packrat使用自己的目錄並在那里安裝程序的所有依賴項,並且不會觸及某人的環境。

Packrat 是 R 的依賴管理系統。

R 包依賴性可能令人沮喪。 您是否曾經不得不使用試錯法來確定需要安裝哪些 R 包才能使其他人的代碼正常工作——然后將這些包永久安裝在全局范圍內,因為現在您不確定是否需要它們? 您是否曾經更新過一個包以使您的一個項目中的代碼工作,卻發現更新的包使另一個項目中的代碼停止工作?

我們構建了 Packrat 來解決這些問題。 使用 packrat 使您的 R 項目更多:

  • 隔離:為一個項目安裝一個新的或更新的包不會破壞你的其他項目,反之亦然。 那是因為 Packrat 為每個項目提供了自己的私有包庫。
  • 便攜:輕松地將您的項目從一台計算機傳輸到另一台計算機,甚至可以跨越不同的平台。 Packrat 可以輕松安裝項目所依賴的包。
  • 可重現:Packrat 會記錄您所依賴的確切軟件包版本,並確保您無論走到哪里都可以安裝這些確切版本。

https://rstudio.github.io/packrat/

這就是rbundler 包的目的:提供一種方法來控制為特定項目安裝的包。 現在,該包與 devtools 功能一起使用以將包安裝到您的項目目錄。 該功能類似於 Ruby 的bundler

如果您的項目是一個包(推薦),那么您所要做的就是加載 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

您可以簡單地使用setdiff函數獲取未安裝的包,然后安裝它們。 在下面的示例中,我們在安裝之前檢查ggplot2Rcpp包是否已安裝。

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

在一行中,上面可以寫成:

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

當然。

您需要將“已安裝的軟件包”與“所需的軟件包”進行比較。 這與我對CRANberries所做的非常接近,因為我需要將“存儲的已知包”與“當前已知的包”進行比較以確定新的和/或更新的包。

所以做類似的事情

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

要獲取所有已知包,模擬調用當前安裝的包並將其與給定的一組目標包進行比較。

下面的簡單函數就像一個魅力:

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

(不是我的,前段時間在網上找到了這個,從那時起就一直在使用它。不確定原始來源)

如果require("<package>")退出時出現包未找到錯誤,我使用以下函數來安裝包。 它將查詢 - CRAN 和 Bioconductor 存儲庫以查找丟失的包。

改編自 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)))
  }
}

例子:

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

PS: update.packages(ask = FALSE) & biocLite(character(), ask=FALSE)將更新系統上所有已安裝的軟件包。 這可能需要很長時間,並將其視為完整的 R 升級,但並非一直都值得!

即將推出的 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/

這似乎特別好地解決了 OP 最初的擔憂:

他們中的許多人是 R 新手/中級用戶,並沒有意識到他們必須安裝他們還沒有的軟件包。

我已經實現了靜默安裝和加載所需 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);

以為我會貢獻我使用的那個:

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

今天偶然發現了 rlang package 提供的兩個好用的 function ,即is_installed()check_installed()

幫助頁面(強調添加):

這些功能檢查安裝的軟件包是否具有最小的副作用。 如果安裝,包將被加載但不附加。

is_installed()不與用戶交互。 它只是根據是否安裝了軟件包返回TRUEFALSE

交互式會話中, check_installed()詢問用戶是否安裝缺少的軟件包 如果用戶接受,則安裝軟件包 [...]。 如果 session 是非交互式的,或者如果用戶選擇不安裝軟件包,則當前評估將中止。

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.

代表 package (v2.0.1) 於 2022 年 3 月 25 日創建

關於您的主要目標“安裝他們還沒有的庫。”並且無論使用“ instllaed.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 方式加載和安裝包。 require ("ggplot2") require ("Rcpp")

 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)
source("https://bioconductor.org/biocLite.R")
if (!require("ggsci")) biocLite("ggsci")

使用 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

我使用以下命令檢查是否安裝了包以及是否更新了依賴項,然后加載包。

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

這是我的代碼:

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)
library <- function(x){
  x = toString(substitute(x))
if(!require(x,character.only=TRUE)){
  install.packages(x)
  base::library(x,character.only=TRUE)
}}

這適用於不帶引號的包名並且相當優雅(參見 GeoObserver 的回答)

就我而言,我想要一個可以從命令行運行的單行代碼(實際上是通過 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 )。

讓我分享一點瘋狂:

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

有一個新的包(我是一個共同開發者), Require ,它旨在成為可重現工作流的一部分,這意味着該函數在第一次運行或后續時間時產生相同的輸出,即最終狀態無論起始狀態如何,都是相同的。 以下安裝任何丟失的包(我包括require = FALSE以嚴格解決原始問題......通常我將其保留為默認值,因為我通常希望它們加載到搜索路徑)。

這兩行位於我編寫的每個腳本的頂部(根據需要調整包選擇),允許任何人在任何情況下使用該腳本(包括缺少任何或所有依賴項)。

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

因此,您可以在腳本中使用它或將其傳遞給任何人。

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

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)

}

站在@MichaelChirico的肩膀上:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM