12

私のRプロジェクトはますます複雑になっており、Java / C#のクラス、またはPythonのモジュールに相当する構造を探し始めています。これにより、グローバル名前空間に外部で使用されることのない関数が散らばることがなくなります。 1つの特定の.rファイル。

ですから、私の質問は、関数の範囲を特定の.rファイルなどにどの程度制限できるかということだと思います。

.rファイル全体を1つの巨大な関数にして、その中に関数を入れることができると思いますが、それはエコーを台無しにします。

myfile.r:

myfile <- function() {
    somefunction <- function(a,b,c){}
    anotherfunction <- function(a,b,c){}

    # do some stuff here...
    123
    456
    # ...
}
myfile()

出力:

> source("myfile.r",echo=T)

> myfile <- function() {
+     somefunction <- function(a,b,c){}
+     anotherfunction <- function(a,b,c){}
+ 
+     # do some stuff here...
+     # . .... [TRUNCATED] 

> myfile()
> 

echo=Tコマンドで使用したにもかかわらず、「123」が出力されていないことがわかりますsource

すべてを単一の関数の中に入れることは本当に標準的なもののように聞こえないので、もっと標準的な他の構造があるかどうか疑問に思いますか?しかし、おそらくそうですか?また、それがうまくいくことを意味するなら、それはecho=T私にとって間違いなくボーナスです。

4

3 に答える 3

21

まず、@ Spacedmanが言ったように、パッケージが最適ですが、他のオプションもあります。

S3メソッド

Rの元の「オブジェクト指向」はS3として知られています。Rのコードベースの大部分は、この特定のパラダイムを使用しています。それはplot()あらゆる種類のオブジェクトのために働くものです。plot()はジェネリック関数であり、Rコアチームとパッケージ開発者は、のために独自のメソッドを作成できますplot()。厳密には、これらのメソッドには、関数がメソッドを定義するオブジェクトのクラスであるplot.foo()whereのような名前が付いている場合があります。S3の利点は、使用することを(ほとんど)知る必要がなく、呼び出す必要がないことです。Rは、オブジェクトのクラスに基づいて、ディスパッチするメソッドを決定します。fooplot()plot.foo()plot(bar)plot()bar

あなたの質問に対するあなたのコメントの中で、あなたはあなたがpopulate()クラスのための(事実上)メソッドを持ち"crossvalidate"そして"prod"あなたが別々の.rファイルに保持する関数を持っていると述べています。これを設定するS3の方法は、次のことです。

populate <- function(x, ...) { ## add whatever args you want/need
    UseMethod("populate")
}

populate.crossvalidate <-
    function(x, y, z, ...) { ## add args but must those of generic
    ## function code here
}

populate.prod <-
    function(x, y, z, ...) { ## add args but must have those of generic
    ## function code here
}

bar与えられたクラスを持つオブジェクト"prod"、呼び出し

populate(bar)

R呼び出しpopulate()(ジェネリック)が発生し、名前の関数が検索されます。populate.prodこれは、のクラスであるためですbar。それは私たちを見つけて、私たちがpopulate.prod()最初に指定した引数をそれに渡す関数をディスパッチします。

したがって、完全な関数名ではなく、ジェネリックの名前を使用してメソッドを参照するだけであることがわかります。Rは、どのメソッドを呼び出す必要があるかを判断します。

2つのpopulate()メソッドは、厳密にはジェネリック関数と同じ引数を持つ必要があることを除いて、非常に異なる引数を持つことができます。したがって、上記の例では、すべてのメソッドに引数xとが必要...です。(式オブジェクトを使用するメソッドには例外がありますが、ここではそれについて心配する必要はありません。)

パッケージの名前空間

R 2.14.0以降、Rには名前空間がずっと長く存在していましたが、パッケージの作成者から提供されていなくても、すべてのRパッケージに独自の名前空間がありました。

あなたの例では、populate()ジェネリックを登録したいと思います。それはS3システムに2つのメソッドです。また、ジェネリック関数をエクスポートしたいと思います。通常、個々のメソッドをエクスポートする必要はありません。.Rしたがって、パッケージソースのフォルダー内のファイルに関数をポップRし、パッケージソースのトップレベルでという名前のファイルを作成しNAMESPACE、次のステートメントを追加します。

export(populate) ## export generic

S3method(populate, crossvalidate) ## register methods
S3method(populate, prod)

次に、パッケージをインストールすると、呼び出すことができることに気付くでしょうが、プロンプトまたは別の関数から名前で直接etcpopulate()を呼び出そうとすると、Rは文句を言います。populate.prod()これは、個々のメソッドである関数が名前空間からエクスポートされていないため、名前空間の外部に表示されないためです。呼び出すパッケージ内の関数はすべてpopulate()、定義したメソッドにアクセスできますが、パッケージ外の関数またはコードはメソッドをまったく認識できません。:::必要に応じて、演算子を使用してエクスポートされていない関数を呼び出すことができます。

mypkg:::populate.crossvalidate(foo, bar)

mypkg動作します。パッケージの名前はどこにありますか。

正直なところ、NAMESPACEパッケージをインストールするとRがファイルを自動生成し、すべての関数を自動的にエクスポートするため、ファイルも必要ありません。そうすれば、2つのメソッドがpopulate.xxx()(特定のメソッドはどこにあるかxxx)として表示され、S3メソッドとして動作します。

関係する内容の詳細については、「R拡張機能の作成」マニュアルのセクション1「Rパッケージの作成」をお読みください。ただし、特にパッケージが自分で使用する場合は、yuoが必要ない場合はこの半分を実行する必要はありません。適切なパッケージフォルダ(つまりRと)を作成し、ファイルをmanに貼り付けます。追加した場所に単一のファイルを書き込む.RR.Rdman

\name{Misc Functions}
\alias{populate}
\alias{populate.crossvalidate}
\alias{populate.prod}

ファイルの先頭にあります。\alias{}あなたが持っている他の機能のために追加してください。次に、パッケージをビルドしてインストールする必要があります。

代替使用sys.source()

ここで長期的に実行可能なオプションとして以下に言及することは本当にお勧めしませんが(できません!)、.r最初に要求したように個々のファイルから機能を分離できる代替手段があります。これは、名前空間ではなく環境を使用することで実現され、パッケージの作成は含まれません。

このsys.source()関数を使用して、.RファイルからRコード/関数を取得し、環境で評価することができます。.Rファイルが関数を作成/定義しているときに、別の環境内でそれを調達すると、それらの関数はその環境でそこで定義されます。これらはデフォルトでは標準の検索パスに表示されないため、で定義されたpopulate()関数はで定義された関数crossvalidate.Rと衝突しません。populate()prod.R2つの別々の環境を使用する限り。1セットの関数を使用する必要がある場合は、環境を検索パスに割り当てることができます。検索パス上で、環境はすべてに奇跡的に表示され、完了したら切り離すことができます。他の環境をアタッチ、使用、デタッチなど。または、などを使用して特定の環境でRコードが評価されるように調整できますeval()

私が言ったように、これは推奨される解決策ではありませんが、流行の後、あなたが説明する方法で機能します。例えば

## two source files that both define the same function
writeLines("populate <- function(x) 1:10", con = "crossvalidate.R")
writeLines("populate <- function(x) letters[1:10]", con = "prod.R")

## create two environments
crossvalidate <- new.env()
prod <- new.env()

## source the .R files into their respective environments
sys.source("crossvalidate.R", envir = crossvalidate)
sys.source("prod.R", envir = prod)

## show that there are no populates find-able on the search path

> ls()
[1] "crossvalidate" "prod" 
> find("populate")
character(0)

次に、環境の1つを接続して、次のように呼び出しますpopulate()

> attach(crossvalidate)
> populate()
 [1]  1  2  3  4  5  6  7  8  9 10
> detach(crossvalidate)

次に、他の環境で関数を呼び出します

> attach(prod)
> populate()
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
> detach(prod)

明らかに、特定の関数を使用するたびに、attach()その環境を呼び出してから呼び出し、続いて呼び出しを行う必要がありdetach()ます。これは苦痛です。

私はあなたがRコード(実際には式)が指定された環境で評価されるように手配できると言いました。たとえば、これにeval()使用できます。with()

> with(crossvalidate, populate())
[1]  1  2  3  4  5  6  7  8  9 10

少なくとも今populate()は、選択したバージョンを実行するために1回の呼び出しだけが必要です。ただし、関数をフルネームで呼び出す場合、たとえばpopulate.crossvalidate()(コメントによると)with()手間がかかりすぎる場合は、アイデアでさえ面倒になると思いますか?そしてとにかく、あなたがあなた自身のRパッケージを非常に簡単に持つことができるのに、なぜあなたはこれを使うのでしょうか。

于 2012-10-26T18:56:04.197 に答える
16

「パッケージを作る」という複雑さについて心配する必要はありません。そんなことを考えるのはやめましょう。あなたがやろうとしていることはこれです:

  1. プロジェクトで作業しているフォルダーに、「R」というフォルダーを作成します
  2. Rコードをそこに入れ、ファイルごとに1つの関数
  3. プロジェクト ディレクトリに DESCRIPTION ファイルを作成します。正確な形式については既存の例を確認してください。ただし、必要なフィールドはわずかです。
  4. 開発ツールを取得します。install.packages("devtools")
  5. 開発ツールを使用します。library(devtools)

ここで、R フォルダー内の R ファイルに関数を記述します。それらを R にロードするには、それらをソーシングしないでください。してくださいload_all()。関数はロードされますが、グローバル環境にはロードされません。

R ファイルの 1 つを編集してから、load_all()もう一度やり直してください。これにより、変更されたファイルが R フォルダーに読み込まれ、関数が更新されます。

それでおしまい。編集、load_allすすぎ、繰り返し。パッケージを作成しましたが、これは非常に軽量であり、R のパッケージ構築ツールの束縛や規律に対処する必要はありません。

オブジェクトをロードするための軽量パッケージメカニズムを実装しようとするコードを見たり、使用したり、書いたりしましたが、devtools ほど優れたものはありません。

ハドリー万歳!

于 2012-10-26T07:33:33.250 に答える
3

パッケージ化を検討してみてはいかがでしょうか。別の方法として、環境を調べることもできます。最後に、RStudio のプロジェクトは、あなたに合ったものに近いかもしれません。

于 2012-10-26T06:24:58.853 に答える