分享

RUtils

 tingx 2016-01-11

  • 注1:RUtils是我偶然发现的一个工具包,它建立在Rserve之上,可以很大程度上简化我们的程序,关于Rserve网络上有很多相关的内容,这里不对其进行介绍,比如这里:http://blog./r-rserve-java/
  • 注2:以下内容有很大一部分是“翻译”自官方网站,详情请点开“参考资料”中的链接查看。


简介


        RUtils是一个用于Java连接和使用R语言的工具集,它使用Rserve并在其上添加了”池“功能,简化了多个主机运行多个Rserve进程实例时的配置和使用;另外RUtils还对Rserve的API进行了封装,使我们发送R脚本命令及获取Rserve返回的计算结果等操作可以用非常简洁的代码来完成。

官方主页:http://icb.med./wiki/index.php/RUtils


依赖


RUtils依赖下面几个软件/项目:

  1. JDK1.6+
  2. R语言
  3. Rserve
  4. Apache Commons CLI
  5. Apache Commons Configuration
  6. Apache Commons I/O
  7. Apache Commons Lang
  8. Apache Commons Logging

RUtils的配置文件


        可用的Rserve实例在RUtils中被配置到一个很简洁的XML文件中,XML文件的根元素为RConnectionPool,每个Rserve实例体现为一个RServer节点,每个Rserver节点可以包含以下几个属性:
  1. host:Rserve正在运行的主机名/IP(必需)
  2. port:Rserve监听的TCP端口(默认为6311)
  3. username:建立连接时的用户名(如果需要的话)
  4. password:建立连接时的密码
  5. command:远程主机上启动Rserve的命令全路径
  6. embedded:如果设置为TRUE,RUtils将在连接池初始化时启动Rserve,并在JVM关闭时关闭Rserve服务。这样就不需要手动起停R服务,R服务将在我们应用的Java进程启动之后以守护线程的方式启动。

下面是一个RUtils配置文件的简单示例:

  1. <RConnectionPool>  
  2.     <RConfiguration>  
  3.         <RServer host="localhost"/>  
  4.         <RServer host="127.0.0.1" port="6312"/>  
  5.         <RServer host="med." port="1234" username="me" password="pwd"/>  
  6.     </RConfiguration>  
  7. </RConnectionPool>  

        指定配置文件最好的方式是添加一个key为RConnectionPool.configuration的系统属性,属性值需要指定为classpath下一个合法的资源文件,如果该系统属性没有指定,则默认配置文件为RConnectionPool.xml。


从连接池中获取连接


        连接池类RConnectionPool被设计为单例模式,因此我们不能调用其构造方法对其进行实例化,但我们可以调用该类的静态方法getInstance()来获取RConnectionPool类的实例。从连接池中获取连接可以调用RConnectionPool类的borrowConnection()方法或者borrowConnection(long,java.util.concurrent.TimeUnit)方法,两个方法在连接可用的情况下都会立即返回一个合法的RConnection实例。不带参数的方法在没有连接可用的情况会阻塞;另外一个方法将会等待直到给定时间参数超时,这时方法将返回null。
从连接池中获取的连接在使用完毕后不需要关闭,以保证下次获取时可以重用。如果返回了被关闭的连接将导致下次获取连接时重新建立连接,从而会影响性能。

        当Java进程结束时,为了断开所有的连接连接池应该以尽可能简单的方式关闭。这可以通过调用RConnectionPool类的shutdown方法进行。如果程序没有显式的调用shutdown方法,一个JVM关闭钩子程序将做这些操作。


在Java中调用R脚本


        一旦你拥有了运行中的RServe服务和RConnectionPool.xml配置文件,就可以从Java中调用R了。我们当然可以通过RConnectionPool获取RConnection连接,然后发送R脚本命令从而与Rserve交互,但这样做在R脚本复杂的情况下会很繁琐。更简单的方式就是使用RScript类,如果我们使用RScript来执行R脚本的话不需要自己去获取连接,一切都在RScript中做完了,我们需要做只有下面几项:
  • 1. 指定一个要执行的R脚本
  • 2. 为该脚本指定输入和输出值
  • 3. 执行脚本及获取R返回到Java中的输出值

下面分别来介绍一下这三步的详细情况:


1、 指定要执行的脚本

RScript类提供了两个工厂方法来指定要运行的R脚本,二者都返回一个封装了指定R脚本的RScript对象:
  • RScript.createFromResource(path to resource) 从CLASSPATH中加载R脚本
  • RScript.createFromScriptString(script string) 使用字符串构建R脚本

