2012-01-13 29 views
12

Me gustaría llamar a un script R desde Java. He realizado búsquedas en Google sobre el tema, pero casi todos los resultados que he visto me obligarían a agregar una dependencia a una biblioteca de terceros. ¿Alguien puede mostrarme una buena manera de lograr lo mismo sin agregar dependencias a mi código?llamando al script R desde java

Estoy usando una máquina de Windows, así que tal vez podría usar la línea de comando para iniciar R (si no está ya abierta) y ejecutar un script R específico. Pero nunca he escrito el código de línea de comando (o lo llamé desde Java), así que necesitaría ejemplos de código.

Estoy incluyendo el código de muestra de trabajo que escribí para un posible enfoque a continuación, utilizando mi idea de línea de comando. En mis comentarios en línea a continuación, puede ver que Paso tres en AssembleDataFile.java fue dejado en blanco intencionalmente por mí. Si crees que puedes hacer que la idea de línea de comando funcione, entonces muéstrame qué código escribir en el Paso Tres.

Además, siéntase libre de sugerir otro enfoque que, afortunadamente, no implique agregar más dependencias a mi código.

Y, como siempre, agradezco mucho cualquier enlace que pueda publicar en artículos/tutoriales/etc. relacionados con esta pregunta.

Esto es lo que tengo hasta ahora:

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:
Aquí está el mensaje de error que se lanza cuando agrego Runtime.getRuntime(). Exec ("Rscript" + rScriptFileName); para el código anterior:

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  

SEGUNDA EDICIÓN: El código anterior funciona ahora porque he seguido las sugerencias de Vincent. Sin embargo, tuve que poner un comando de suspensión para darle al guión R tiempo suficiente para ejecutar. Sin el comando de suspensión, el código de Java anterior arroja un error que dice que el archivo Matches.csv no existe. Me preocupa que un período de sueño de 30 segundos sea demasiado duro para un instrumento. ¿Alguien puede mostrarme el código que hace que el programa java espere hasta que el programa R tenga la oportunidad de crear Matches.csv? Dudo en utilizar las herramientas de subprocesos porque he leído que los subprocesos mal diseñados pueden causar errores que son casi imposibles de localizar y corregir.

+2

cuanto a la espera para el trabajo a fin: Usted puede ver el ID de terminación del proceso. También puede simplemente sondear la existencia (o inexistencia) de algún archivo creado especialmente. – Iterator

Respuesta

14

Simplemente desea llamar a una aplicación externa: ¿no funcionaría lo siguiente?

Runtime.getRuntime().exec("Rscript myScript.R"); 
+1

Muchas gracias por intentar ayudar. +1 por darme una idea específica. Ejecuté su código, y está arrojando un mensaje de error, que publiqué arriba en una edición de mi publicación original. También agregué su código a la sección del código de mi publicación original anterior, para que pueda ver lo que hice. Tal vez no estoy entendiendo correctamente. ¿Puedes mostrarme cómo corregir mi código para que funcione arriba? – CodeMed

+1

Puede necesitar la ruta completa del ejecutable de Rscript. –

+0

Tengo la ruta completa de myScript.R comprobé para asegurarme de eso antes de publicar el mensaje de error. ¿Te refieres a la ruta completa de R? – CodeMed

1

... me requeriría añadir una dependencia a alguna biblioteca de terceros ...

Por qué es tan malo? Lo haces sonar como "... me requeriría atacar a un pajarraco con un bate de béisbol ..." No veo el daño, especialmente si funciona.

Tal vez RCaller puede ayudarlo. No se requiere JNI.

+0

Si bien este enlace puede responder a la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página vinculada cambia. – durron597

+0

Disculpe, pero el que está justo debajo de mí también incluye un enlace. Lo mismo ocurre con el de abajo. ¿Por qué me elegiste para un comentario? – duffymo

+0

No te indiqué por nada, esta respuesta apareció en la cola de revisión de LQP. Yo tampoco te devolví el voto. – durron597

5

se puede adaptar fácilmente este código: http://svn.rforge.net/org/trunk/rosuda/REngine/Rserve/test/StartRserve.java

Entre otras cosas se encuentra R y ejecuta un script fija en I - se puede sustituir con ese guión con su guión y pasar por alto los dos últimos métodos.

+0

Gracias por esta sugerencia y por su comentario sobre la sugerencia de Vincent. Su sugerencia anterior implica agregar otra dependencia a mi código. Estoy más interesado en el comentario que usted escribió en la sugerencia de Vincent. Gracias de nuevo. – CodeMed

+1

Eso no es una dependencia: el código anterior muestra cómo obtener la ubicación de R en Windows y cómo ejecutar R en cualquier sitio; es solo código real que hace lo que describí en el comentario. No tiene nada que ver con Rserve, salvo que es parte de los ejemplos, por eso te dije que ignorases los últimos métodos que tienen que ver con Rserve; de ​​eso no se trataba esta respuesta. Reemplaza la "biblioteca (Rserve)" con su propio código R. –

+0

Gracias por aclarar. +2 más por tratar de ayudar. Examinaré tu código más mañana, cuando vuelva a trabajar en esto. – CodeMed

4

no espere a que el proceso finalice con Thread.sleep() ...

utilizar el método de waitFor() en su lugar.

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

No puedo verificar esto en este momento. Pero +1 por tomarse el tiempo para agregar una idea a esta vieja pregunta. – CodeMed

+0

Eso no funcionó para mí, pero cambiando el argumento para 'exec' de' String [] 'a' String' del formulario 'C:/Program Files/path/to/the/Rscript.exe C:/path to the/something.R' hizo el truco. – Antoine