19

I would like to call an R script from Java. I have done google searches on the topic, but almost all of the results I have seen would require me to add a dependency to some third party library. Can anyone show me a good way to accomplish the same thing without adding any dependencies to my code?

I am using a windows machine, so perhaps I might use the command line to start R (if it is not already open) and to run a specific R script. But I have never written command line code (or called it from Java) so I would need code examples.

I am including working sample code that I wrote for one possible approach below, using my command line idea. In my in-line-comments below, you can see that Step Three in AssembleDataFile.java is intentionally left blank by me. If you think that you can make the command line idea work, then please show me what code to write in Step Three.

Also, feel free to suggest another approach that, hopefully, does not involve adding any more dependencies to my code.

And, as always, I very much appreciate any links you might post to articles/tutorials/etc related to this question.

Here is what I have so far:

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)

EDIT:
Here is the error message that is being thrown when I add Runtime.getRuntime().exec("Rscript "+rScriptFileName); to the code above:

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    

SECOND EDIT: The code above now works because I followed Vincent's suggestions. However, I had to put in a sleep command in order to give the R script enough time to run. Without the sleep command, the java code above throws an error saying that the Matches.csv file does not exist. I am concerned that a 30 second sleep period is too rough of an instrument. Can anyone show me code that gets the java program to wait until the R program has a chance to create Matches.csv? I hesitate to use thread tools because I have read that poorly designed threads can cause bugs that are nearly impossible to localize and fix.

CodeMed
  • 9,527
  • 70
  • 212
  • 364
  • 2
    Regarding waiting for the job to finish: You can watch for the process ID to terminate. You can also just poll for the existence (or non-existence) of some specially created file. – Iterator Jan 13 '12 at 23:35

5 Answers5

20

You just want to call an external application: wouldn't the following work?

Runtime.getRuntime().exec("Rscript myScript.R"); 
Vincent Zoonekynd
  • 31,893
  • 5
  • 69
  • 78
  • 1
    Thank you very much for trying to help. +1 for giving me a targeted idea. I ran your code, and it is throwing an error message, which I posted above in an edit to my original posting. I also added your code to the code section of my original posting above, so that you can see what I did. Perhaps I am not understanding correctly. Can you please show me how to fix my code so that it works above? – CodeMed Jan 13 '12 at 02:43
  • 1
    You may need the full path of the Rscript executable. – Vincent Zoonekynd Jan 13 '12 at 02:53
  • I have the full path of myScript.R I checked to make sure of that before posting the error message. Do you mean the full path of R? – CodeMed Jan 13 '12 at 03:10
  • 1
    Yes: the error message seems to be about the `Rscript` executable not being found. – Vincent Zoonekynd Jan 13 '12 at 03:37
  • 1
    on Windows `R` and `Rscript` is not on the `PATH` normally so you'll have to do some legwork to find it. It can be obtained from the Windows registry which is a pain from Java, but can be done via the `reg` utility albeit in a complicated manner. – Simon Urbanek Jan 13 '12 at 08:56
  • @Vincent Zoonekynd, Thank you again. I solved that issue, and now it is giving a different error which I think has to do with my R code not recognizing the right directories. So I am working on the problem within R. I might post something else if I do not figure it out myself. Another +1 for trying to help. – CodeMed Jan 13 '12 at 21:40
  • @Simon Urbanek, Thank you. +1 for trying to help. I actually solved that problem and am working on to others. Thank you again. – CodeMed Jan 13 '12 at 21:42
  • @Vincent Zoonekynd, I just updated the code in my original post above. I got your suggestion to work, but only by making my java program sleep for 30 seconds while it waits for the R script to create the needed Matches.csv file. Can you suggest any better code for getting the java program to wait for the R script to create Matches.csv? – CodeMed Jan 13 '12 at 23:05
  • @VincentZoonekynd also have similar problem [here](http://stackoverflow.com/questions/32618048/not-able-to-execute-r-script-from-java-program). If possible can you help me out over there? – user1950349 Sep 16 '15 at 21:40
  • @SimonUrbanek I don't understand how to use reg, I run my code like "Runtime.getRuntime().exec("reg Rscript myScript.R") , it doesn't work. – M_L_Sing_Jump_Rap Mar 09 '23 at 08:31
6

Do not wait for the process to finish with Thread.sleep()...

Use the waitFor() method instead.

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);
}
Draken
  • 3,134
  • 13
  • 34
  • 54
