• Java-调用R语言和调用Python(前后端展示)


    1. 背景

    1. R语言和Python用于数据分析和数据处理,并生成相应的直方图和散点图
    2. 需要实现一个展示平台,后端使用Java,分别调用R语言和调用Python,并返回数据和图给前端显示
    3. 这个平台主要实现多维度数据的特征选择,以及数据集协变量偏移(Covariate shift)的纠正的功能
    4. 本质就是一个Java调用R语言以及Java调用Python的Demo,做得很简单,大神勿喷

    2. 技术栈

    • Java 用的是 Springboot
    • R语言
    • Python
    • 前端用的是 Vue + ElementUI (前端只会点皮毛)
    • MySQL

    3. Java调用R语言

    3.1 R语言安装Rserve服务器

    在这之前需要分别对Java和R做些准备,首先是R语言安装Rserve服务器
    Java调用R语言时,Rserve需要启动,可以通过CMD命令行 / RStudio 执行

    # 安装Rserve
    install.packages("Rserve")
    # 载入Rserve
    library(Rserve())
    # 启动Rserve
    Rserve()
    

    这里使用CMD命令行展示启动Rserve,这样完成了Java调用R语言的第一步

    image


    3.2 Springboot添加Rsession依赖

    添加Rsession依赖之后就可以直接调包了

    <dependency>
       <groupId>com.github.yannrichet</groupId>
       <artifactId>Rsession</artifactId>
       <version>1.8.3</version>
    </dependency>
    

    3.3 Java调用R常用命令

    这里演示一些我需求中Java调用R的一些方式,其中包括一些比较常用的方法
    Java调用R的基本指令、R的图片如何保存并返回、R的结果如何获取和过滤等

    /**
    * 这里是Java调用R语言,R语言对多维度的数据进行特征选择,并将特征选择的结果返回,写入MySQL
    **/
    public List<Map<String,String>> featureSelection(){
        RConnection c = null;// RConnection用于和Rserve建立连接
    	try{
    		c = new RConncetion();// 建立连接
    
    		String RPath = "../featureSelection.R";// R文件的地址
    
    		c.assign("path",Rpath);// assign命令是将Rpath添加到R中,命名为path
    
    		c.eval("source(path)");// eval命令是执行R命令,这里则是执行source方法根据路径加载R文件
    
    		String Dpath = fileMapper.selectFilePath("train",1);// 通过MySQL获取数据集路径
    
    		String str = "rfProfile <- fsFunction('"+Dpath+"')";// R命令,执行我的R文件中的方法
    
    		c.eval(str);//执行
    
    		// 出图,因为是个Demo,图片我就直接存储在了本地,图片以数据集名称命名
    		String fileName = fileMapper.selectFileName("train", 1);//文件名
    
    		String imgPath = "D:/fileAndData/imgs/" + fileName + ".png";// 图片保存路径
    
    		c.assign("imgPath",imgPath);
    		c.eval("png(imgPath)");// 使用R语言的png()方法保存图片
    		c.assign("mainName",fileName);
    		c.eval("print(plot(rfProfile,type='b',main=mainName))");// 想要出图一定要套一个print(),不然会是空白
    		c.eval("dev.off()");// 出图这个也是必不可少,自行百度了解
    
    		// 获取特征选择的结果,结果使用String接收,需要通过正则表达式过滤一下我们需要的结果
    		c.eval("features <- rfProfile$optVariables");
    		// 获取R的结果使用的是paste()以及capture.output()方法,相当于把输出全捕获过来了
    		String feature = c.eval("paste(capture.output(features),collapse='\\n')").asString();
    		// 获取重要性得分
    		c.eval("impt <- varImp(rfProfile)");
    		String imptScores = c.eval("paste(capture.output(impt$Overall),collapse='\\n')").asString();
    
    		// 写了个工具类过滤R返回的结果,可以根据你的输出结果去定义
    		handlerRresults = new HandlerRresults();
    		List<Map<String, String>> stringStringMapList = handlerRresults.catchAndHandlerR(feature, imptScores);
    		fileMapper.deleteFileInfo(-1,"train");//-1 文件已使用
    		String featsStr = handlerRresults.getFeatsStr(feature);
    		featMapper.insertFeat(featsStr);
    		return stringStringMapList;
    	} catch (RserveException | REXPMismatchException e) {
    		e.printStackTrace();
    	} finally {
    		c.close(); // 一定要这一行!!!用完一定要关!!!
    	}
    	return null;
    }
    

    总结一个简易的Java调用R的模板,R语言是按行执行的,无情eval()

    public void JavaCallRDemo(){
    	RConnection c = null;
    	try{
    		c = new RConnection();
    		
    		c.assign();//通过Java添加变量至R
    		
    		c.eval();//Java执行R命令
    		
    	} catch (RserveException | REXPMismatchException e) {
    		e.printStackTrace();
    	} finally {
    		c.close();
    	}
    }
    

    3.4 Java调用R的特征选择前端演示

    我的数据集是30维的,结果选取了其中5个特征(Best trade-off)
    这里将特征及其对应的重要性得分通过表格的形式展示
    图片则是通过Base64转码的方式传给前端

    image


    4 Java调用Python

    4.1 Java调用Python代码部分

    Java调用Python,我使用的是Process类并通过Runtime调用其他进程
    Runtime可以调用cmd、shell等,这里我以我的项目为例稍作演示

    /**
    * Java使用Runtime调用python
    **/
    public String callPy(){
    	StringBuffer arr = new StringBuffer();// 用于获取结果
    	String basePath = "d://fileAndData/process/";// demo都是将文件直接存本地了,图方便
    	
    	// 以下为调用Python时传递的参数
    	String featName = featMapper.getFeat();
    	String trainPath = fileMapper.selectFilePath("train",-2);
    	String ptrainPath = basePath + fileMapper.selectFileName("train",-2);
    	String ptestPath = basePath + fileMapper.selectFileName("test",-2);
    	
    	Process proc; //声明一下Process
    	try{
    		// 字符串数组保存一下调用命令:1.使用python3 2.调用某个.py文件 3-6.传递的参数
    		String[] args = new String[]{"python3","../kmm.py",featName,trainPath,ptrainPath,ptestPath};
    		
    		proc = Runtime.getRuntime().exec(args);// 调用命令,cmd方式
    		
    		BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));// 得到输入流
    		
    		String line = null;
    		while((line = in.readLine())!=null){
    			arr.append(line).append("\n");// 写入
    		}
    		in.close();
    		proc.waitFor();
    	} catch (IOException | InterruptedException e) {
    		e.printStackTrace();
    	}
    	return arr.toString();
    }
    

    由于在我这个Demo中,Python脚本执行完成后的结果全是散点图
    我的做法是python直接把图保存本地,python执行完成后调用接口通过Base64格式传给前端
    后来发现其实也可以直接将返回的Base64格式的图片丢给前端,不用那么麻烦

    /**
    * 这里是一个我用于获取某个文件夹下所有文件,并转为Base64格式的方法
    * 因为我文件夹下只会有图片,我Demo也就只做了一个判空校验,直接开干
    * Controller层
    **/
    public List<String> getPyFigsListBase64(HttpServletResponse response){
    	String pyFilePath = "d://fileAndData/kmmImgs";// 图片本地路径
    	
    	List<String> res = new ArrayList<>();
    	
    	handlerPyresults = new HandlerPyresults();// 写个了工具类
    	
    	List<File> pyFiles = handlerPyresults.getAllFile(pyFilePath);// 获取所有文件
    	
    	for(File file : pyFiles) {
    		byte[] fig = handlerPyresults.file2Byte(file);// file类型转为byte[]类型
    		String base64str = Base64.encodeBase64String(fig);// byte[]转为base64
    		String img = "data:image/png;base64," + base64str;// 添加头,告诉前端这是个图片
    		res.add(img);
    	}
    	return res;
    }
    

    /**
    * file转byte[]
    **/
    public byte[] file2Byte(File file){
            if(file == null){
                return null;
            }
            FileInputStream fileInputStream = null;
            ByteArrayOutputStream byteArrayOutputStream = null;
            try {
                fileInputStream = new FileInputStream(file);
                byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] b = new byte[1024];
                int n;
                while ((n = fileInputStream.read(b))!=-1){
                    byteArrayOutputStream.write(b,0,n);
                }
                return byteArrayOutputStream.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fileInputStream.close();
                    byteArrayOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    

    4.2 Java调用Python结果演示

    我的python脚本主要是对数据集使用了KMM算法,是一种协变量偏移纠正的方法
    通过散点图反映测试集和训练集之间的分布情况和差异,这里略...

    image


    5. 总结

    这个项目是我硕士期间导师丢给我的一个需求,这里说一下为什么要用Java调用R语言和Python。

    1. 首先我有一个伽马射线的二分类任务,通过R语言使用多个传统机器学习模型实现。

    2. 在此之前使用R语言实现了多维度数据集的数据预处理、特征选择等功能,并且出图方便,代码简单。

    3. Python则实现了数据集协变量偏移纠正的功能,最终得到的数据集用于丢进模型做分类。

    4. 这个平台通过调用R和Python,集成了数据集预处理、协变量偏移纠正的方法,并且可以通过多个图可视化看到分析结果。平台还实现了数据集上传、下载等功能...

    5. 主要是针对Java调用R语言以及调用Python作一个记录,实际上平台有许多细节都没有顾虑到,相当于一个学习笔记吧。

  • 相关阅读:
    MSP430如何给板子下载程序?(IAR MSPFET CCS)
    C++——list的模拟实现
    SpringBoot如何配置拦截器呢?
    CYEZ 模拟赛 5
    脚本实用性工具介绍
    【Linux】基础IO(万字详解) —— 系统文件IO | 文件描述符fd | 重定向原理
    Android SELinux访问向量规则
    G. The Morning Star
    ActivitiListener
    FastReport .NET验证器以检查报告模板错误
  • 原文地址:https://www.cnblogs.com/torima/p/16312798.html