使用字符串构建R脚本示例:

  1. final String ksTest = "q <- ks.test(x,y)\n"  
  2.             + "p_value <- q$p.value\n"  
  3.             + "test_statistic <- q$statistic[[1]]";  
  4. final RScript rscript = RScript.createFromScriptString(ksTest);  

如果要使用预先定义的R脚本文件构建RScript的话,只需要将脚本文件放在CLASSPATH下,RScript会自动寻找并加载。


2、 为脚本绑定输入输出参数

        很多情况下我们在调用R脚本需要传入一些参数,同时也可能需要接收R脚本运行得到的结果,RScript为我们提供了setInput方法和setOutput方法分别来绑定输入和输出变量;其中setInput方法有多个重载方法以支持不同的数据类型,而setOutput则使用RDataObjectType枚举类来定义输出变量的类型(见第4点)。在上面的示例中,x和y都是输入参数(double数组类型),而我们要取得变量p_value和test_statistic的值也需要绑定输出变量,所以我们需要为RScript绑定输入输出参数,如下所示:

  1. final double[] xValues = new double[] {0.1, 0.2, 0.3, 0.4, 0.5};  
  2. final double[] yValues = new double[] {0.6, 0.7, 0.8, 0.9, 1.0};  
  3. // 为脚本指定输入变量的名字和值  
  4. rscript.setInput("x", xValues);  
  5. rscript.setInput("y", yValues);  
  6. // 为脚本输出指定变量名和类型, 输出必须在脚本执行前指定  
  7. rscript.setOutput("p_value", RDataObjectType.Double);  
  8. rscript.setOutput("test_statistic", RDataObjectType.Double);  


3、 执行脚本并获取返回值

        经过上面两步后,所剩下的也就只有执行R脚本以及获取脚本运行的返回值了。运行脚本通过调用RScript类的execute方法进行;而针对获取不同类型的返回值RScript则提供了一系列的getOutputXXX()方法,其中XXX代表数据类型。如下所示:

  1. rscript.execute();  
  2. final double pvalue = rscript.getOutputDouble("p_value");  
  3. final double testStat = rscript.getOutputDouble("test_statistic");  


4、 RDataObjectType 枚举

RUtils使用RDataObjectType枚举类定义了R和Java之间通信支持的数据类型(主要用在绑定输出变量上,以获取R脚本的计算结果),其定义的数据类型有:
  • RDataObjectType.String 在Java和R中都代表String;获取返回值时用rscript.getOutputString(R script variable name)
  • RDataObjectType.StringArray 在Java中代表String[],在R中则是c(string vals);获取返回值时调用rscript.getOutputStringArray(R script variable name)
  • RDataObjectType.Double 在Java和R中都代表Double;获取返回值时调用rscript.getOutputDouble(R script variable name)
  • RDataObjectType.DoubleArray 在Java中代表Double[],在R中则代表c(double vals);获取数据时调用rscript.getOutputDoubleArray(R script variable name)
  • RDataObjectType.Double2DArray 在Java中代表double[][],在R中则是matrix;获取返回值时调用rscript.getOutputDouble2DArray(R script variable name)

总结


        RUtils构建于REngine.jar之上,只是简化了其使用,原理也很简单。但如果不使用RUtils我们在调用R脚本文件或者获取各种类型的返回数据时会很麻烦,RUtils正是解决了这些问题,使我们调用Rserve的操作得以简化。

        其实RUtils执行脚本文件时是将脚本文件内容读到内存,构建成一个大的字符串(去掉空行和注释),在调用execute方法时将其发送到Rserve进行执行,这可以从RScript的createFromResource方法的源码看出来;

        RUtils绑定输入参数的原理也十分简单,在我们调用execute方法执行脚本时,如果我们设置了输入变量,那么RUtils会迭代这些变量,根据变量类型组成不同的R变量定义脚本语句,发送到Rserve执行(变量定义);然后才去执行真正的R脚本内容,因此在R解释器执行这些脚本内容时已经可以找到要使用的变量了,脚本因此得以顺利执行。这些可以从RScript源码中的execute方法中看到,execute方法在执行过程中调用了setInputs方法,该方法便做了如上所述的操作。

        RUtils绑定输出参数的原理类似于输入参数的原理,在execute方法执行之后,如果我们绑定了输出参数,那么RUtils会遍历所有绑定的输出参数,直接将参数名作为命令发送到Rserve;在R中如果直接将变量名传递给解释器,解释器会将这个变量的值输出,这时RUtils再获取Rserve的返回值,根据我们绑定输出参数时指定的数据类型封装成相应的Java对象。然后我们向RUtils要求获取输出时它们已经存在了,而且RUtils还会根据我们指定的数据类型进行转换。


参考资料

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多