RLinkユーザガイド
このガイドでは,Wolfram言語とRの間の交信にRLinkを使う方法について説明する.
RLinkのロード,インストール,アンインストール
R言語配布のインストールと設定
RLinkは,Wolfram言語を使用中のマシンにある既存のR言語配布に接続する.RLink自体はR言語のインストールプロセスを実行しないので,少なくともR言語配布が1つ事前にインストールされている必要がある.複数のR言語が同じマシンにインストールされている場合,作業に使いたい配布を通常RLinkに指し示す必要がある.以下でどのようにそれを行うかについて説明する.また,RLinkにはR言語の発見システムが組み込まれており,RLinkは見付けた最初のR言語のインストール(通常それが使用中のシステムのデフォルトのR言語のインストールとなる)を使おうと試みる.最後に,RLinkは最後に使ったR言語のインストールを覚えているので,特に指示がない限り,デフォルトでその同じインストールをもう一度使おうと試みる.
RLinkが指定のR言語のインストールと一緒に使えるようにするために設定する必要がある.設定プロセスは主に,R言語パッケージ"rJava"をインストールすることと,RLinkにそのインストールされたパッケージへのパス(RLinkはJavaを中間層として使うのでこれが必要である)を提供することからなる.ほとんどの場合,RLinkは自動的にこの設定プロセスを行う.しかし必要な場合には,手動でR言語のインストールを設定することも可能である.
どちらの場合でも,R言語の設定プロセスに成功するかどうかは,ビルドツールおよび使用するシステムにインストールされるべき従属物における必要条件にかかっている.これらの必要条件についての情報も同じ手動でR言語のインストールを設定することについてのチュートリアルに記載されている.WindowsとmacOSでは通常これらはあまり重要ではなく,ほとんどのシステムで自動的に満足させられるが,Linuxではこれらの点は重要となってくる.
デフォルトのR言語配布をRLinkと一緒に使う
パッケージがロードされたが,Rのランタイムはまだ起動されていない.これを行うには,InstallR関数を呼び出さなければならない:
明示的な引数なしでInstallRを使うと,RLinkは最初に見付けたR言語のインストール(通常使用しているシステム上のデフォルトのもの)を使う.ただし,既存のR言語のインストールをすでに使ったことがある場合には,その前に使ったものに接続する.
R言語を特定のコマンドラインオプションで起動する必要がある場合には,そのオプションをオプション"RCommandLine"で文字列として指定することができる.
R言語のランタイムを止め,アンインストールするには,UninstallRコマンドを使う.以下でR言語をアンインストールする:
これで,もう一度InstallRを呼び出すまで,R言語と交信を行うRLink関数を使うことはできなくなった.例えば,データをR言語に送ってみる:
InstallRを使ってもう一度R言語をインストールする:
UninstallRを使わなければならないことはめったにない.この関数が役に立つ状況としては,別の組合せのコマンドラインオプションを使ってR言語のランタイムを再起動しなければならない場合がある.また,同じマシンに複数のR言語配布がインストールされていて,RLinkに現在使用しているものとは別のものを使うように指示したいという場合もある.
特定のR言語配布を使う
RLinkは, RLinkとR言語がサポートされているプラットフォームすべてにおいて,外部のR言語配布と接続することをサポートする.簡単な状況では,接続を開始するのに追加の設定は必要ない.しかし一般的には一回きりのR言語の設定プロセスが指定のR言語のインストールで必要である.そのような場合,RLinkはそのプロセスを自動的に行うことができる.しかし場合によっては(特にLinuxでは),「R言語の外部インストールがRLinkで使えるように設定する」に詳しく説明されているステップを手動で行うことが必要となることもある.
外部のR言語のインストールに唯一必要な引数は,オプション"RHomeLocation"であり,これはRLinkにRのホームディレクトリを探す場所を示す.このオプションだけを渡したときにInstallRが失敗する場合には,設定プロセスが必要である.いったんこの設定プロセスを特定のR言語のインストールに行うと,InstallRを呼び出す(追加のオプションが渡される場合もある)ことでそのインストールに接続できるはずである.
RLinkをインストールする際に,R言語配布の場所を指定する
簡単な状況の場合には,InstallRの"RHomeLocation"オプションを使い,それを以下のように呼び出すことによって場所を指定することができる:
オプションの値は,R言語配布で通常設定されているR_HOME変数に対応し,そのルートを指さなくてはならない.
追加のオプションを指定する
RLinkと一緒に使う外部のR言語配布を設定プロセスで設定する必要がある場合には,RLinkに追加の情報,つまり上の設定プロセスに組み込まれているネイティブJRIライブラリの場所("JRINativeLibraryLocation"オプション)とこの特定のR言語のインストールのバージョン番号("RVersion"オプション)をInstallRに渡す必要がある.これは以下で行うことができる:
サポートされているプラットフォームすべてについての詳細と例は,「R言語の外部インストールがRLinkで使えるように設定する」に記載されている.
実際には,設定プロセスが正しく行われると,ほとんどの場合RLinkは指定のR言語配布についてこれらのパラメータを自動的に決定することができるようになるので,InstallRにこれらのオプションを渡す必要が出てくるのは特別の状況のみである.
Rパッケージのインストール
Rの言語とエコシステムによって提供される有用な機能のほとんどは,Rパッケージ内に含まれるものである.このパッケージは標準のRのインストールの一部ではないので,別個にインストールしなければならない.R言語はパッケージのインストールのために数多くのツールを提供し,その最もよく知られているものにRの標準install.packages()関数がある.RLinkは,専門的な関数であるRInstallPackageを提供し,これはinstall.packages()の機能をラップしてWolfram言語のインターフェースをそれにさらす.
以下は,使い方の簡単な例である.まず,RLinkをロードする:
使いたいパッケージをインストールする.この例では,prettyunitsという名前のRパッケージをインストールする.このパッケージはよく使われるRのデータ型用のさまざまなプリティプリントの機能を提供する:
パッケージは使う前に,Rセッションにロードしなければならない:
パッケージは,RInstallPackageをオプション"InstallationMethod" "Remove"と一緒に使ってアンインストールする:
RInstallPackageを使うのに実行中のRLinkは必要ないが,実行中のセッションがあると,RInstallPackageを単一の引数(インストールされたパッケージの名前)だけで使い,2つ目のオプショナルの引数(Rホームディレクトリへのパス)は実行中のRセッションから推測されるので,省略することができる.
Rパッケージのインストールとアンインストールの詳細については,RInstallPackageのドキュメントを参照されたい.
データをR言語に送る
データをR言語に送るためには,RSet関数を使わなければならない.データは,RLinkが理解できる形式で表さなければならない.(多次元)配列等,最もよくあるデータの型については,Wolfram言語で通常使われるネストしたリストを使えばよい.このことについての詳細は,「RLinkにおけるR言語データの型」を参照されたい.
整数をR言語に送り,R言語のワークスペースの変数にその値を割り当てる:
REvaluateを使って割当てをテストすることができる:
スカラーは一要素ベクトルとして解釈されるので,結果はリストとなる.これについての詳細は,「RLinkにおけるR言語データの型」を参照されたい.
今度はネストした不規則リストをR言語に送る.これはR言語リストであると解釈される:
ほとんどの場合,R言語に送ろうとしているデータの型を示す必要はない.データの型は,データの形式に基づいて,自動的にRLinkが認識する.型の自動検知の過程の詳細については,ToRFormのドキュメントページと「RLinkにおけるR言語データの型」のセクションに記載されている.
RSetは,変数よりも一般的な式にも使える.特にリストと配列の要素に部分割当てを行うことができる:
R言語側で割当てが行われたかどうかをテストすることができる:
一般に,唯一必要なのは,文字列として表されたRの式でRSetの第1引数として渡されたものが,値を割り当てられる(R言語では左辺の値)ことである.
R言語コードの実行とRからのデータの取得
有効なR言語コードの文字列を実行し,その結果をWolfram言語に返すためには,REvaluate関数を使うとよい.すでにこの関数を使ったいくつかの例は上で紹介されている.
ここでは,結果はWolfram言語に送り返されたが,R言語のワークスペースには保存されていない.結果を保存したい場合は,これにR言語のワークスペースの変数を割り当てるとよい:
値がR言語の変数に保存されたかどうかをテストすることができる:
以下ではもう少し難しい計算を行う.tableで数の頻度を計算し,その結果をR言語のデータフレームとして返す:
R言語側でも出力の表示を抑制したい場合がある.つまりR言語からWolfram言語へのデータ転送を抑制するのである.これを行うには,コードの最後にセミコロンを加える:
この操作が必要となる1つの理由として,結果がRLinkを通してWolfram言語に返せないが,R言語ではさらに操作を行うことができる型であるという場合がある.
R言語コードの複数行はREvaluateで実行することができるが,その場合にはコードを中カッコで囲まなければならない:
ここでもコードの文字列の最後にセミコロンが使われていて,コードはもちろん実行されるが,R言語からWolfram言語へのデータ転送は抑制されている.
REvaluateのドキュメントページにはこの他にも数多くの例が挙げられている.
自分のR言語関数を定義する
このセクションのトピックは論理的には前のセクションで説明したことに関係しているが,重要なトピックなので,別個のセクションを取って説明する.R言語で,あるいはそのさまざまな拡張で使用できる関数を使う以上のことを行いたい場合に,自分自身のR言語関数をWolfram言語から定義したいかもしれない.
RLinkを使えばこれは可能である.どのように行うかについての詳細は,「関数」で説明されている.ここでは,簡単な例だけを見てみる.一般に,RLinkの関数は不明瞭な参照によって表され,この参照はR言語ワークスペースで定義された関数を指す.このような参照は変数に保存して,あるいは直接使って,Wolfram言語式上のR関数を引数として呼び出し,結果をWolfram言語に返すことができる.
上では,関数の参照を生成し,それを変数sqに保存した.しかし,R言語のワークスペースでの割当ても行われた.これでいくつかの方法を使ってこの関数が呼び出せるようになった.まず,これをR言語で直接呼び出す:
しかしこの方法では,Wolfram言語から引数を渡すことはできない.この場合に関数の参照が役に立つ:
参照を変数に保存する必要はない.これを生成したコードと同じコードの中で使うことができる:
しかし,REvaluateを使って関数の参照を構築し,これをこのような方法で使うことは,必ずしも最適であるとは限らない.呼出しを行うたびに,新しい関数の参照が生成され,このような参照は,現行のRLinkセッションのみで有効なものであるからである(このことについては,「RLinkの関数」に詳しく説明されている).「よりよい」関数の参照を作成するための特別のデバイスがある.RFunctionを使う方法がそうである.このデバイスは,キャッシュされ,無限に使える.
また,上と同じように,変数に保存せずに直接使うこともできる:
この方法では,RFunctionで生成された参照はキャッシュされるので,呼出しのたびに新しい関数の参照が生成されるということがない.
関数の参照は,RLinkの他のオブジェクトと同じように使える.R言語に送ったり,他(高次数)関数の引数として渡したりすることができる.例えば,先ほどの関数の参照をR言語の変数に割り当てる.
関数の参照は,他の関数の引数として渡すことができる.例えば,Wolfram言語のSelect関数と同じものをR言語で以下のように定義することができる:
これをカスタムのフィルタリング関数と一緒に使うことができる.この関数もRFunctionで定義することができる:
RLinkで関数の参照を使うことについては,細かい点が他にもたくさんあるが,これらは「関数」で説明されている.
エラーの処理
RLinkでは,可能なエラーを数個のレベルで処理しようとする.まずWolfram言語側では,RLinkは無効なWolfram言語入力を見付け,対応するエラーメッセージを出そうとする.
例えば,Rに一般的な記号式を転送しようとすると,RLinkではこの入力をR言語に転送できるデータの型に変換する方法が分からないことを示すエラーメッセージが返される:
R言語にどの入力が転送でき,どれが転送できないかを知るためには,「RLinkにおけるR言語データの型」を参照されたい.このチュートリアルにはこの点についての詳細が記載されている.
エラーによっては,R言語へのデータ転送の際には起らないが,R言語のランタイムのエラーとして起るというものもある.このような場合には,RLinkはR言語のワークスペースで生成されたRのエラーメッセージをWolframシステムに送信しようとする.
構文解析エラーは通常検知されるが,間接的にであり,そのエラーメッセージは分かりにくく,実装の詳細に関するものであることがある:
性能チューニング
RLinkは,JLinkの上に構築されている高レベルインターフェースである.JLink自体も,Wolfram Symbolic Transfer Protocol (WSTP)と,RのランタイムへのJavaインターフェースであるRJava / JRI(JRIは,スタンドアロンの動的なネイティブライブラリとして使われる)の上に構築されている.またRLinkでは,データの転送と実行に柔軟な方法が使われることが多く,時にはランタイムのR言語コードの生成と実行が行われることもある.この柔軟性によってRLinkは,可能なR言語オブジェクトのかなり大規模な部分集合を処理することができ,配列とリストへの部分割当てを統一した方法で行うことができる.しかしその結果生まれるのがオーバーヘッドで,これはかなり大きなものであることが多い.このようなオーバーヘッドが許容されないという場合に,Wolfram言語とRの間のデータ転送を最適化する方法がある.以下ではそのいくつかについて説明する.
ベクトル対リスト
ここでお勧めしたいのは,できるだけRリストの送信と受取りを避け,代りにRベクトル(配列)の送信と受取りを行うということである.これは,配列を使うほうがRとの交信のすべての段階において効率的であるからである.内部表現により効率的に変換され(Wolfram言語のパックアレーが利用できることが多いので),Rにより効率的に変換され,Rでより効率的に処理され,より速くWolfram言語が結果を得ることもできる.
後者の場合には,時間の複雑性も線形であるが,ずっと大きな定数であり,ベクトルの場合の約40倍(これはトップレベルの反復を正しく行った場合のスピードとパックアレーを使った場合のスピードの差とほぼ同じである)であることが分かる.
今度はデータをRに送るオーバーヘッドについて見てみよう.まずベクトルの内部形式を準備し,データの転送だけにかかる時間を計るようにする:
オーバーヘッドの違いは明らかである(まして,ベクトルと同じ大きな数の要素を使ってさえもいない).RLinkの将来のバージョンでは,おそらくリストのデータを転送するより効率的な方法が開発されるはずだが,現行バージョンでは,数千項目を超えるリストの交信を行うことは避けたほうがよい. Rリストは集約のデータ構造として使われることが多いので,大きなR言語のリストは自然にはあまり起らないと考えてよい:
関数の呼出し
関数の呼出しを行う以下のシンタックスは便利であるが,これは結果としてオーバーヘッドを出し,それが許容範囲を超える大きなものになることがよくある.
ベクトル化された操作をより多く使い,R言語で作業全体をまとめて行う(データ交換を最小化する)ことができれば,コードはより速く実行できる:
2度目の実行は,より速くなる(最初の実行は,新しいカーネルでの最初の呼出しでJavaのクラスのロードやその他のイベントが引き起されるため,必要であった.このため,最初の呼出しに基づく測定は正しくない):
関数の呼出しには常にオーバーヘッドが起り,上の例ではそれが実行時間のほとんどを占めていた.ここでは点の数を100倍に増やしたが,結果を計算するのにかかる時間はほとんど同じである:
一般
最後に一般的なアドバイスとして述べたいことは,どのような操作をするにしても,両方向に転送されるデータ量を最小化しなくてはならず,さらに重要なことは,REvaluate,RSet,RFunction等の関数が呼び出される回数を最小化しなくてはならないということである.
最悪の状況として考えられるのは,R言語で定義された軽量の関数(あるいは非常に小さな操作を行うR言語のコード)をWolfram言語から何度も呼び出すという場合である.このような場合,実行にかかる時間の大部分は,R言語での実際の計算に費やされるのではなく,データ転送とその他のRLinkの内部作業に費やされる.
一番いい状況は,難しく,計算に時間がかかる作業のほとんどをR言語で行い,データがRで効率的に転送される(例えば,ほとんどがベクトルであるデータ構造を使う)場合である.要素の数が大きくない場合にリストを使うことは問題ない.リストは異種のベクトル(あるいは別のデータ構造)の集まりをまとめる集約のデータ構造として使われることが多いので,ベクトルが使われるべき場面で不適切に使われるのでない限り,リストが大きな数の要素に使われることは普通ない.RLinkで大きな問題となる唯一の例外は,R言語での計算の結果が,大きな(例えば整数の)不規則配列である場合で,これはR言語のリストとしてしか表すことができない.このような場合には,転送を速める一つの方法として,そのような配列を可能な場合は常に矩形配列に充填する方法がある.