So fügen Sie (Quell-) R-Skript in andere Skripte ein


108

Ich habe ein Dienstprogramm-R-Skript, util.R, erstellt, das ich aus anderen Skripten in meinem Projekt verwenden möchte. Wie kann sichergestellt werden, dass die in diesem Skript definierte Funktion für meine anderen Skripte verfügbar ist?

Ich suche etwas Ähnliches wie die requireFunktion, die ein Paket nur lädt, wenn es noch nicht geladen wurde. Ich möchte nicht aufrufen, source("util.R")da dadurch das Skript bei jedem Aufruf geladen wird.

Ich weiß, dass ich einige Antworten erhalten werde, die mich auffordern, ein Paket zu erstellen, wie in Organisieren von R-Quellcode :) Aber ich erstelle nichts, das an anderer Stelle verwendet wird, es ist nur ein eigenständiges Projekt.


37
Ich erstelle ständig Pakete für eigenständige Projekte. Es ist nicht viel Arbeit und die Vorteile sind enorm.
Weiter

Antworten:


93

Hier ist ein möglicher Weg. Verwenden Sie die existsFunktion, um nach etwas Einzigartigem in Ihrem util.RCode zu suchen.

Beispielsweise:

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

(Bearbeitet, um aufzunehmen mode="function", wie Gavin Simpson betonte)


4
Gute Verwendung von exists()- muss mode = "function"hinzugefügt werden, um es narrensicher zu machen
Gavin Simpson

1
exists()scheint einen Fehler auszulösen, außer dass in R 3.0.2 ein Fehler zurückgegeben wird.
Michael Schubert

Die korrekte Verwendung ist `existiert (" foo ") und die Antwort wurde bearbeitet.
Andrie

18

Es ist so etwas nicht eingebaut, da R keine Anrufe an verfolgt sourceund nicht herausfinden kann, was von wo geladen wurde (dies ist bei Verwendung von Paketen nicht der Fall). Sie können jedoch dieselbe Idee wie in C- .hDateien verwenden, dh das Ganze einpacken in:

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

 #Code

}

und dann source("util.R")innerhalb des ifCodes anrufen , oder?
Rafalotufo

1
@rafalotufo Du würdest wie gewohnt beschaffen ("util.R"). Der Code in mbqs Post würde in util.R. Sie setzen einfach den gesamten Körper dessen, was gerade in util.R enthalten ist, in eine riesige if () - Anweisung, wenn dies sinnvoll ist.
Keith Twombley

10

Say util.Rerzeugt eine Funktion foo(). Sie können überprüfen, ob diese Funktion in der globalen Umgebung verfügbar ist, und das Skript als Quelle verwenden, wenn dies nicht der Fall ist:

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

Das wird alles mit dem Namen finden foo. Wenn Sie eine Funktion finden möchten, ist dies (wie von @Andrie erwähnt) exists()hilfreich, es muss jedoch genau angegeben werden, nach welcher Art von Objekt gesucht werden soll, z

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

Hier ist exists()in Aktion:

> 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

In diesem Fall möchten Sie möglicherweise verwenden, grepl(..., value=TRUE)da Ihr Suchbegriff wahrscheinlich kein regulärer Ausdruck ist. +1 übrigens.
Andrie

?? grepl()hat kein Argument value, aber ich sollte wahrscheinlich den regulären Ausdruck in ls()...
Gavin Simpson

Entschuldigung, mein Fehler. Ich meintefixed=TRUE
Andrie

@ Andrea - Ah, OK. Es hat sowieso nicht funktioniert. Wurde weggeschleppt, während ich darüber nachdachte. exists()ist besser, aber ich sehe jetzt, dass Sie in der Zwischenzeit eine solche Antwort gepostet haben.
Gavin Simpson

5

Sie können eine Funktion schreiben, die einen Dateinamen und einen Umgebungsnamen verwendet, prüft, ob die Datei in die Umgebung geladen wurde, und verwendet, sys.sourceum die Datei als Quelle zu verwenden , wenn nicht.

Hier ist eine schnelle und ungetestete Funktion (Verbesserungen willkommen!):

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
  }
}

5

Hier ist eine Funktion, die ich geschrieben habe. Es umschließt die base::sourceFunktion zum Speichern einer Liste von Quelldateien in einer globalen Umgebungsliste mit dem Namen sourced. Eine Datei wird nur dann erneut als Quelle bereitgestellt, wenn Sie ein .force=TRUEArgument für den Aufruf der Quelle angeben. Die Signatur des Arguments ist ansonsten identisch mit der tatsächlichen Signatur, source()sodass Sie Ihre Skripte nicht neu schreiben müssen, um dies zu verwenden.

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, ...)
  }
}

Es ist ziemlich gesprächig (viele Anrufe bei message()), so dass Sie diese Zeilen herausnehmen können, wenn Sie sich interessieren. Jeder Rat von erfahrenen R-Benutzern wird geschätzt; Ich bin ziemlich neu bei R.


0

Ich habe mein Problem mit der gesamten Adresse gelöst, an der mein Code lautet: Vorher:

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

Nach dem:

if(!exists("foo", mode="function")) source("C:/tests/utils.r")
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.