sirven - Actualizar un paquete R específico y sus dependencias
r paquetes instalados (2)
Tengo alrededor de 4000 paquetes R instalados en mi sistema (un servidor) y la mayoría de ellos están desactualizados porque fueron creados antes de R-3.0.0. Ahora sé
update.packages(checkBuilt=TRUE, ask=FALSE)
actualizaría todos mis paquetes pero eso es demasiado lento. La cosa es que los usuarios no usan la mayoría de los paquetes y de vez en cuando me piden que actualice un paquete (por ejemplo, campos) que usarían. Ahora si corro
install.packages("fields")
solo actualizaría los campos del paquete, pero no los mapas del paquete, incluso si los campos dependen de los mapas. Así cuando intento cargar los campos del paquete:
library("fields")
Me aparece un mensaje de error
Error: package ‘maps’ was built before R 3.0.0: please re-install it
¿Hay alguna forma de actualizar los campos para que también actualice automáticamente los campos de los paquetes?
Como Ben indicó en su comentario, debe obtener las dependencias para los fields
, luego filtrar los paquetes con Prioridad "Base"
o "Recommended"
, y luego pasar esa lista de paquetes a install.packages()
para tratar la instalación. Algo como:
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
}
Esto da:
R> instPkgPlusDeps("fields")
Loading required package: tools
[1] "fields" "spam" "maps"
que coincide con
> packageDescription("fields", fields = "Depends")
[1] "R (>= 2.13), methods, spam, maps"
Obtendrá advertencias de la línea sapply()
si una dependencia en deps
no está realmente instalada. Creo que estos son inofensivos ya que el valor devuelto en ese caso es NA
y lo usamos para indicar los paquetes que queremos instalar. Dudo que te afecte si tienes 4000 paquetes instalados.
El valor predeterminado es no instalar paquetes, sino simplemente devolver la lista de dependencias. Pensé que esto era lo más seguro, ya que es posible que no se dé cuenta de la cadena de dependencias implícitas y termine de instalar cientos de paquetes accidentalmente. Pase in install = TRUE
si está dispuesto a instalar los paquetes indicados.
Tenga en cuenta que restrinjo los tipos de dependencias buscadas (el globo de las cosas si utiliza el campo which = "most"
) tiene más de 300 dependencias de este tipo una vez que resuelve recursivamente esas dependencias (que también incluyen los campos Suggests:
. which = "all"
buscará todo, incluyendo Enhances:
que será una lista más grande de paquetes nuevamente. Vea ?tools::package_dependencies
para entradas válidas para el argumento which
.
Mi respuesta se basa en la respuesta de Gavin ... Tenga en cuenta que el póster original, user3175783, solicitó una versión más inteligente de update.packages()
. Esa función omite la instalación de paquetes que ya están actualizados. Pero la solución de Gavin instala un paquete y todas sus dependencias, estén actualizadas o no. Utilicé la punta de Gavin de omitir paquetes base (que en realidad no son instalables), y codifiqué una solución que también omite paquetes actualizados.
La función principal es installPackages()
. Esta función y sus ayudantes realizan una ordenación topológica del árbol de dependencias enraizado en un conjunto dado de paquetes. Los paquetes en la lista resultante se comprueban para comprobar su estabilidad y se instalan uno por uno. Aquí hay un ejemplo de salida:
> 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
Aquí está el código, perdón por la longitud:
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);
}