Элегантный способ проверить отсутствующие пакеты и установить их?

260

Кажется, в наши дни я использую много кода с соавторами. Многие из них являются новичками/промежуточными пользователями R и не понимают, что им приходится устанавливать пакеты, которые у них еще нет.

Есть ли элегантный способ вызвать installed.packages(), сравнить его с теми, которые я загружаю и устанавливаю, если они отсутствуют?

  • 1
    @krlmlr А как насчет принятого ответа устарела и требует доработки? Это работает для меня (для нескольких быстрых тестов) под R version 3.0.2 (2013-09-25) x86_64-w64-mingw32/x64 (64-bit) .
  • 1
    @BrianDiggs: По крайней мере, три пакета, которые решают эту проблему, только один упоминается ниже. Есть ли еще больше - вот в чем вопрос.
Показать ещё 2 комментария
Теги:
packages
r-faq

26 ответов

220
Лучший ответ

Да. Если у вас есть список пакетов, сравните его с выходом из 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)

В противном случае:

Если вы поместите свой код в пакет и сделаете его зависимыми, тогда они будут автоматически установлены при установке вашего пакета.

  • 6
    Я думаю, что правильный синтаксис: if(length(new.packages)>0) {install.packages(new.packages)}
  • 4
    @psql, Shine прав, поскольку «> 0» является «неявным» в условии if. запустите это для проверки: new.packages <- c(1,2) length(new.packages) if(length(new.packages)){print("hello!")}else{print("oh no!")}
Показать ещё 5 комментариев
178

Dason K. и у меня есть пакет pacman, который может сделать это красиво. Функция p_load в пакете делает это. Первая строка предназначена только для обеспечения установки pacman.

if (!require("pacman")) install.packages("pacman")
pacman::p_load(package1, package2, package_n)
  • 14
    Это похоже на один удобный пакет там. Каждый должен рассказать об этом своим друзьям!
  • 1
    Каков статус пакета? Я не вижу в C-RAN.
Показать ещё 10 комментариев
51

Вы можете просто использовать возвращаемое значение 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.

  • 3
    Вы можете значительно упростить свою жизнь, используя character.only = TRUE в require , но тогда, я думаю, ничто не сможет отличить ваш ответ от моего.
  • 1
    Спасибо @Livius. Очень понравился трюк eval .
16

Это решение примет вектор символа имен пакетов и попытается их загрузить или установить, если загрузка не удалась. Он полагается на поведение возврата 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" ) )
  • 0
    Разве вы не хотите звонить require снова после установки?
  • 0
    @krlmlr Нет, потому что для оценки оператора if он должен сначала оценить require , побочным эффектом которого является загрузка пакета, если он доступен!
Показать ещё 4 комментария
15

Хотя ответ Шейна действительно хорош, для одного из моих проектов мне нужно было удалить сообщения, предупреждения и установить пакеты автоматически. Мне наконец удалось получить этот script:

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)
    }
}
12
if (!require('ggplot2')) install.packages('ggplot2'); library('ggplot2')

"ggplot2" - это пакет. Он проверяет, установлен ли пакет, если он не устанавливает его. Затем он загружает пакет независимо от того, какая ветка потребовалась.

11

Многие ответы выше (и на дубликаты этого вопроса) полагаются на installed.packages, который является плохим. Из документации:

Это может быть медленным, когда установлены тысячи пакетов, поэтому не используйте это, чтобы узнать, установлен ли именованный пакет (используйте файл system.file или find.package), а также выяснить, можно ли использовать пакет (требуется вызов и проверить возвращаемое значение), а также не найти детали небольшого количества пакетов (используйте 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)
        }
    }
}
  • 0
    Это довольно элегантный способ, намного лучше, чем принятый. Я включу это в мою личную библиотеку. Благодарю.
8
# 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)
6

Это цель пакета 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.

5

Конечно.

Вам нужно сравнить "установленные пакеты" с "желаемыми пакетами". Это очень близко к тому, что я делаю с CRANberries, поскольку мне нужно сравнить "сохраненные известные пакеты" с "известными в настоящее время пакетами" для определения новых и/или обновленные пакеты.

Итак, сделайте что-нибудь вроде

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

чтобы получить все известные пакеты, имитировать вызов установленных пакетов и сравнить их с заданным набором целевых пакетов.

4

Вы можете просто использовать функцию setdiff, чтобы получить пакеты, которые не установлены, а затем установить их. В приведенном ниже примере мы проверяем, установлены ли пакеты ggplot2 и Rcpp, прежде чем устанавливать их.

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

В одной строке вышесказанное может быть записано как:

