Mettre à jour un paquet R spécifique et ses dépendances

j'ai environ 4000 paquets R installés dans mon système (un serveur) et la plupart d'entre eux sont obsolètes parce qu'ils ont été construits avant R-3.0.0. Maintenant je sais

update.packages(checkBuilt=TRUE, ask=FALSE)

mettrait à jour tous mes paquets, mais c'est trop lent. Le truc c'est que les utilisateurs n'utilisent pas la plupart des paquets et de temps en temps ils me demandent de mettre à jour un paquet (disons des champs) qu'ils utiliseraient. Maintenant si je cours

install.packages("fields")

il ne ferait que mettre à jour les champs du paquet mais pas le paquet cartes même si les champs dépendent de cartes. Ainsi, lorsque j'essaie de charger les champs du paquet:

library("fields")

j'ai un message d'erreur

Error: package ‘maps’ was built before R 3.0.0: please re-install it

y a-t-il un moyen de mettre à jour les champs afin qu'ils mettent automatiquement à jour les champs des paquets dont dépend?

27
demandé sur Gavin Simpson 2014-01-09 06:40:38

2 réponses

comme Ben l'a indiqué dans son commentaire, vous devez obtenir les dépendances pour fields , puis filtrer les paquets avec la priorité "Base" ou "Recommended" , puis passer cette liste de paquets à install.packages() pour traiter de l'installation. Quelque chose comme:

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
}

Cela donne:

R> instPkgPlusDeps("fields")
Loading required package: tools
[1] "fields" "spam"   "maps"

qui correspond à

> packageDescription("fields", fields = "Depends")
[1] "R (>= 2.13), methods, spam, maps"

vous obtenez des avertissements de la ligne sapply() 1519240920" si une dépendance dans deps n'est pas réellement installée. Je pense qu'ils sont inoffensifs car la valeur retournée dans ce cas est NA et nous l'utilisons pour indiquer les paquets que nous voulons installer. Je doute que cela vous affecte si vous avez 4000 paquets installés.

la valeur par défaut est pas pour installer les paquets, mais juste retourner la liste des dépendances. J'ai pensé que c'était plus sûr car vous ne pouvez pas vous rendre compte de la chaîne de dépendances implicite et finir par installer des centaines de paquets accidentellement. Passer dans install = TRUE si vous êtes heureux d'installer les paquets indiqués.

notez que je limite les types de dépendances recherchés - objets ballon si vous utilisez which = "most" - champs a plus de 300 de ces dépendances une fois que vous résolvez de façon récursive ces dépendances (qui incluent Suggests: champs aussi). which = "all" pour tout, y compris Enhances: qui sera une plus grande liste de paquets. Voir ?tools::package_dependencies pour les entrées valides pour l'argument which .

10
répondu Gavin Simpson 2014-01-10 21:35:58

ma réponse s'appuie sur celle de Gavin... Notez que l'affiche originale, user3175783, demandait une version plus intelligente de update.packages() . Cette fonction évite d'installer des paquets qui sont déjà à jour. Mais Gavin solution installe un paquet et toutes ses dépendances, qu'ils sont à jour ou non. J'ai utilisé la pointe de Gavin de sauter des paquets de base (qui ne sont pas réellement installables), et j'ai codé une solution qui évite également les paquets à jour.

le la fonction principale est installPackages() . Cette fonction et ses assistants exécutent une sorte d'arbre de dépendance topologique enraciné dans un ensemble donné de paquets. Les paquets de la liste résultante sont vérifiés pour la staleness et installés un par un. Voici un exemple de sortie:

> 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 

voici le code, désolé pour la longueur:

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);
}
5
répondu Metamorphic 2016-11-27 06:34:19