Рубрики
Без рубрики

Интеграция Java-R

Узнайте о некоторых наиболее распространенных способах интеграции R-кода в Java.

Автор оригинала: Donato Rimenti.

1. Обзор

R – популярный язык программирования, используемый для статистики. Поскольку он имеет широкий спектр доступных функций и пакетов, нет ничего необычного в том, чтобы встроить код R в другие языки.

В этой статье мы рассмотрим некоторые из наиболее распространенных способов интеграции R-кода в Java.

2. Сценарий 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. Вызывающий абонент

Первая библиотека, которую мы рассмотрим, – это , называемая , которая может выполнять код, порождая выделенный процесс R на локальной машине.

Поскольку RCaller доступен из Maven Central , мы можем просто включить его в ваш pom.xml :


    com.github.jbytecode
    RCaller
    3.0

Далее, давайте напишем пользовательский метод, который возвращает среднее значение наших значений с помощью нашего исходного сценария 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];
}

В этом методе мы в основном используем два объекта:

  • Код , который представляет наш контекст кода, включая нашу функцию, ее входные данные и оператор вызова
  • Напомним , который позволяет нам запустить наш код и получить результат обратно

Важно отметить, что Caller не подходит для небольших и частых вычислений из-за времени, необходимого для запуска процесса R. Это заметный недостаток.

Кроме того, Called работает только с R, установленным на локальном компьютере .

4. Ренджин

Renjin – еще одно популярное решение, доступное в ландшафте интеграции R. Он получил более широкое распространение, а также предлагает корпоративную поддержку .

Добавление Renjin в наш проект немного менее тривиально, так как мы должны добавить bedatadriven репозиторий вместе с зависимостью Maven:


    
        bedatadriven
        bedatadriven public repo
        https://nexus.bedatadriven.com/content/groups/public/
    



    
        org.renjin
        renjin-script-engine
        RELEASE
    

Еще раз, давайте создадим оболочку 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? Вот где Reserve вступает в игру, позволяя нам запускать код R на удаленной машине через TCP-сервер .

Настройка сервера включает в себя установку соответствующего пакета и запуск сервера, загружающего ваш сценарий, через консоль R:

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

Далее, теперь мы можем включить резерв в наш проект, как обычно, добавив зависимость Maven :


    org.rosuda.REngine
    Rserve
    1.8.1

Наконец, давайте обернем наш R – скрипт в метод Java. Здесь мы будем использовать объект Connection с нашим адресом сервера, по умолчанию равным 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. Быстрее

Последняя библиотека, о которой мы будем говорить, – это Быстрее . высокопроизводительная реализация R,построенная на GraalVM . На момент написания этой статьи Faster доступен только в системах Linux и Darwin x64 .

Для того, чтобы использовать его, нам сначала нужно установить Graal VM с официального сайта. После этого нам нужно установить его самостоятельно с помощью средства обновления компонентов Graal, а затем запустить сценарий конфигурации, который поставляется вместе с ним:

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

На этот раз наш код будет зависеть от Polyglot , внутреннего API виртуальной машины Graal для встраивания различных гостевых языков в Java. Поскольку Полиглот-это общий 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 . Чтобы узнать больше о Грэме, ознакомьтесь с нашей статьей о JIT-компиляторе Graal Java .

7. Заключение

В этой статье мы рассмотрели некоторые из самых популярных технологий интеграции R в Java. Подводить итоги:

  • RCaller легче интегрировать, так как он доступен в Maven Central
  • Renjin предлагает корпоративную поддержку и не требует установки R на локальном компьютере, но он не на 100% совместим с GNU R
  • Резерв можно использовать для выполнения кода R на удаленном сервере
  • Faster обеспечивает бесшовную интеграцию с Java, но делает наш код зависимым от виртуальной машины и доступен не для каждой ОС

Как всегда, весь код, используемый в этом учебнике, доступен на GitHub .