1. 概要

R は、統計に使用される一般的なプログラミング言語です。 さまざまな関数とパッケージを利用できるため、Rコードを他の言語に埋め込むことは珍しいことではありません。

この記事では、RコードをJavaに統合する最も一般的な方法のいくつかを見ていきます。

2. Rスクリプト

このプロジェクトでは、入力としてベクトルを受け取り、その値の平均を返す非常に単純なR関数を実装することから始めます。 これを専用ファイルで定義します。

customMean <- function(vector) {
    mean(vector)
}

このチュートリアル全体を通して、Javaヘルパーメソッドを使用してこのファイルを読み取り、その内容をStringとして返します。

String getMeanScriptContent() throws IOException, URISyntaxException {
    URI rScriptUri = RUtils.class.getClassLoader().getResource("script.R").toURI();
    Path inputScript = Paths.get(rScriptUri);
    return Files.lines(inputScript).collect(Collectors.joining());
}

それでは、Javaからこの関数を呼び出すために必要なさまざまなオプションを見てみましょう。

3. RCaller

検討する最初のライブラリはRCallerで、ローカルマシンで専用のRプロセスを生成することでコードを実行できます。

RCallerはMavenCentral から入手できるため、pom.xmlに含めることができます。

<dependency>
    <groupId>com.github.jbytecode</groupId>
    <artifactId>RCaller</artifactId>
    <version>3.0</version>
</dependency>

次に、元のRスクリプトを使用して値の平均を返すカスタムメソッドを作成しましょう。

public double mean(int[] values) throws IOException, URISyntaxException {
    String fileContent = RUtils.getMeanScriptContent();
    RCode code = RCode.create();
    code.addRCode(fileContent);
    code.addIntArray("input", values);
    code.addRCode("result <- customMean(input)");
    RCaller caller = RCaller.create(code, RCallerOptions.create());
    caller.runAndReturnResult("result");
    return caller.getParser().getAsDoubleArray("result")[0];
}

このメソッドでは、主に2つのオブジェクトを使用しています。

  • RCode 。これは、関数、その入力、および呼び出しステートメントを含む、コードコンテキストを表します。
  • RCaller 。これにより、コードを実行して結果を返すことができます

RCallerは、Rプロセスの開始に時間がかかるため、小規模で頻繁な計算には適していないことに注意することが重要です。 これは顕著な欠点です。

また、 RCallerは、ローカルマシンにインストールされているRでのみ機能します。

4. レンジン

Renjin は、R統合ランドスケープで利用できるもう1つの人気のあるソリューションです。 より広く採用されており、エンタープライズサポートも提供しています

Mavenの依存関係とともにMulesoftリポジトリを追加する必要があるため、プロジェクトにRenjinを追加するのは少し簡単です。

<repositories>
    <repository>
        <id>mulesoft</id>
        <name>Mulesoft Repository</name>
        <url>https://repository.mulesoft.org/nexus/content/repositories/public/</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>org.renjin</groupId>
        <artifactId>renjin-script-engine</artifactId>
        <version>RELEASE</version>
    </dependency>
</dependencies>

もう一度、R関数のJavaラッパーを作成しましょう。

public double mean(int[] values) throws IOException, URISyntaxException, ScriptException {
    RenjinScriptEngine engine = new RenjinScriptEngine();
    String meanScriptContent = RUtils.getMeanScriptContent();
    engine.put("input", values);
    engine.eval(meanScriptContent);
    DoubleArrayVector result = (DoubleArrayVector) engine.eval("customMean(input)");
    return result.asReal();
}

ご覧のとおり、 eval メソッドを使用して名前で関数を直接呼び出すことができるため、の概念はRCallerと非常に似ていますが、冗長性は低くなります

Renjinの主な利点は、JVMベースのインタープリターを使用するため、Rをインストールする必要がないことです。 ただし、Renjinは現在GNURと100% c互換性がありません。

5. Rserve

これまでに確認したライブラリは、コードをローカルで実行するための適切な選択肢です。 しかし、Rスクリプトを呼び出す複数のクライアントが必要な場合はどうでしょうか。 そこでRserveが登場し、TCPサーバーを介してリモートマシンでRコードを実行できるようになります。

Rserveのセットアップには、Rコンソールを介して、関連パッケージをインストールし、スクリプトをロードするサーバーを起動することが含まれます。

> install.packages("Rserve")
...
> library("Rserve")
> Rserve(args = "--RS-source ~/script.R")
Starting Rserve...

次に、通常どおり Maven依存関係を追加することで、プロジェクトにRserveを含めることができます。

<dependency>
    <groupId>org.rosuda.REngine</groupId>
    <artifactId>Rserve</artifactId>
    <version>1.8.1</version>
</dependency>

最後に、RスクリプトをJavaメソッドにラップしましょう。 ここでは、サーバーアドレスで RConnection オブジェクトを使用します。指定されていない場合、デフォルトで127.0.0.1:6311になります。

public double mean(int[] values) throws REngineException, REXPMismatchException {
    RConnection c = new RConnection();
    c.assign("input", values);
    return c.eval("customMean(input)").asDouble();
}

6. FastR

最後に説明するライブラリはFastRです。 GraalVM上に構築された高性能R実装。 この記事の執筆時点では、FastRはLinuxおよびDarwinx64システムでのみ使用できます。

これを使用するには、まず公式WebサイトからGraalVMをインストールする必要があります。 その後、Graal Component Updaterを使用してFastR自体をインストールし、それに付属する構成スクリプトを実行する必要があります。

$ bin/gu install R
...
$ languages/R/bin/configure_fastr

今回のコードは、Javaにさまざまなゲスト言語を埋め込むためのGraalVM内部APIであるPolyglotに依存します。 Polyglotは一般的なAPIであるため、実行するコードの言語を指定します。 また、 c R関数を使用して、入力をベクトルに変換します。

public double mean(int[] values) {
    Context polyglot = Context.newBuilder().allowAllAccess(true).build();
    String meanScriptContent = RUtils.getMeanScriptContent(); 
    polyglot.eval("R", meanScriptContent);
    Value rBindings = polyglot.getBindings("R");
    Value rInput = rBindings.getMember("c").execute(values);
    return rBindings.getMember("customMean").execute(rInput).asDouble();
}

このアプローチに従う場合、コードがJVMと緊密に結合されることに注意してください。 GraalVMの詳細については、 GraalJavaJITコンパイラに関する記事を参照してください。

7. 結論

この記事では、RをJavaに統合するための最も一般的なテクノロジーのいくつかを紹介しました。 総括する:

  • RCallerは、Maven Centralで利用できるため、統合が容易です。
  • Renjinはエンタープライズサポートを提供しており、ローカルマシンにRをインストールする必要はありませんが、GNURと100% c互換性はありません。
  • Rserveを使用して、リモートサーバーでRコードを実行できます
  • FastRを使用すると、Javaとのシームレスな統合が可能になりますが、コードはVMに依存するため、すべてのOSで使用できるわけではありません。

いつものように、このチュートリアルで使用されるすべてのコードは、GitHubから入手できます。