Jak dołączyć (źródłowy) skrypt R do innych skryptów


108

Stworzyłem skrypt narzędzia R, util.R, którego chcę używać z innych skryptów w moim projekcie. Jaki jest właściwy sposób, aby zapewnić, że funkcja zdefiniowana w tym skrypcie będzie działać w innych moich skryptach?

Szukam czegoś podobnego do requirefunkcji, która ładuje paczkę tylko wtedy, gdy nie została jeszcze załadowana. Nie chcę dzwonić, source("util.R")ponieważ spowoduje to załadowanie skryptu za każdym razem, gdy zostanie wywołany.

Wiem, że dostanę kilka odpowiedzi, które każą mi stworzyć pakiet, tak jak w Organizowaniu kodu źródłowego R :) Ale nie tworzę czegoś, co będzie używane gdzie indziej, to tylko samodzielny projekt.


37
Cały czas tworzę pakiety dla samodzielnych projektów. To niewiele pracy, a korzyści są ogromne. No dalej, wiesz, że chcesz to zrobić ...
Andrie

Odpowiedzi:


93

Oto jeden możliwy sposób. Użyj tej existsfunkcji, aby sprawdzić coś unikalnego w swoim util.Rkodzie.

Na przykład:

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

(Zredagowano, aby uwzględnić mode="function", jak wskazał Gavin Simpson)


4
Niezłe użytkowanie exists()- wymaga mode = "function"dodania, aby było niezawodny
Gavin Simpson

1
exists()wydaje się zgłaszać błąd, z wyjątkiem zwracania jednego w R 3.0.2.
Michael Schubert,

Prawidłowe użycie to „istnieje („ foo ”) i odpowiedź została zmieniona.
Andrie,

18

Nie ma czegoś takiego wbudowanego, ponieważ R nie śledzi wywołań sourcei nie jest w stanie dowiedzieć się, co zostało załadowane skąd (nie ma to miejsca w przypadku używania pakietów). Możesz jednak użyć tego samego pomysłu co w .hplikach C , czyli zawinąć całość w:

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

 #Code

}

a następnie zadzwoń source("util.R")w ramach ifkodu, prawda?
rafalotufo

1
@rafalotufo Jak zwykle użyjesz źródła ("util.R"). Kod w poście mbq trafiłby do pliku util.R. Po prostu umieszczasz całą zawartość pliku util.R w gigantycznej instrukcji if (), jeśli ma to sens.
Keith Twombley

10

Say util.Rtworzy funkcję foo(). Możesz sprawdzić, czy ta funkcja jest dostępna w środowisku globalnym i pobrać skrypt, jeśli nie jest:

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

To znajdzie wszystko o nazwie foo. Jeśli chcesz znaleźć funkcję, to (jak wspomniał @Andrie) exists()jest pomocne, ale musisz wiedzieć , jakiego typu obiektu szukać, np.

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

Oto exists()akcja:

> 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

W takim przypadku możesz użyć, grepl(..., value=TRUE)ponieważ wyszukiwane hasło prawdopodobnie nie jest wyrażeniem regularnym. Przy okazji +1.
Andrie

?? grepl()nie ma argumentu value, ale prawdopodobnie powinienem poprawić wyrażenie regularne w ls()...
Gavin Simpson,

Przepraszam, mój błąd. Miałem na myślifixed=TRUE
Andrie

@Andrie - Ach, OK. I tak się nie udało. Zostałem odciągnięty podczas rozważania tego.exists()jest lepiej, ale widzę, że w międzyczasie opublikowałeś taką odpowiedź.
Gavin Simpson

5

Możesz napisać funkcję, która pobiera nazwę pliku i nazwę środowiska, sprawdza, czy plik został załadowany do środowiska, a sys.sourcejeśli nie, używa go jako źródła.

Oto szybka i nieprzetestowana funkcja (mile widziane ulepszenia!):

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

Oto funkcja, którą napisałem. Otacza base::sourcefunkcję, aby przechowywać listę plików źródłowych w globalnej liście środowiska o nazwie sourced. Plik zostanie ponownie źródłowy tylko wtedy, gdy podasz .force=TRUEargument do wywołania źródła. Jego sygnatura argumentów jest poza tym identyczna z rzeczywistą, source()więc nie musisz przepisywać swoich skryptów, aby z niej korzystać.

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

Jest dość rozmowny (wiele telefonów do message()), więc możesz usunąć te linie, jeśli ci zależy. Wszelkie rady od doświadczonych użytkowników języka R są mile widziane; Jestem całkiem nowy w R.


0

Rozwiązałem swój problem używając całego adresu, gdzie mój kod to: Przed:

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

Po:

if(!exists("foo", mode="function")) source("C:/tests/utils.r")
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.