您的位置:首页 > 编程语言 > Java开发

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

2017-09-22 16:10 597 查看
在 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 不够友好,没有跟上潮流啊。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  R Java Rserve
相关文章推荐