Vasily
  • 116
  • 1
  • 3
6

You can easily adapt this code: http://svn.rforge.net/org/trunk/rosuda/REngine/Rserve/test/StartRserve.java

Among other things it finds R and runs a fixed script in R - you can replace that script with with your script and ignore the last two methods.

Simon Urbanek
  • 13,842
  • 45
  • 45
  • Thank you for this suggestion, and for your comment on Vincent's suggestion. Your suggestion above involves adding another dependency to my code. I am more interested in the comment that you wrote in Vincent's suggestion. Thank you again. – CodeMed Jan 13 '12 at 21:44
  • 1
    That's not a dependency - the code above shows how to get the location of R on Windows and how to run R anywhere - it is just actual code doing what I described in the comment. It has nothing to do with Rserve itself except that it is part of the examples - that's why I told you to ignore the last methods that have to do with Rserve - that's not what this answer was about. You replace the "library(Rserve)" with your own R code. – Simon Urbanek Jan 13 '12 at 23:49
  • Thank you for clarifying. +2 more for trying to help. I will examine your code more tomorrow, when I next work on this. – CodeMed Jan 14 '12 at 05:39
  • I added a while loop based on your suggested code, and that seems to fix the last remaining problem in my code. The while loop enables java to wait until R has created Matches.csv, but then enables java to start again as soon as Matches.csv is created, because there is a break command in the while loop. Thank you. – CodeMed Jan 16 '12 at 22:52
  • @SimonUrbanek I also have similar problem [here](http://stackoverflow.com/questions/32618048/not-able-to-execute-r-script-from-java-program). If possible can you help me out over there? – user1950349 Sep 16 '15 at 21:15
3
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();
        }
subinksoman
  • 426
  • 4
  • 20
  • I cannot verify this at the moment. But +1 for taking the time to add insight to this old question. – CodeMed Oct 08 '15 at 15:42
  • That did not worked for me, but changing the argument for `exec` from `String[]` to `String` of the form `C:/Program Files/path/to/the/Rscript.exe C:/path to the/something.R` did the trick. – Antoine Jan 28 '16 at 10:25
2

...would require me to add a dependency to some third party library...

Why is that so bad? You make it sound like "...would require me to assault a honeybadger with a baseball bat..." I don't see the harm, especially if it works.

Maybe RCaller can help you. No JNI required.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – durron597 Aug 19 '15 at 19:26
  • Excuse me, but the one right below me also includes a link. So does the one below that. Why did you single me out for a comment? – duffymo Aug 19 '15 at 19:42
  • I didn't single you out for anything, this answer came up in the LQP review queue. I didn't downvote you either. – durron597 Aug 19 '15 at 19:46
  • You did single me out. There were two others that did precisely the same thing b/c it's the right thing to do. Comment on theirs as well. – duffymo Aug 19 '15 at 19:47
  • 1
    If your answer comes up in the Low Quality Posts queue and I don't happen to open the complete question, seeing the other answers, that's not singling you out. That said, I have downvoted and flagged the other link only answer now. – durron597 Aug 19 '15 at 19:49
  • There are two. Did you do the same for all of us? Maybe the queue isn't the best mechanism. SO hits it right often, but the narrow view seems unfair and narrow to me. – duffymo Aug 19 '15 at 19:52
  • 1
    You aren't supposed to Not An Answer flag a Link Only answer, only Very Low Quality, (it needs to be 0 score or lower), so I didn't flag the other one. I did downvote it though. Also, if you are unhappy with how the LQP Queue works, I invite you to post on [meta] about it. See [this image](http://i.stack.imgur.com/gJ2nT.png) – durron597 Aug 19 '15 at 19:54
  • I did give something besides a link. Telling someone that their reluctance to add a dependency is off base in this case is worth knowing. – duffymo Aug 19 '15 at 19:57