Java pandas d3x-morpheus数据处理工具

d3x-morpheus

介绍

Morpheus 库旨在促进涉及大型数据集的高性能分析软件的开发,以便在Java 虚拟机(JVM)上进行离线和实时分析。该库是用 Java 8 编写的,广泛使用了 lambda,但所有 JVM 语言都可以访问。

目的

在其核心,Morpheus 提供了一种通用的二维内存高效表格数据结构,称为 a DataFrame,类似于R中首次普及的结构。虽然R、Python和Matlab等动态类型的科学计算语言非常适合进行研究,但它们不太适合大规模生产系统,因为它们变得极难维护,并且重构起来很危险。Morpheus 库试图保留这一DataFrame概念的强大功能和多功能性,同时提供更加类型安全和自我描述的接口集,这将使开发、维护和扩展代码复杂性变得更加容易。

Morpheus 库的另一个优势是,鉴于Java 虚拟机的强大线程功能,它非常擅长在多核处理器架构上进行扩展。Morpheus 上的许多操作可以通过简单地调用您希望操作的实体来无缝并行运行,就像Java 8 Streams一样。在内部,这些并行实现基于 Fork & Join 框架,随着 CPU 内核的增加,某些类型的操作的性能近乎线性提升。DataFrameparallel()

能力

MorpheusDataFrame是一种列存储结构,其中每一列都由一个 Morpheus 表示,MorpheusArray有许多实现,包括密集、稀疏和内存映射版本。Morpheus 数组经过优化,并尽可能得到原始原生 Java 数组的支持(甚至对于LocalDate,LocalDateTime等类型),因为从存储、访问和垃圾收集的角度来看,这些数组的效率要高得多。内存映射的 Morpheus虽然仍处于实验阶段,但允许使用由文件支持的堆外存储创建Arrays非常大的内存。DataFrames

虽然 Morpheus 的完整功能集DataFrame仍在不断发展,但已经有许多强大的 API 可以轻松影响复杂的转换和分析操作。有标准函数可以计算汇总统计数据,执行各种类型的线性回归,应用主成分分析(PCA) 等等。在行和列维度上都建立了索引,允许数据沿任一轴DataFrame进行高效排序、切片、分组和聚合。

Data Access

Morpheus 还旨在提供一种标准机制来加载来自各种数据提供者的数据集。希望这个 API 将被社区接受,以增加支持的数据源目录。目前,供应商已实施以允许从Quandl、美联储、世界银行、雅虎财经和谷歌财经加载数据。

Morpheus at a Glance

A Simple Example

考虑可在此处访问的机动车特征数据集。下面的代码将此 CSV 数据加载到 Morpheus 中DataFrame,过滤行以仅包括那些功率重量比 > 0.1(其中重量转换为千克)的车辆,然后添加一列以记录高速公路和城市之间的相对效率mileage (MPG),按照这个新增的列对行进行降序排序,最后将这个转换后的结果记录到一个CSV文件中。

DataFrame.read().csv(options -> {
    options.setResource("https://www.d3xsystems.com/public/data/samples/cars93.csv");
    options.setExcludeColumnIndexes(0);
}).rows().select(row -> {
    var weightKG = row.getDouble("Weight") * 0.453592d;
    var horsepower = row.getDouble("Horsepower");
    return horsepower / weightKG > 0.1d;
}).cols().add("MPG(Highway/City)", Double.class, v -> {
    var cityMpg = v.row().getDouble("MPG.city");
    var highwayMpg = v.row().getDouble("MPG.highway");
    return highwayMpg / cityMpg;
}).rows().sort(false, "MPG(Highway/City)").write().csv(options -> {
    options.setFile("/Users/witdxav/cars93m.csv");
    options.setTitle("DataFrame");
});

此示例演示了 Morpheus API 的功能特性,其中许多方法返回类型实际上是 a DataFrame,因此允许这种形式的方法链接。在此示例中,方法csv()、select()、add()和sort()都返回一个框架。在某些情况下,该方法在同一帧上运行,或者在其他情况下,过滤器或正在运行的帧的浅表副本。此示例中转换后的数据集的前 10 行如下所示,新添加的列显示在框架的最右侧。

回归示例

Morpheus API 包括一个回归接口,以便使用OLS、WLS或GLS将数据拟合到线性模型。下面的代码使用上一个示例中介绍的相同汽车数据集,并在EngineSize上回归Horsepower。代码示例将模型结果打印到标准输出,如下所示,然后创建一个清晰显示回归线的散点图。

