Comment inclure le script R (source) dans d'autres scripts

108

J'ai créé un script utilitaire R, util.R, que je souhaite utiliser à partir d'autres scripts de mon projet. Quelle est la bonne façon de garantir que la fonction définie par ce script est disponible pour fonctionner dans mes autres scripts?

Je recherche quelque chose de similaire à la requirefonction, qui ne charge un package que s'il n'a pas encore été chargé. Je ne veux pas appeler source("util.R")car cela chargera le script à chaque fois qu'il sera appelé.

Je sais que j'obtiendrai des réponses me disant de créer un package, comme dans Organizing R Source Code :) Mais je ne crée pas quelque chose qui sera utilisé ailleurs, c'est juste un projet autonome.

rafalotufo
la source
37
Je crée tout le temps des packages pour des projets autonomes. Ce n'est pas beaucoup de travail et les avantages sont énormes. Allez, vous savez que vous voulez le faire ...
Andrie

Réponses:

93

Voici un moyen possible. Utilisez la existsfonction pour rechercher quelque chose d'unique dans votre util.Rcode.

Par exemple:

if(!exists("foo", mode="function")) source("util.R")

(Modifié pour inclure mode="function", comme Gavin Simpson l'a souligné)

Andrie
la source
4
Bonne utilisation de exists()- doit être mode = "function"ajouté pour le rendre infaillible
Gavin Simpson
1
exists()semble lancer une erreur sauf d'en renvoyer une dans R 3.0.2.
Michael Schubert
L'utilisation correcte est `existe (" foo ") et la réponse a été modifiée.
Andrie
18

Il n'y a pas une telle chose intégrée, puisque R ne suit pas les appels vers sourceet n'est pas capable de déterminer ce qui a été chargé à partir d'où (ce n'est pas le cas lors de l'utilisation de packages). Pourtant, vous pouvez utiliser la même idée que dans les .hfichiers C , c'est-à-dire envelopper le tout dans:

if(!exists('util_R')){
 util_R<-T

 #Code

}
mbq
la source
puis appelez source("util.R")dans le ifcode, non?
rafalotufo
1
@rafalotufo Vous seriez source ("util.R") comme d'habitude. Le code dans le post de mbq irait dans util.R. Vous venez de mettre le corps entier de ce qui est dans util.R en ce moment dans une instruction if () géante, si cela a du sens.
Keith Twombley
10

Say util.Rproduit une fonction foo(). Vous pouvez vérifier si cette fonction est disponible dans l'environnement global et générer le script si ce n'est pas le cas:

if(identical(length(ls(pattern = "^foo$")), 0))
    source("util.R")

Cela trouvera n'importe quoi avec le nom foo. Si vous voulez trouver une fonction, alors (comme mentionné par @Andrie) exists()est utile mais doit être dit exactement quel type d'objet rechercher, par exemple

if(exists("foo", mode = "function"))
    source("util.R")

Voici exists()en action:

> exists("foo", mode = "function")
[1] FALSE
> foo <- function(x) x
> exists("foo", mode = "function")
[1] TRUE
> rm(foo)
> foo <- 1:10
> exists("foo", mode = "function")
[1] FALSE
Gavin Simpson
la source
Dans ce cas, vous voudrez peut-être utiliser grepl(..., value=TRUE)parce que votre terme de recherche n'est probablement pas une expression régulière. +1, au fait.
Andrie
?? grepl()n'a pas d'argument value, mais je devrais probablement corriger l'expression rationnelle dans ls()...
Gavin Simpson
Désolé, mon erreur. Je voulais direfixed=TRUE
Andrie
@Andrie - Ah, d'accord. Cela n'a pas fonctionné de toute façon. J'ai été traîné en réfléchissant à cela. exists()c'est mieux mais je vois maintenant que vous avez posté une telle réponse entre-temps.
Gavin Simpson
5

Vous pouvez écrire une fonction qui prend un nom de fichier et un nom d'environnement, vérifie si le fichier a été chargé dans l'environnement et utilise sys.sourcepour trouver le fichier sinon.

Voici une fonction rapide et non testée (améliorations bienvenues!):

include <- function(file, env) {
  # ensure file and env are provided
  if(missing(file) || missing(env))
    stop("'file' and 'env' must be provided")
  # ensure env is character
  if(!is.character(file) || !is.character(env))
    stop("'file' and 'env' must be a character")

  # see if env is attached to the search path
  if(env %in% search()) {
    ENV <- get(env)
    files <- get(".files",ENV)
    # if the file hasn't been loaded
    if(!(file %in% files)) {
      sys.source(file, ENV)                        # load the file
      assign(".files", c(file, files), envir=ENV)  # set the flag
    }
  } else {
    ENV <- attach(NULL, name=env)      # create/attach new environment
    sys.source(file, ENV)              # load the file
    assign(".files", file, envir=ENV)  # set the flag
  }
}
Joshua Ulrich
la source
5

Voici une fonction que j'ai écrite. Il encapsule la base::sourcefonction pour stocker une liste de fichiers source dans une liste d'environnement global nommée sourced. Il ne re-source un fichier que si vous fournissez un .force=TRUEargument à l'appel à la source. Sa signature d'argument est par ailleurs identique au réel source(), vous n'avez donc pas besoin de réécrire vos scripts pour l'utiliser.

warning("overriding source with my own function FYI")
source <- function(path, .force=FALSE, ...) {
  library(tools)
  path <- tryCatch(normalizePath(path), error=function(e) path)
  m<-md5sum(path)

  go<-TRUE
  if (!is.vector(.GlobalEnv$sourced)) {
    .GlobalEnv$sourced <- list()
  }
  if(! is.null(.GlobalEnv$sourced[[path]])) {
    if(m == .GlobalEnv$sourced[[path]]) {
      message(sprintf("Not re-sourcing %s. Override with:\n  source('%s', .force=TRUE)", path, path))
      go<-FALSE
    }
    else {
      message(sprintf('re-sourcing %s as it has changed from: %s to: %s', path, .GlobalEnv$sourced[[path]], m))
      go<-TRUE
    }
  } 
  if(.force) {
    go<-TRUE
    message("  ...forcing.")
  }
  if(go) {
    message(sprintf("sourcing %s", path))
    .GlobalEnv$sourced[path] <- m
    base::source(path, ...)
  }
}

C'est assez bavard (beaucoup d'appels à message()), vous pouvez donc supprimer ces lignes si vous vous en souciez. Tout conseil des utilisateurs vétérans de R est apprécié; Je suis assez nouveau chez R.

Keith Twombley
la source
0

J'ai résolu mon problème en utilisant l'adresse entière où mon code est: Avant:

if(!exists("foo", mode="function")) source("utils.r")

Après:

if(!exists("foo", mode="function")) source("C:/tests/utils.r")
José Roberto Ribeiro Filho
la source