Get same output as R console in Java using JRI

2019-08-06 03:34发布

问题:

When I enter the following commands directly into the R console

library("xts")
mySeries <- xts(c(1.0, 2.0, 3.0, 5.0, 6.0), order.by=c(ISOdatetime(2001, 1, 1, 0, 0, 0), ISOdatetime(2001, 1, 2, 0, 0, 0), ISOdatetime(2001, 1, 3, 0, 0, 0), ISOdatetime(2001, 1, 4, 0, 0, 0), ISOdatetime(2001, 1, 5, 0, 0, 0)))
resultingSeries <- to.monthly(mySeries)
resultingSeries 

I will get an output like this

             mySeries.Open mySeries.High mySeries.Low mySeries.Close
Jan 2001             1             6            1              6

When I look into the attributes, I see the following output

attributes(resultingSeries)

$dim
[1] 1 4

$dimnames
$dimnames[[1]]
NULL

$dimnames[[2]]
[1] "mySeries.Open"  "mySeries.High"  "mySeries.Low"   "mySeries.Close"
$index
[1] 978307200
attr(,"tclass")
[1] "yearmon"
$tclass
[1] "POSIXct" "POSIXt" 
$tzone
[1] ""
$class
[1] "xts" "zoo"
$.indexCLASS
[1] "yearmon"

This is the same I get in Java. I'm wondering where the magic happens so that I see the nice output I get in R. I have no access to the event loop, since I'm using JRI like this (since, it's the recommended way and simplifies error handling):

REngine engine = REngine.engineForClass("org.rosuda.REngine.JRI.JRIEngine");
REXP result = engine.parseAndEval(...)

/edit In Java I execute each command from above as follows:

REXP result = engine.parseAndEval("resultingSeries") // or any other command

What I get is

org.rosuda.REngine.REXPDouble@4ac66122+[12]

The payload being doubles: 1, 6, 1, 6 The attributes are the same as specified above.

Now R does some magic to display the output above. Is there a way I can get the same output without having to create it manually by myself? Where's the implementation stored, that R gets the above mentioned output?

回答1:

Here is a piece of code that will work, here i extracted the first element of the field mySeries.Open from the object resultingSeries (which i converted to a data frame) which is equal to 1, notice that you can't pass all of the resultingSeries object strait into Java, you will need to break it down.

package stackoverflow;

import org.rosuda.JRI.REXP;
import org.rosuda.JRI.Rengine;
/**
 *
 * @author yschellekens
 */
public class StackOverflow {      
   public static void main(String[] args) throws Exception {
    String[] Rargs = {"--vanilla"};
    Rengine rengine = new Rengine(  Rargs, false, null);
    rengine.eval("library('xts')");
    rengine.eval("mySeries <- xts(c(1.0, 2.0, 3.0, 5.0, 6.0), order.by=c(ISOdatetime(2001, 1, 1, 0, 0, 0), ISOdatetime(2001, 1, 2, 0, 0, 0), ISOdatetime(2001, 1, 3, 0, 0, 0), ISOdatetime(2001, 1, 4, 0, 0, 0), ISOdatetime(2001, 1, 5, 0, 0, 0)))");
    rengine.eval("resultingSeries <- to.monthly(mySeries)");
    rengine.eval("resultingSeries<-as.data.frame(resultingSeries)");              
    REXP result= rengine.eval("resultingSeries$mySeries.Open");
    System.out.println("Greeting from R: "+result.asDouble());
   }
}

And the Java output:

run:
Greeting from R: 1.0


回答2:

I figured out the following workaround. The solution is far from perfect.

R offers a command to save its console output as characters vector.

capture.output( {command} )

We can access the output using

REXPString s = rengine.parseAndEval("capture.output( to.monthly(mySeries))")
String[] output = result.asStrings()

The variable output will contain all output lines

[0]             mySeries.Open mySeries.High mySeries.Low mySeries.Close
[1]Jan 2001             1             6            1              6

Alternatively you coud use JRIEngine and attack yourself to the event loop, which it did not want in my case (due to the more complicated error handling).



标签: r jri