//Load the data
var data = DataFrame.read().csv(options -> {
    options.setResource("https://www.d3xsystems.com/public/data/samples/cars93.csv");
    options.setExcludeColumnIndexes(0);
});

//Run OLS regression and plot
var regressand = "Horsepower";
var regressor = "EngineSize";
data.regress().ols(regressand, regressor, true, model -> {
    IO.println(model);
    var xy = data.cols().select(regressand, regressor);
    Chart.create().withScatterPlot(xy, false, regressor, chart -> {
        chart.title().withText(regressand + " regressed on " + regressor);
        chart.subtitle().withText("Single Variable Linear Regression");
        chart.plot().style(regressand).withColor(Color.RED).withPointsVisible(true);
        chart.plot().trend(regressand).withColor(Color.BLACK);
        chart.plot().axes().domain().label().withText(regressor);
        chart.plot().axes().domain().format().withPattern("0.00;-0.00");
        chart.plot().axes().range(0).label().withText(regressand);
        chart.plot().axes().range(0).format().withPattern("0;-0");
        chart.show();
    });
    return Optional.empty();
});

英国房价趋势

通过英国政府开放数据计划,可以访问从 1995 年至今的所有英国住宅房地产交易记录。数据以CSV格式呈现,包含众多栏目,包括交易日期、支付价格、完全合格地址(包括邮政编码)、房产类型、租赁类型等信息。

让我们首先编写一个函数来从 Amazon S3 存储桶中加载这些 CSV 文件,并且由于它们每年存储一个文件,因此我们相应地提供了一个参数化函数。鉴于我们分析的需求,不需要加载文件中的所有列,所以下面我们只选择读取索引为1、2、4、11的列。另外,由于文件不包含header ,我们将列重新命名为更有意义的名称,以使后续访问更加清晰。

/**
 * Loads UK house price from the Land Registry stored in an Amazon S3 bucket
 * Note the data does not have a header, so columns will be named Column-0, Column-1 etc...
 * @param year      the year for which to load prices
 * @return          the resulting DataFrame, with some columns renamed
 */
private DataFrame<Integer,String> loadHousePrices(Year year) {
    var resource = "http://prod.publicdata.landregistry.gov.uk.s3-website-eu-west-1.amazonaws.com/pp-%s.csv";
    return DataFrame.read().csv(options -> {
        options.setResource(String.format(resource, year.getValue()));
        options.setHeader(false);
        options.setCharset(StandardCharsets.UTF_8);
        options.setIncludeColumnIndexes(1, 2, 4, 11);
        options.getFormats().setParser("TransactDate", Parser.ofLocalDate("yyyy-MM-dd HH:mm"));
        options.setColumnNameMapping((colName, colOrdinal) -> {
            switch (colOrdinal) {
                case 0:     return "PricePaid";
                case 1:     return "TransactDate";
                case 2:     return "PropertyType";
                case 3:     return "City";
                default:    return colName;
            }
        });
    });
}

下面我们使用这些数据来计算1995 年至 2014 年期间英国部分最大城市的公寓的名义价格中位数(未经通胀调整) 。1993 年到 2014 年间未过滤的数据集中大约有 2000 万条记录,虽然加载和解析需要相当长的时间(大约 3.5GB 数据),但 Morpheus 执行代码的分析部分大约需要 5 秒(不包括加载时间)在 2013 年底购买的标准 Apple Macbook Pro 上。请注意我们如何使用并行处理通过调用来加载和处理数据results.rows().keys().parallel()。

//Create a data frame to capture the median prices of Apartments in the UK'a largest cities
var results = DataFrame.ofDoubles(
    Range.of(1995, 2015).map(Year::of),
    Array.of("LONDON", "BIRMINGHAM", "SHEFFIELD", "LEEDS", "LIVERPOOL", "MANCHESTER")
);

//Process yearly data in parallel to leverage all CPU cores
results.rows().keys().parallel().forEach(year -> {
    System.out.printf("Loading UK house prices for %s...\n", year);
    var prices = loadHousePrices(year);
    prices.rows().select(row -> {
        //Filter rows to include only apartments in the relevant cities
        var propType = row.getValue("PropertyType");
        var city = (String)row.getValue("City");
        var cityUpperCase = city != null ? city.toUpperCase() : null;
        return propType != null && propType.equals("F") && results.cols().contains(cityUpperCase);
    }).rows().groupBy("City").forEach(0, (groupKey, group) -> {
        //Group row filtered frame so we can compute median prices in selected cities
        var city = (String)groupKey.item(0);
        var priceStat = group.col("PricePaid").stats().median();
        results.setDouble(year, city, priceStat);
    });
});

