使用 Rserve 实现 R 程序的复杂数据返回

来源:互联网 发布:淘宝卖家退货流程 编辑:程序博客网 时间:2024/05/16 06:58

在 RCaller 无法返回复杂数据的研究 我们知道 RCaller 无法处理复杂数据的返回,那么就看看其他工具吧,比如 Rserve。

使用下来的感觉是 Rserve 虽然能把值都给你,但是怎么获取却是一个艰难的过程,比如和 RCaller 一样,对矩阵值的读取也是先列后行,定位到具体的值的变量上,API 显然没有与时俱进,不用泛型,使用古老的 Vector 类型,经常要强制转型,要不是 debug, 我怎么知道那个列是什么数据类型啊。

private RList exeRMatchCode(PropensityMatchInput input, Map<String, List<String>> varResultMap,List<String> covairateNames) {logger.info("entering exeRMatchCode()");try {RConnection rc = new RConnection();for (String key : varResultMap.keySet()){logger.info("key is :: {}", key);List<String> varVals = varResultMap.get(key);String[] valStrs = new String[varVals.size()];int num = 0;for (String s : varVals){valStrs[num++] = s;}rc.assign(key, valStrs);}logger.info("各个变量值的数组已经加入到了 R 的变量里");StringBuffer sb = new StringBuffer();StringBuffer cbind = new StringBuffer();cbind.append("matrix <- cbind(").append(OUTCOME_VARIABLE).append(",").append(StringUtils.join(covairateNames, ",")).append(")\n");sb.append(cbind.toString());sb.append("df <- as.data.frame(matrix)\n");sb.append("library(MatchIt)\n");String replaceStr = input.isReplacement() ? "TRUE" : "FALSE";StringBuffer propensity = new StringBuffer();propensity.append("fm <- matchit(").append(OUTCOME_VARIABLE).append(" ~ ").append(StringUtils.join(covairateNames, " + ")).append(", data = df, method = \"").append(input.getAlgorithm().getValue()).append("\", caliper = ").append(input.getCaliper()).append(", replace = ").append(replaceStr).append(", ratio = ").append(input.getMatchRatio()).append(")\n");sb.append(propensity.toString());sb.append("result <- summary(fm)\n");sb.append("sum <- result$sum.all\n");sb.append("mat <- result$sum.matched\n");sb.append("red <- result$reduction\n");sb.append("ss <- result$nn\n");sb.append("mData <- match.data(fm)[1]\n");sb.append("out <- list(sum, mat, red, ss, mData)\n");String code = sb.toString();logger.info("完整的 Match 算法的 R 程序:: \n {}", code);REXP rexp = rc.eval(code);logger.info("完成 Propensity Score Match 匹配逻辑");return rexp.asList();} catch (Exception e) {logger.error(e.getMessage());throw new RuntimeException("执行 Propensity Score Match 匹配出错了");}}

将变量值数组传入 Rserve 里使用 assign 函数,对于独立的语句比如 “library(MatchIt)” 也可以直接使用:

rc.eval("library(MatchIt)");
这里我将多条语句放入缓存池,然后一起 eval , 注意:需要在每一行末尾加上换行符,模拟命令行的执行。执行完,从 list 里面取出各个对象:

logger.info("将 Map 对象里的数据注入到 R 程序里,同时执行 R 的匹配算法");RList result = exeRMatchCode(input, varResultMap, covairateNames);logger.info("从 R 的匹配结果里取出需要的数据,放入到 R 对应的 java 对象备用");REXPGenericVector sumAllVector = (REXPGenericVector)result.elementAt(0);REXPGenericVector matchedVector = (REXPGenericVector)result.elementAt(1);REXPGenericVector balImproveVector = (REXPGenericVector)result.elementAt(2);REXPDouble sampleSize = (REXPDouble)result.elementAt(3);REXPGenericVector matchedPersonsVector = (REXPGenericVector)result.elementAt(4);

下面就挑其中一个来看看,怎么获取每个具体的值:

// 由于 R 对应的对象在取 R 矩阵的值,读取是先列后行,所有数据处理比较特殊private Collection<PropensitySum> extractMatchVectorData(REXPGenericVector vector,LinkedList<String> variableNamesR) {LinkedHashMap<String, PropensitySum> map = new LinkedHashMap<>();for (String name : variableNamesR){map.put(name, new PropensitySum());}RList t =  vector.asList();Vector names = t.names;for (int i = 0; i < names.size(); i++){String name = ((String)names.get(i)).trim();double[] vals = ((REXPDouble)t.elementAt(i)).asDoubles();if ("Means Treated".equalsIgnoreCase(name)){for (int j = 0; j < vals.length; j++){map.get(variableNamesR.get(j)).setMeansTreated(vals[j]);}} else if ("Means Control".equalsIgnoreCase(name)){for (int j = 0; j < vals.length; j++){map.get(variableNamesR.get(j)).setMeansControl(vals[j]);}} else if ("SD Control".equalsIgnoreCase(name)){for (int j = 0; j < vals.length; j++){map.get(variableNamesR.get(j)).setSdControl(vals[j]);}} else if (name.startsWith("Mean Diff")){ // 这个列名比较特殊, Bal Improve 矩阵这个列名后面有个点号for (int j = 0; j < vals.length; j++){map.get(variableNamesR.get(j)).setMeanDiff(vals[j]);}} else if ("eQQ Med".equalsIgnoreCase(name)){for (int j = 0; j < vals.length; j++){map.get(variableNamesR.get(j)).seteQQMed(vals[j]);}} else if ("eQQ Mean".equalsIgnoreCase(name)){for (int j = 0; j < vals.length; j++){map.get(variableNamesR.get(j)).seteQQMean(vals[j]);}} else if ("eQQ Max".equalsIgnoreCase(name)){for (int j = 0; j < vals.length; j++){map.get(variableNamesR.get(j)).seteQQMax(vals[j]);}} }return map.values();}

可以看到,有强转型,有数组下标取值,就这两个就觉得程序耦合性太高了。总之,感觉 API 不够友好,没有跟上潮流啊。


原创粉丝点击