他のスクリプトに(ソース)Rスクリプトを含める方法


108

プロジェクトの他のスクリプトから使用するユーティリティRスクリプトutil.Rを作成しました。このスクリプトが定義する関数が他のスクリプトで機能できるようにする適切な方法は何ですか?

requireまだロードされていない場合にのみパッケージをロードする、関数に似たものを探しています。source("util.R")呼び出されるたびにスクリプトが読み込まれるため、呼び出したくありません。

私は、Rソースコードの整理のように、パッケージを作成するように指示するいくつかの回答を受け取ることを知っています。


37
スタンドアロンプ​​ロジェクトのパッケージは常に作成しています。それは多くの仕事ではありません、そして利点は巨大です。続けて、あなたはあなたがそれをしたいのを知っています...
Andrie

回答:


93

これが可能な方法の1つです。exists関数を使用して、util.Rコード内で固有のものをチェックします。

例えば:

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

mode="function"Gavin Simpsonが指摘したように、を含むように編集)


4
使いやすいexists()- 簡単に使えるようにmode = "function"するために追加する必要がある
Gavin Simpson

1
exists()R 3.0.2でエラーを返す以外はエラーをスローするようです。
Michael Schubert

正しい使い方は `exists(" foo ")で、答えは編集されました。
Andrie

18

Rは呼び出しを追跡せずsource、どこから何がロードされたかを把握できないため、組み込みのものはありません(これはパッケージを使用する場合は異なります)。しかし、C .hファイルと同じアイデアを使用できます。つまり、全体を次のようにラップします。

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

 #Code

}

コードsource("util.R")内で呼び出しますifよね?
rafalotufo 2011年

1
@rafalotufoいつものようにsource( "util.R")します。mbqの投稿にあるコードはutil.Rに入ります。util.Rの内容全体を巨大なif()ステートメントに挿入するだけです(意味がある場合)。
キースTwombley 2013年

10

セイは、util.R機能を生成しますfoo()。この関数がグローバル環境で使用できるかどうかを確認し、使用できない場合はスクリプトを入手できます。

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

という名前のものが見つかりますfoo。関数を見つけたい場合は、(@ Andrieによって言及されているように)exists()役に立ちますが、検索するオブジェクトのタイプを正確に指示する必要があります。たとえば、

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

これがexists()動作しています:

> 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

この場合、grepl(..., value=TRUE)検索語がおそらく正規表現ではないため、使用することができます。ちなみに+1。
Andrie

?? grepl()引数はありませんがvalue、私はおそらく正規表現を修正する必要がありls()ます...
Gavin Simpson

すみません、私の間違いです。私は意味fixed=TRUE
Andrie

@Andrie-ああ、そうか。とにかくうまくいきませんでした。これについて熟考している間に引きずり出されました。exists()その方が良いですが、今のところそのような回答を投稿しているようです。
Gavin Simpson

5

ファイル名と環境名を取り、ファイルが環境にロードされているかどうかを確認し、ロードされていない場合はファイルを読み込むために使用する関数を作成できsys.sourceます。

以下は、テストされていない関数です(改善を歓迎します)。

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

これが私が書いた関数です。これbase::sourceは、ソースファイルのリストをという名前のグローバル環境リストに格納する関数をラップしますsourced。ソース.force=TRUEへの呼び出しに引数を指定した場合にのみ、ファイルを再ソースします。その他の点では、引数の署名は実際の署名と同じであるため、source()これを使用するためにスクリプトを書き直す必要はありません。

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

かなりおしゃべり(への呼び出しが多いmessage())なので、気になったらこれらの行を削除できます。ベテランRユーザーからのアドバイスは大歓迎です。私はRにかなり新しいです。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.