Java调用基于 R 的 One-Way ANOVA检测

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Allen_jinjie/article/details/77982273

本文主要说下面三点:

1. R 的 ANOVA 测试默认是基于 Type I Sum of Squares,而 SPSS 默认则是 Type III 平方和的,所以需要对 R 做修改;

2. 普通应用上,都是准备好一个 excel 文件,里面包含一个二维矩阵作为 SPSS / R 的数据输入;而 java 则是逐个计算变量 (列)的值,怎么构建 java -> R 的数据桥梁?

3. java 应用调用 R 函数要使用一些开源包,比如 RCaller,其提供的 API 只能从 R 结果集里返回一个对象句柄,如何将多个值打包返回呢?


对于第一点,请看 Ensuring R Generates the Same ANOVA F-values as SPSS ,里面详述了 R 的客户端的设置使得 ANOVA 分析使用 Type III Sum of Squares 而非默认的 Type I Sum of Squares。

以前我都是将所有变量的值写到硬盘文件,然后使用 R  的 read.csv / read.table 的方式读取数据,但这个方式引入了文件操作,比较麻烦。借助 R 的 cbind 函数不单可以将 Java 的变量值数组合并成一个 matrix, 然后使用 as.table 将其转成 R 函数需要的 data frame 就可以绕开读写 CSV / EXCEL 文件的麻烦了。

同样的,RCaller 只有一个 runAndReturnResult() 方法,该方法只能传入一个 R 的结果对象句柄,如果要返回多个,就需要 cbind 将这多个对象的值放到一个 matrix 里返回。

至此,第二,三两点都解决了。

下面贴上我的一个案例的代码,参数 group, values 是两个变量的值数组,返回类型是 ANOVA 测试的多个统计值在一起的数组:

private double[] exeAnovaTest(String varName, int[] group, double[] values) {
	RCaller caller = initRCaller(new RCallerTemplate() {
		@Override
		public void addRCode(RCode code) {				
			code.addIntArray("group", group);
			code.addDoubleArray(varName, values);
			
			StringBuffer cbind = new StringBuffer();
			cbind.append("matrix <- cbind(group, ").append(varName).append(")");
			code.addRCode(cbind.toString());
			code.addRCode("tab <- as.table(matrix)");
			/*
			 * 1. Set each independent variable as a factor
			 * 2. Set the default contrast to helmert
			 */
			code.addRCode("tab$group <- as.factor(tab$group)");
			code.addRCode("options(contrasts = c('contr.helmert', 'contr.poly'))");
			code.addRCode("library(car)");
			
			StringBuffer fm = new StringBuffer();
			fm.append("fm <- aov(").append(varName).append(" ~ group, data = tab)");
			code.addRCode(fm.toString());
			// 使用 Type III 型平方和(让 R 和 SPSS 一样的机制),进行 Anova 检测
			code.addRCode("ret <- Anova(fm, type=3)"); 
			
			// 完成 Anova 测试,从结果里面取值,注意:R 的下标是  1-based, 不同意 java 的  0-based!!!
			code.addRCode("SumSq <- ret$Sum[2]");
			code.addRCode("df <- ret$Df[2]");
			code.addRCode("fvalue <- ret$Df[2]");
			code.addRCode("pvalue <- ret$F[2]");
			code.addRCode("out <- cbind(SumSq, df, fvalue, pvalue)");
		}
	});
	caller.runAndReturnResult("out");
	
	return caller.getParser().getAsDoubleArray("out");
}

但是跑的时候报了下面的一个错,出错行在 tab$group <- as.factor(tab$group):

R command failed with error. Reason: Error in tab$factor : $ operator is invalid for atomic vectors

Google 才知道是因为 cbind 后创建了一个数组,数组是不可以使用 "$" 符号引用的,R Error in x$ed : $ operator is invalid for atomic vectors 里面说可以使用 tab["<列名>"]的方式引用。所以改成了 tab["group"] <- as.factor(tab["group"]) ,然后再跑,又报了下面这个错:

Error in dimnames(x) <- dnx : 'dimnames'只能用于陈列 
Calls: aov ... val -> eval -> data.frame -> do.call -> provideDimnames
停止执行

这个一下子也找不出原因,还是对比自己的代码和最前面那个链接的页面的代码,发现怎么修改 Type III 型的例子里面是直接从 CSV 文件里读取数据的,那么我的问题是否出在 as.table 函数上? 就试着将 as.table 换成了 as.data.frame 后,程序就可以顺利执行了。也就是将 exeAnovaTest() 里面的 
code.addRCode("tab <- as.table(matrix)");

改成:

code.addRCode("tab <- as.data.frame(matrix)");


猜你喜欢

转载自blog.csdn.net/Allen_jinjie/article/details/77982273