Функция R для установки недостающих пакетов

Для одного из моих сценариев я хочу написать функцию R, которая проверяет, установлен ли пакет: если это так, он должен использовать library() для импорта его в пространство имен, в противном случае он должен установить его и импортировать.

Я предположил, что pkgname является строкой и попытался написать что-то вроде:

ensure_library <- function(pkgname) {
  if (!require(pkgname)) {
    install.packages(pkgname, dependencies = TRUE)
  }
  require(pkgname)
}

Так же просто, как и эта функция не работает. Если я попытаюсь запустить его как ensure_library("dplyr") он устанавливает пакет dplyr, но затем терпит неудачу, потому что пытается импортировать pkgname скорее, чем dplyr в пространстве имен.

ensure_library("dplyr")
Loading required package: pkgname
Installing package into ‘/home/luca/R-dev’
(as ‘lib’ is unspecified)
trying URL 'https://cran.rstudio.com/src/contrib/dplyr_0.5.0.tar.gz'
Content type 'application/x-gzip' length 708476 bytes (691 KB)
==================================================
downloaded 691 KB

* installing *source* package ‘dplyr’ ...
** package ‘dplyr’ successfully unpacked and MD5 sums checked
** libs

.... a lot of compiling here....

installing to /home/luca/R-dev/dplyr/libs
** R
** data
*** moving datasets to lazyload DB
** inst
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded
* DONE (dplyr)

The downloaded source packages are in
    ‘/tmp/Rtmpfd2Lep/downloaded_packages’
Loading required package: pkgname
Warning messages:
1: In library(package, lib.loc = lib.loc, character.only = TRUE, logical.return = TRUE,  :
  there is no package called ‘pkgname’
2: In library(package, lib.loc = lib.loc, character.only = TRUE, logical.return = TRUE,  :
  there is no package called ‘pkgname’

Кроме того, если я сейчас перезапущу его, он установит dplyr снова.

Я понимаю, что это, вероятно, из-за R-нестандартной оценки, и я попробовал несколько комбинаций eval / replace/quote, чтобы заставить его работать с require но я не смог добиться успеха.

Может ли кто-нибудь помочь мне понять, что происходит, и если есть какие-то легко исправить?

Если функция, уже реализующая это, существует, я хотел бы знать, но что меня действительно интересует, так это понимание того, почему мой код работает не так, как задумано.

2 ответа

Решение

Расширение предложения по использованию character.only=TRUE: Если вы посмотрите на код для requireВы видите, что первый шаг выполняется только тогда, когда по умолчанию установлено значение "character.only" (= FALSE) содержит:

> require
function (package, lib.loc = NULL, quietly = FALSE, warn.conflicts = TRUE, 
    character.only = FALSE) 
{
    if (!character.only) 
        package <- as.character(substitute(package))
    loaded <- paste("package", package, sep = ":") %in% search()
    if (!loaded) {
        if (!quietly) 
            packageStartupMessage(gettextf("Loading required package: %s", 
                package), domain = NA)
        value <- tryCatch(library(package, lib.loc = lib.loc, 
            character.only = TRUE, logical.return = TRUE, warn.conflicts = warn.conflicts, 

# snipped rest of code

Таким образом, оставляя значение по умолчанию для character.only на месте, вынуждает функцию конвертировать символ pkgname на символьное значение.

  as.character(substitute(pkgname))
 [1] "pkgname"

И так как "character.only" также является частью library логика и требовать звонки libraryВы могли бы использовать library,

Дальнейший комментарий: Вы опубликовали продолжение к Релпу и получили несколько полезных ответов от Дункана Мердока и Питера Далгаарда, которые прояснили (я надеюсь) этот вопрос. В процессе я задавался вопросом, происходит ли ваше сопротивление этому ответу из-за ожидания, заданного именем этой функции, что должно произойти замещение, но ничего не происходило, что выглядело как "замещение". Это ожидание кажется вполне обоснованным, и я вижу его с запозданием в ретроспективе. Я думаю, что правильное имя функции могло бы быть: substitute_but_only_on_the_basis_of_the_local_environment_or_second_argument. Более распространенное использование substitute с двумя аргументами:

   y_val=45; a_val=99
   substitute( x + y == z + a , list( y= y_val, a = a_val)
   x + 45 == z + 99

Не было предпринято никаких попыток проверить значения любого символа в первом аргументе, если только у него не было именованного элемента во втором аргументе (который называется env.)

Приведенные выше предложения уже хороши и могут решить вашу проблему. Тем не менее, вы заново изобретаете колесо.

Если вы хотите распространять код R с документацией, которая содержит требования к внешним пакетам и, возможно, нуждается в надлежащем тестировании, я бы посоветовал вам сделать пакет из него. Когда пакет устанавливается, он автоматически обеспечивает доступность всех зависимостей. Кроме того, у вас есть документация и место для ваших сценариев тестирования. Он хранит все в одном месте и в то же время поддерживается версия.

Другие вопросы по тегам