install.packages(setdiff(c("ggplot2", "Rcpp"), rownames(installed.packages())))
  • 0
    Я использую тот же подход. Мы также можем использовать rownames(installed.packages()) installed.packages()[,'Package'] вместо rownames(installed.packages()) .
4

Я использую следующую функцию для установки пакета, если require("<package>") завершает работу с ошибкой пакета. Он будет запрашивать как хранилища CRAN, так и Bioconductor для отсутствия пакета.

Адаптировано из оригинальной работы Джошуа Вили, 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, которое не может быть гарантировано все время!

  • 0
    Это можно улучшить, проверив, действительно ли пакеты доступны на кране или bc. Также он должен использовать библиотеку в конце, чтобы выдать ошибку, если установка не удалась или пакет не существует. Смотрите мою улучшенную версию loadpack() на raw.githubusercontent.com/holgerbrandl/datautils/master/R/…
4

Следующая простая функция работает как шарм:

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

(не мой, нашел это в Интернете некоторое время назад и использовал его с тех пор, не уверен в исходном источнике)

3

Используйте packrat, чтобы общие библиотеки были одинаковыми и не меняли другую среду.

С точки зрения элегантности и передовой практики, я думаю, что вы в корне идете по этому пути неправильно. пакет packrat был разработан для этих проблем. Он разработан RStudio Хэдли Уикхэмом. Вместо того, чтобы устанавливать зависимостей и, возможно, испортить кому-то систему окружения, packrat использует свой собственный каталог и устанавливает все зависимости для ваших программ в своих и не затрагивает кого-то среду.

Packrat - это система управления зависимостями для R.

Зависимости пакетов R могут быть разочаровывающими. Вам когда-нибудь приходилось использовать пробную версию и ошибки, чтобы выяснить, какие пакеты R вам нужно установить, чтобы заставить кого-то работать с кодом elses, а затем остался с этими пакетами, установленными на глобальном уровне навсегда, потому что теперь вы не уверены, нужны ли они вам? Вы когда-нибудь обновляли пакет, чтобы получить код в одном из ваших проектов, чтобы работать, только чтобы найти, что обновленный пакет заставляет код работать в другом проекте?

Мы создали пакет для решения этих проблем. Используйте packrat, чтобы увеличить ваши проекты R:

  • Изолированные. Установка нового или обновленного пакета для одного проекта не приведет к разрыву ваших других проектов и наоборот. Thats, потому что packrat дает каждому проекту свою собственную библиотеку частных пакетов.
  • Portable: легко переносите свои проекты с одного компьютера на другой, даже на разных платформах. Packrat упрощает установку пакетов, от которых зависит ваш проект.
  • Воспроизводимые: Packrat записывает точные версии пакетов, на которые вы полагаетесь, и гарантирует, что те точные версии будут теми, которые будут установлены везде, где бы вы ни находились.

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

3

Я реализовал функцию для установки и загрузки требуемых 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);
2

Используя семейный подход и анонимную функцию, вы можете:

  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 
    
2

Думаю, что я внес бы то, что я использую:

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

Довольно простой.

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

Относительно вашей основной цели "установить библиотеки, которые у них еще нет" и независимо от использования "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" )

  • 0
    Если вам больше не нравится ваш ответ, не разрушайте его - просто удалите его.
  • 0
    Ну, я пытался, но я не мог. Я думаю, что мое расширение FF для NoScript отключает его, или у меня нет прав и прав на удаление моего собственного ответа. Но я думаю, что Ливиус довольно близок к моему ответу, подумал без маскировки. Спасибо Михаилу Петротте. для уведомления.
Показать ещё 1 комментарий
1
source("https://bioconductor.org/biocLite.R")
if (!require("ggsci")) biocLite("ggsci")
1
library <- function(x){
  x = toString(substitute(x))
if(!require(x,character.only=TRUE)){
  install.packages(x)
  base::library(x,character.only=TRUE)
}}

Это работает с именами без кавычек и довольно элегантно (см. ответ GeoObserver)

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

Вот мой код для него:

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

Я использую следующее, которое будет проверять, установлен ли пакет и обновлены зависимости, а затем загружает пакет.

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

В моем случае я хотел один лайнер, который я мог запустить из командной строки (на самом деле через Makefile). Вот пример установки "VGAM" и "Перо", если они еще не установлены:

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 параметр repos (чтобы избежать всплывающих окон, спрашивающих об использовании зеркала)
  • Я не пытаюсь определить функцию, которая будет использоваться в другом месте

Также обратите внимание на важный character.only=TRUE (без него require попытается загрузить пакет p).

-2

попробуйте следующее:

if (!require(MyDesiredLibrary)) {
   install.packages("MyDesiredLibrary")
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню