Обновление определенного пакета R и его зависимостей
У меня в моей системе (сервер) установлено около 4000 R пакетов, и большинство из них устарели, потому что они были созданы до R-3.0.0. Теперь я знаю
update.packages(checkBuilt=TRUE, ask=FALSE)
будет обновлять все мои пакеты, но это слишком медленно. Дело в том, что пользователи не используют большинство пакетов, и теперь они просят меня обновить пакет (скажем, поля), который они будут использовать. Теперь, если я запустил
install.packages("fields")
он будет обновлять поля пакета, но не карты пакетов, даже если поля зависят от карт. Таким образом, когда я пытаюсь загрузить поля пакета:
library("fields")
Я получаю сообщение об ошибке
Error: package ‘maps’ was built before R 3.0.0: please re-install it
Есть ли способ обновить поля, чтобы он автоматически обновлял поля пакетов, зависит от?
Ответы
Ответ 1
Как указал Бен в своем комментарии, вам нужно получить зависимости для fields
, а затем отфильтровать пакеты с Приоритетом "Base"
или "Recommended"
, а затем передать этот список пакетов в install.packages()
для решения установки. Что-то вроде:
instPkgPlusDeps <- function(pkg, install = FALSE,
which = c("Depends", "Imports", "LinkingTo"),
inc.pkg = TRUE) {
stopifnot(require("tools")) ## load tools
ap <- available.packages() ## takes a minute on first use
## get dependencies for pkg recursively through all dependencies
deps <- package_dependencies(pkg, db = ap, which = which, recursive = TRUE)
## the next line can generate warnings; I think these are harmless
## returns the Priority field. `NA` indicates not Base or Recommended
pri <- sapply(deps[[1]], packageDescription, fields = "Priority")
## filter out Base & Recommended pkgs - we want the `NA` entries
deps <- deps[[1]][is.na(pri)]
## install pkg too?
if (inc.pkg) {
deps = c(pkg, deps)
}
## are we installing?
if (install) {
install.packages(deps)
}
deps ## return dependencies
}
Это дает:
R> instPkgPlusDeps("fields")
Loading required package: tools
[1] "fields" "spam" "maps"
который соответствует
> packageDescription("fields", fields = "Depends")
[1] "R (>= 2.13), methods, spam, maps"
Вы получаете предупреждения из строки sapply()
, если зависимость в deps
на самом деле не установлена. Я думаю, что они безвредны, так как возвращаемое значение в этом случае NA
, и мы используем его для указания пакетов, которые мы хотим установить. Я сомневаюсь, что это повлияет на вас, если у вас установлено 4000 пакетов.
По умолчанию используется не, чтобы установить пакеты, но просто верните список зависимостей. Я полагал, что это было безопаснее, так как вы не понимаете, что подразумевается цепочка зависимостей и в конечном итоге устанавливают сотни пакетов случайно. Перейдите в install = TRUE
, если вы готовы установить указанные пакеты.
Обратите внимание, что я ограничиваю типы найденных зависимостей - вещи, если вы используете which = "most"
- поля, имеет более 300 таких зависимостей, как только вы рекурсивно разрешаете эти зависимости (включая также поля Suggests:
). which = "all"
будет искать все, в том числе Enhances:
, что будет большим списком пакетов. См. ?tools::package_dependencies
для допустимых входов для аргумента which
.
Ответ 2
Мой ответ основывается на ответе Гэвина... Обратите внимание, что оригинальный плакат user3175783 попросил более интеллектуальную версию update.packages()
. Эта функция пропускает установку уже обновленных пакетов. Но решение Gavin устанавливает пакет и все его зависимости, независимо от того, являются ли они актуальными или нет. Я использовал подсказку Gavin о пропусках базовых пакетов (которые на самом деле не устанавливаются) и закодировал решение, которое также пропускает обновленные пакеты.
Основная функция installPackages()
. Эта функция и ее помощники выполняют топологическую сортировку дерева зависимостей, внедренного в определенный набор пакетов. Пакеты в результирующем списке проверяются на постоянство и устанавливаются один за другим. Вот пример вывода:
> remove.packages("tibble")
Removing package from ‘/home/frederik/.local/lib/x86_64/R/packages’
(as ‘lib’ is unspecified)
> installPackages(c("ggplot2","stringr","Rcpp"), dry_run=T)
## Package digest is out of date ( 0.6.9 < 0.6.10 )
Would have installed package digest
## Package gtable is up to date ( 0.2.0 )
## Package MASS is up to date ( 7.3.45 )
## Package Rcpp is out of date ( 0.12.5 < 0.12.8 )
Would have installed package Rcpp
## Package plyr is out of date ( 1.8.3 < 1.8.4 )
Would have installed package plyr
## Package stringi is out of date ( 1.0.1 < 1.1.2 )
Would have installed package stringi
## Package magrittr is up to date ( 1.5 )
## Package stringr is out of date ( 1.0.0 < 1.1.0 )
Would have installed package stringr
...
## Package lazyeval is out of date ( 0.1.10 < 0.2.0 )
Would have installed package lazyeval
## Package tibble is not currently installed, installing
Would have installed package tibble
## Package ggplot2 is out of date ( 2.1.0 < 2.2.0 )
Would have installed package ggplot2
Вот код, извините за длину:
library(tools)
# Helper: a "functional" interface depth-first-search
fdfs = function(get.children) {
rec = function(root) {
cs = get.children(root);
out = c();
for(c in cs) {
l = rec(c);
out = c(out, setdiff(l, out));
}
c(out, root);
}
rec
}
# Entries in the package "Priority" field which indicate the
# package can't be upgraded. Not sure why we would exclude
# recommended packages, since they can be upgraded...
#excl_prio = c("base","recommended")
excl_prio = c("base")
# Find the non-"base" dependencies of a package.
nonBaseDeps = function(packages,
ap=available.packages(),
ip=installed.packages(), recursive=T) {
stopifnot(is.character(packages));
all_deps = c();
for(p in packages) {
# Get package dependencies. Note we are ignoring version
# information
deps = package_dependencies(p, db = ap, recursive = recursive)[[1]];
ipdeps = match(deps,ip[,"Package"])
# We want dependencies which are either not installed, or not part
# of Base (e.g. not installed with R)
deps = deps[is.na(ipdeps) | !(ip[ipdeps,"Priority"] %in% excl_prio)];
# Now check that these are in the "available.packages()" database
apdeps = match(deps,ap[,"Package"])
notfound = is.na(apdeps)
if(any(notfound)) {
notfound=deps[notfound]
stop("Package ",p," has dependencies not in database: ",paste(notfound,collapse=" "));
}
all_deps = union(deps,all_deps);
}
all_deps
}
# Return a topologically-sorted list of dependencies for a given list
# of packages. The output vector contains the "packages" argument, and
# recursive dependencies, with each dependency occurring before any
# package depending on it.
packageOrderedDeps = function(packages, ap=available.packages()) {
# get ordered dependencies
odeps = sapply(packages,
fdfs(function(p){nonBaseDeps(p,ap=ap,recursive=F)}))
# "unique" preserves the order of its input
odeps = unique(unlist(odeps));
# sanity checks
stopifnot(length(setdiff(packages,odeps))==0);
seen = list();
for(d in odeps) {
ddeps = nonBaseDeps(d,ap=ap,recursive=F)
stopifnot(all(ddeps %in% seen));
seen = c(seen,d);
}
as.vector(odeps)
}
# Checks if a package is up-to-date.
isPackageCurrent = function(p,
ap=available.packages(),
ip=installed.packages(),
verbose=T) {
if(verbose) msg = function(...) cat("## ",...)
else msg = function(...) NULL;
aprow = match(p, ap[,"Package"]);
iprow = match(p, ip[,"Package"]);
if(!is.na(iprow) && (ip[iprow,"Priority"] %in% excl_prio)) {
msg("Package ",p," is a ",ip[iprow,"Priority"]," package\n");
return(T);
}
if(is.na(aprow)) {
stop("Couldn't find package ",p," among available packages");
}
if(is.na(iprow)) {
msg("Package ",p," is not currently installed, installing\n");
F;
} else {
iv = package_version(ip[iprow,"Version"]);
av = package_version(ap[aprow,"Version"]);
if(iv < av) {
msg("Package ",p," is out of date (",
as.character(iv),"<",as.character(av),")\n");
F;
} else {
msg("Package ",p," is up to date (",
as.character(iv),")\n");
T;
}
}
}
# Like install.packages, but skips packages which are already
# up-to-date. Specify dry_run=T to just see what would be done.
installPackages =
function(packages,
ap=available.packages(), dry_run=F,
want_deps=T) {
stopifnot(is.character(packages));
ap=tools:::.remove_stale_dups(ap)
ip=installed.packages();
ip=tools:::.remove_stale_dups(ip)
if(want_deps) {
packages = packageOrderedDeps(packages, ap);
}
for(p in packages) {
curr = isPackageCurrent(p,ap,ip);
if(!curr) {
if(dry_run) {
cat("Would have installed package ",p,"\n");
} else {
install.packages(p,dependencies=F);
}
}
}
}
# Convenience function to make sure all the libraries we have loaded
# in the current R session are up-to-date (and to update them if they
# are not)
updateAttachedLibraries = function(dry_run=F) {
s=search();
s=s[grep("^package:",s)];
s=gsub("^package:","",s)
installPackages(s,dry_run=dry_run);
}