Вызов сценария R из Java
Я хотел бы вызвать скрипт R из Java. Я провел поиск в Google по этой теме, но почти все результаты, которые я видел, потребовали бы от меня добавления зависимости к какой-либо сторонней библиотеке. Может кто-нибудь показать мне хороший способ сделать то же самое, не добавляя каких-либо зависимостей в мой код?
Я использую Windows-машину, поэтому, возможно, я мог бы использовать командную строку для запуска R (если она еще не открыта) и для запуска конкретного R-скрипта. Но я никогда не писал код командной строки (или вызывал его из Java), поэтому мне понадобятся примеры кода.
Я включил рабочий пример кода, который я написал для одного возможного подхода ниже, используя мою идею командной строки. В моих комментариях ниже вы можете видеть, что третий шаг в AssembleDataFile.java специально оставлен мной пустым. Если вы думаете, что можете заставить идею командной строки работать, то, пожалуйста, покажите мне, какой код написать на третьем шаге.
Кроме того, не стесняйтесь предлагать другой подход, который, мы надеемся, не требует добавления каких-либо дополнительных зависимостей в мой код.
И, как всегда, я очень ценю любые ссылки, которые вы можете публиковать на статьи / руководства / и т.д., связанные с этим вопросом.
Вот что у меня так далеко:
AssembleDataFile.java
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
public class AssembleDataFile {
static String delimiter;
static String localPath = "C:\\test\\cr\\";
static String[][] myDataArray;
public static void main(String[] args) {
String inputPath = localPath+"pd\\";
String fileName = "MSData.txt";
delimiter = "\\t";
// Step One: Import data in two parts
try {
// 1A: get length of data file
BufferedReader br1 = new BufferedReader(new FileReader(inputPath+fileName));
int numRows = 0;
int numCols = 0;
String currentRow;
while ((currentRow = br1.readLine()) != null) {
numRows += 1;
numCols = currentRow.split(delimiter).length;}
br1.close();
//1B: populate data into array
myDataArray = new String[numRows][numCols+1];
BufferedReader br2 = new BufferedReader(new FileReader(inputPath+fileName));
String eachRow;
int rowIdx = 0;
while ((eachRow = br2.readLine()) != null) {
String[] splitRow = eachRow.split(delimiter);
for(int z = 0;z < splitRow.length;z++){myDataArray[rowIdx][z] = splitRow[z];}
rowIdx += 1;}
br2.close();
// Step Two: Write data to csv
String rPath = localPath+"r\\";
String sFileName = rPath+"2colData.csv";
PrintWriter outputWriter = new PrintWriter(sFileName);
for(int q = 0;q < myDataArray.length; q++){
outputWriter.println(myDataArray[q][8]+", "+myDataArray[q][9]);
}
outputWriter.close();
//Step Three: Call R script named My_R_Script.R that uses 2ColData.csv as input
// not sure how to write this code. Can anyone help me write this part?
// For what it is worth, one of the R scripts that I intend to call is included below
//
//added the following lines here, per Vincent's suggestion:
String rScriptFileName = rPath+"My_R_Script.R";
Runtime.getRuntime().exec("mypathto\\R\\bin\\Rscript "+rScriptFileName);
//
//
//Step Four: Import data from R and put it into myDataArray's empty last column
try {Thread.sleep(30000);}//make this thread sleep for 30 seconds while R creates the needed file
catch (InterruptedException e) {e.printStackTrace();}
String matchFileName = rPath+"Matches.csv";
BufferedReader br3 = new BufferedReader(new FileReader(matchFileName));
String thisRow;
int rowIndex = 0;
while ((thisRow = br3.readLine()) != null) {
String[] splitRow = thisRow.split(delimiter);
myDataArray[rowIndex][numCols] = splitRow[0];
rowIndex += 1;}
br3.close();
//Step Five: Check work by printing out one row from myDataArray
//Note that the printout has one more column than the input file had.
for(int u = 0;u<=numCols;u++){System.out.println(String.valueOf(myDataArray[1][u]));}
}
catch (FileNotFoundException e) {e.printStackTrace();}
catch (IOException ie){ie.printStackTrace();}
}
}
My_R_Script.R
myCSV <- read.csv(file="2colData.csv",head=TRUE,sep=",")
pts = SpatialPoints(myCSV)
Codes = readShapeSpatial("mypath/myshapefile.shp")
write.csv(ZipCodes$F[overlay(pts,Codes)], "Matches.csv", quote=FALSE, row.names=FALSE)
РЕДАКТИРОВАТЬ:
Вот сообщение об ошибке, которое выдается, когда я добавляю Runtime.getRuntime(). Exec("Rscript "+rScriptFileName); к коду выше:
java.io.IOException: Cannot run program "Rscript": CreateProcess error=2, The system cannot find the file specified
at java.lang.ProcessBuilder.start(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at AssembleDataFile.main(AssembleDataFile.java:52)
Caused by: java.io.IOException: CreateProcess error=2, The system cannot find the file specified
at java.lang.ProcessImpl.create(Native Method)
at java.lang.ProcessImpl.<init>(Unknown Source)
at java.lang.ProcessImpl.start(Unknown Source)
... 5 more
ВТОРОЕ РЕДАКТИРОВАНИЕ: Код выше теперь работает, потому что я последовал советам Винсента. Тем не менее, мне пришлось ввести команду сна, чтобы дать сценарию R достаточно времени для запуска. Без команды sleep приведенный выше код Java выдает ошибку, говорящую о том, что файл Matches.csv не существует. Я обеспокоен тем, что 30-секундный период сна - слишком грубый инструмент. Может кто-нибудь показать мне код, который заставляет Java-программу ждать, пока R-программа не сможет создать Matches.csv? Я не решаюсь использовать инструменты потоков, потому что я прочитал, что плохо спроектированные потоки могут привести к ошибкам, которые почти невозможно локализовать и исправить.
5 ответов
Вы просто хотите вызвать внешнее приложение: не будет ли работать следующее?
Runtime.getRuntime().exec("Rscript myScript.R");
Вы можете легко адаптировать этот код: http://svn.rforge.net/org/trunk/rosuda/REngine/Rserve/test/StartRserve.java
Помимо прочего, он находит R и запускает фиксированный скрипт в R - вы можете заменить этот скрипт на ваш скрипт и игнорировать два последних метода.
Не ждите окончания процесса с Thread.sleep()
...
Использовать waitFor()
метод вместо.
Process child = Runtime.getRuntime().exec(command, environments, dataDir);
int code = child.waitFor();
switch (code) {
case 0:
//normal termination, everything is fine
break;
case 1:
//Read the error stream then
String message = IOUtils.toString(child.getErrorStream());
throw new RExecutionException(message);
}
BufferedReader reader = null;
Process shell = null;
try {
shell = Runtime.getRuntime().exec(new String[] { "/usr/bin/Rscript", "/media/subin/works/subzworks/RLanguage/config/predict.R" });
reader = new BufferedReader(new InputStreamReader(shell.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
... потребовал бы, чтобы я добавил зависимость в какую-нибудь стороннюю библиотеку...
Почему это так плохо? Вы говорите так: "... потребовалось бы, чтобы я напал на медоносника с бейсбольной битой..." Я не вижу вреда, особенно если это работает.
Может быть, RCaller может помочь вам. Не требуется JNI.