1. Обзор
R — популярный язык программирования, используемый для статистики. Поскольку он имеет широкий спектр доступных функций и пакетов, нередко требуется встраивать код R в другие языки.
В этой статье мы рассмотрим некоторые наиболее распространенные способы интеграции кода R в Java.
2. R-скрипт
Для нашего проекта мы начнем с реализации очень простой функции R, которая принимает вектор в качестве входных данных и возвращает среднее значение его значений. Мы определим это в специальном файле:
customMean <- function(vector) {
mean(vector)
}
В этом руководстве мы будем использовать вспомогательный метод Java для чтения этого файла и возврата его содержимого в виде строки
:
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 доступен в Maven Central , мы можем просто включить его в наш 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];
}
В этом методе мы в основном используем два объекта:
RCode
, который представляет контекст нашего кода, включая нашу функцию, ее ввод и оператор вызова.RCaller
, который позволяет нам запустить наш код и получить результат обратно
Важно отметить, что RCaller не подходит для небольших и частых вычислений из-за времени, которое требуется для запуска процесса R. Это заметный недостаток.
Также RCaller работает только с R, установленным на локальной машине .
4. Ренджин
Renjin — еще одно популярное решение, доступное в сфере интеграции R. Он получил более широкое распространение, а также предлагает корпоративную поддержку .
Добавление Renjin в наш проект немного менее тривиально, поскольку нам нужно добавить репозиторий Mulesoft
вместе с зависимостью Maven:
<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>
Давайте еще раз создадим Java-оболочку для нашей функции R:
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();
}
Как мы видим, концепция очень похожа на RCaller, хотя и менее многословна , поскольку мы можем вызывать функции напрямую по имени с помощью метода eval
.
Основное преимущество Renjin заключается в том, что он не требует установки R, поскольку использует интерпретатор на основе JVM. Однако в настоящее время Renjin не на 100% совместим с GNU R.
5. Резерв
Библиотеки, которые мы рассмотрели до сих пор, являются хорошим выбором для локального запуска кода. Но что, если мы хотим, чтобы наш R-скрипт вызывали несколько клиентов? Вот где Rserve вступает в игру, позволяя нам запускать R-код на удаленной машине через TCP-сервер .
Настройка Rserve включает в себя установку соответствующего пакета и запуск сервера, загружающего наш скрипт, через консоль R:
> install.packages("Rserve")
...
> library("Rserve")
> Rserve(args = "--RS-source ~/script.R")
Starting Rserve...
Теперь мы можем включить Rserve в наш проект, как обычно, добавив зависимость Maven :
<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 . высокопроизводительная реализация R, построенная на GraalVM . На момент написания этой статьи FastR доступен только в системах Linux и Darwin x64 .
Чтобы использовать его, нам сначала нужно установить GraalVM с официального сайта. После этого нам нужно установить сам FastR с помощью Graal Component Updater, а затем запустить конфигурационный скрипт, который идет вместе с ним:
$ bin/gu install R
...
$ languages/R/bin/configure_fastr
На этот раз наш код будет зависеть от Polyglot , внутреннего API GraalVM для встраивания различных гостевых языков в Java. Поскольку 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, ознакомьтесь с нашей статьей о Graal Java JIT Compiler .
7. Заключение
В этой статье мы рассмотрели некоторые из самых популярных технологий для интеграции R в Java. Подводить итоги:
- RCaller легче интегрировать, поскольку он доступен на Maven Central.
- Renjin предлагает корпоративную поддержку и не требует установки R на локальном компьютере, но он не на 100% совместим с GNU R.
- Rserve можно использовать для выполнения кода R на удаленном сервере.
- FastR обеспечивает бесшовную интеграцию с Java, но делает наш код зависимым от виртуальной машины и доступен не для каждой ОС.
Как всегда, весь код, используемый в этом руководстве, доступен на GitHub .