1

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

 1 year ago
source link: https://www.cnblogs.com/torima/p/16312798.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
  1. R语言和Python用于数据分析和数据处理,并生成相应的直方图和散点图
  2. 需要实现一个展示平台,后端使用Java,分别调用R语言和调用Python,并返回数据和图给前端显示
  3. 这个平台主要实现多维度数据的特征选择,以及数据集协变量偏移(Covariate shift)的纠正的功能
  4. 本质就是一个Java调用R语言以及Java调用Python的Demo,做得很简单,大神勿喷

2. 技术栈

  • Java 用的是 Springboot
  • 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

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

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

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

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK