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. How can I 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, it can be seen that Step Three in AssembleDataFile.java is intentionally left blank by me. How can I make the command line idea work at this point?

I would be open to another approach that does not involve adding any more dependencies to my code.

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.  How can I 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. How can I get 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.

1
  • 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. Commented Jan 13, 2012 at 23:35

5 Answers 5

20

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

Runtime.getRuntime().exec("Rscript myScript.R"); 
Sign up to request clarification or add additional context in comments.

10 Comments

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?
You may need the full path of the Rscript executable.
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?
Yes: the error message seems to be about the Rscript executable not being found.
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.
|
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.

5 Comments

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.
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.
Thank you for clarifying. +2 more for trying to help. I will examine your code more tomorrow, when I next work on this.
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.
@SimonUrbanek I also have similar problem here. If possible can you help me out over there?
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);
}

Comments

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();
        }

2 Comments

I cannot verify this at the moment. But +1 for taking the time to add insight to this old question.
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.
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.

8 Comments

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.
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?
I didn't single you out for anything, this answer came up in the LQP review queue. I didn't downvote you either.
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.
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 Stack Overflow about it. See this image
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.