//Map row keys to LocalDates, and map values to be percentage changes from start date
var plotFrame = results.mapToDoubles(v -> {
    var firstValue = v.col().getDoubleAt(0);
    var currentValue = v.getDouble();
    return (currentValue / firstValue - 1d) * 100d;
}).rows().mapKeys(row -> {
    var year = row.key();
    return LocalDate.of(year.getValue(), 12, 31);
});

//Create a plot, and display it
Chart.create().withLinePlot(plotFrame, chart -> {
    chart.title().withText("Median Nominal House Price Changes");
    chart.title().withFont(new Font("Arial", Font.BOLD, 14));
    chart.subtitle().withText("Date Range: 1995 - 2014");
    chart.plot().axes().domain().label().withText("Year");
    chart.plot().axes().range(0).label().withText("Percent Change from 1995");
    chart.plot().axes().range(0).format().withPattern("0.##'%';-0.##'%'");
    chart.plot().style("LONDON").withColor(Color.BLACK);
    chart.legend().on().bottom();
    chart.show();
});

下图显示了所选城市子集中公寓名义中位数价格的百分比变化。它表明伦敦并未因全球金融危机 (GFC) 而遭受任何名义房价下跌,但并非英国的所有城市都证明具有弹性。略微令人惊讶的是,与伦敦相比,一些不太富裕的北方城市在 2003 年至 2006 年期间的升值率更高。需要注意的一件事是,虽然伦敦没有看到任何名义价格下降,但由于英镑在全球金融危机期间对这些货币大幅贬值,因此欧元和美元肯定出现了相当严重的调整。

可视化

通过一个简单的图表抽象 API以及支持JFreeChart和Google Charts 的适配器(根据大众需求还有其他的),在 Morpheus 中可视化数据DataFrames变得容易。这种设计使得通过相同的编程接口生成交互式Java Swing图表以及基于 HTML5 浏览器的图表成为可能。有关如何使用此 API 的更多详细信息,请参阅此处的可视化部分和此处的代码。

Maven 引用

Morpheus 已发布到 Maven Central,因此可以轻松地将其作为依赖项添加到您选择的构建工具中。代码库被分成模块以更好地管理外部依赖性。核心库目前只有三个外部依赖,进一步模块化后将减少为零。目前可用的各种Maven引用如下:

Morpheus Core

包含 Morpheus 数组、DataFrames 和其他关键接口和实现的核心库。

<dependency>
    <groupId>com.d3xsystems</groupId>
    <artifactId>d3x-morpheus-core</artifactId>
    <version>1.0.31</version>
</dependency>

Morpheus Excel

将 Excel 电子表格加载到 Morpheus DataFrame 的适配器

<dependency>
    <groupId>com.d3xsystems</groupId>
    <artifactId>d3x-morpheus-excel</artifactId>
    <version>1.0.31</version>
</dependency>

Morpheus JSON

一种利用 Google GSON 库以高效 JSON 格式读写 Morpheus 数据帧的适配器。

<dependency>
    <groupId>com.d3xsystems</groupId>
    <artifactId>d3x-morpheus-json</artifactId>
    <version>1.0.31</version>
</dependency>

Morpheus DB

针对 SQL 数据存储读写 Morpheus 数据帧的适配器。

<dependency>
    <groupId>com.d3xsystems</groupId>
    <artifactId>d3x-morpheus-db</artifactId>
    <version>1.0.31</version>
</dependency>

Morpheus Visualization

在图表和表格中显示 Morpheus 的组件库DataFrames。

<dependency>
    <groupId>com.d3xsystems</groupId>
    <artifactId>d3x-morpheus-viz</artifactId>
    <version>1.0.31</version>
</dependency>

Morpheus Quandl

将数据从Quandl加载到 Morpheus DataFrame 的适配器

<dependency>
    <groupId>com.d3xsystems</groupId>
    <artifactId>d3x-morpheus-quandl</artifactId>
    <version>1.0.31</version>
</dependency>

Morpheus Worldbank

将数据从Worldbank加载到 Morpheus DataFrame 的适配器。

<dependency>
    <groupId>com.d3xsystems</groupId>
    <artifactId>d3x-morpheus-worldbank</artifactId>
    <version>1.0.31</version>
</dependency>