使用 Renjin 将 POJO 对象的 ArrayList 转换为 R 数据帧
Convert ArrayList of POJO objects to R dataframe with Renjin
我正在尝试使用 Renjin 从 Java 程序中存在的数据构建模型。我有一个 ArrayList
POJO 对象列表,其中每个属性是 String
、double
或 int
。如果我调用 toString()
,记录如下所示:
Record{id='uibbd923e5929b43', countryCode='FR', revenue=3.14159, count=1}
Record{id='uicdd967e5942b55', countryCode='GB', revenue=0.07, count=49}
...
我在 JVM 中实例化了 R,运行,如下所示:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Renjin");
...并将ArrayList
条记录放入R:
engine.put("records", records);
在 R 内部,记录存储为 <externalptr>
个对象的列表。可以看到存储在指针内的值的字符串表示形式,例如
engine.eval("print(data.frame(lapply(records, as.character), stringsAsFactors=FALSE))");
但是,我真的希望将这些存储为具有正确数据类型的数据框,而不是可以视为字符串的外部指针列表。
如何将 externalptr
的列表转换为数据框?
更新:
这是我蹩脚的解决方法,至少目前是这样。将数据写入 CSV:
CSVWriter writer = new CSVWriter(new FileWriter("tmp/output.csv"), '\t');
writer.writeNext(new String[] {"id", "countryCode", "revenue", "count"});
for (Record record : records){
writer.writeNext(new String[]{record.getId(),
record.getCountryCode(),
record.getRevenue().toString(),
record.getCount().toString()});
}
writer.close();
然后让 Renjin 将 CSV 读入数据框:
engine.eval("df <- read.table(\"tmp/output.csv\", header = TRUE)");
更新:
现在,我决定改用 Rserve,因为它提供了更多的灵活性。 Rserve(相对于 Renjin)的一个缺点是我们现在需要确保 R 是 运行 并且安装了必要的包。
这可能有助于作为一个小助手库,但目前,您可以 "manually" 在 Java 中逐步构建一个 data.frame以下方式:
StringArrayVector.Builder id = new StringArrayVector.Builder();
StringArrayVector.Builder country = new StringArrayVector.Builder();
DoubleArrayVector.Builder revenue = new DoubleArrayVector.Builder();
for(Record record : records) {
id.add(record.getId());
country.add(record.getCountry());
revenue.add(record.getRevenue());
}
ListVector.NamedBuilder myDf = new ListVector.NamedBuilder();
myDf.setAttribute(Symbols.CLASS, StringVector.valueOf("data.frame"));
myDf.setAttribute(Symbols.ROW_NAMES, new RowNamesVector(records.size());
myDf.add("id", id.build());
myDf.add("country", country.build());
myDf.add("revenue", revenue.build());
一个data.frame对象,正如你从上面看到的,实际上只是一个列的列表,所以需要一些摆弄才能得到Java个Bean的集合,这本质上是基于行的格式,到列的集合。
添加 "row.names" 属性也很重要,nrow() 等函数使用该属性来获取 data.frame 对象的维度。
上面的 RowNamesVector 是 StringVector 的专门实现,它根据需要计算 row.names“1”、“2”、“3”等,而无需为所有字符串分配内存。
我正在尝试使用 Renjin 从 Java 程序中存在的数据构建模型。我有一个 ArrayList
POJO 对象列表,其中每个属性是 String
、double
或 int
。如果我调用 toString()
,记录如下所示:
Record{id='uibbd923e5929b43', countryCode='FR', revenue=3.14159, count=1}
Record{id='uicdd967e5942b55', countryCode='GB', revenue=0.07, count=49}
...
我在 JVM 中实例化了 R,运行,如下所示:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("Renjin");
...并将ArrayList
条记录放入R:
engine.put("records", records);
在 R 内部,记录存储为 <externalptr>
个对象的列表。可以看到存储在指针内的值的字符串表示形式,例如
engine.eval("print(data.frame(lapply(records, as.character), stringsAsFactors=FALSE))");
但是,我真的希望将这些存储为具有正确数据类型的数据框,而不是可以视为字符串的外部指针列表。
如何将 externalptr
的列表转换为数据框?
更新:
这是我蹩脚的解决方法,至少目前是这样。将数据写入 CSV:
CSVWriter writer = new CSVWriter(new FileWriter("tmp/output.csv"), '\t');
writer.writeNext(new String[] {"id", "countryCode", "revenue", "count"});
for (Record record : records){
writer.writeNext(new String[]{record.getId(),
record.getCountryCode(),
record.getRevenue().toString(),
record.getCount().toString()});
}
writer.close();
然后让 Renjin 将 CSV 读入数据框:
engine.eval("df <- read.table(\"tmp/output.csv\", header = TRUE)");
更新:
现在,我决定改用 Rserve,因为它提供了更多的灵活性。 Rserve(相对于 Renjin)的一个缺点是我们现在需要确保 R 是 运行 并且安装了必要的包。
这可能有助于作为一个小助手库,但目前,您可以 "manually" 在 Java 中逐步构建一个 data.frame以下方式:
StringArrayVector.Builder id = new StringArrayVector.Builder();
StringArrayVector.Builder country = new StringArrayVector.Builder();
DoubleArrayVector.Builder revenue = new DoubleArrayVector.Builder();
for(Record record : records) {
id.add(record.getId());
country.add(record.getCountry());
revenue.add(record.getRevenue());
}
ListVector.NamedBuilder myDf = new ListVector.NamedBuilder();
myDf.setAttribute(Symbols.CLASS, StringVector.valueOf("data.frame"));
myDf.setAttribute(Symbols.ROW_NAMES, new RowNamesVector(records.size());
myDf.add("id", id.build());
myDf.add("country", country.build());
myDf.add("revenue", revenue.build());
一个data.frame对象,正如你从上面看到的,实际上只是一个列的列表,所以需要一些摆弄才能得到Java个Bean的集合,这本质上是基于行的格式,到列的集合。
添加 "row.names" 属性也很重要,nrow() 等函数使用该属性来获取 data.frame 对象的维度。
上面的 RowNamesVector 是 StringVector 的专门实现,它根据需要计算 row.names“1”、“2”、“3”等,而无需为所有字符串分配内存。