Morpheus 库旨在促进涉及大型数据集的高性能分析软件的开发,以便在 Java 虚拟机 (JVM) 上进行离线和实时分析。该库是用 Java 8 编写的,广泛使用了 lambda,但所有 JVM 语言都可以访问。
有关示例的详细文档,请参阅此处
Morpheus 的核心提供了一种称为DataFrame
的多功能二维内存高效表格数据结构,类似于 R 中首次流行的结构。虽然 R、Python 和 Matlab 等动态类型科学计算语言非常适合进行研究,但它们并不适合适合大规模生产系统,因为它们变得极难维护,并且重构起来很危险。 Morpheus 库试图保留DataFrame
概念的强大功能和多功能性,同时提供更加类型安全和自描述的接口集,这将使开发、维护和扩展代码复杂性变得更加容易。
Morpheus 库的另一个优点是,鉴于 Java 虚拟机强大的线程功能,它非常擅长在多核处理器架构上进行扩展。 Morpheus DataFrame
上的许多操作可以通过简单地在您想要操作的实体上调用parallel()
来无缝地并行运行,就像 Java 8 Streams 一样。在内部,这些并行实现基于 Fork & Join 框架,并且随着 CPU 核心的添加,某些类型的操作的性能得到了近乎线性的改进。
Morpheus DataFrame
是一种列存储结构,其中每一列都由 Morpheus Array
表示,该数组有多种实现方式,包括密集、稀疏和内存映射版本。 Morpheus 数组经过优化,并且尽可能由原始本机 Java 数组支持(即使是LocalDate
、 LocalDateTime
等类型),因为从存储、访问和垃圾收集的角度来看,它们的效率要高得多。内存映射的 Morpheus Arrays
虽然仍处于实验阶段,但允许使用由文件支持的堆外存储来创建非常大的DataFrames
。
虽然 Morpheus DataFrame
的完整功能集仍在不断发展,但已经有许多强大的 API 可以轻松影响复杂的转换和分析操作。有一些标准函数可以计算汇总统计量、执行各种类型的线性回归、应用主成分分析 (PCA),仅举几例。 DataFrame
在行和列维度上都有索引,允许数据沿任一轴高效排序、切片、分组和聚合。
Morpheus 还旨在提供一种标准机制来加载来自各种数据提供者的数据集。希望该 API 能够受到社区的欢迎,以扩大支持的数据源目录。目前,提供程序可以从 Quandl、美联储、世界银行、雅虎财经和谷歌财经加载数据。
考虑可在此处访问的机动车辆特征数据集。下面的代码将此 CSV 数据加载到 Morpheus DataFrame
中,过滤行以仅包含功率重量比 > 0.1(其中重量转换为千克)的车辆,然后添加一列来记录高速公路和高速公路之间的相对效率城市里程 (MPG),按此新添加的列按降序对行进行排序,最后将转换后的结果记录到 CSV 文件中。
DataFrame . read (). csv ( options -> {
options . setResource ( "http://zavtech.com/data/samples/cars93.csv" );
options . setExcludeColumnIndexes ( 0 );
}). rows (). select ( row -> {
double weightKG = row . getDouble ( "Weight" ) * 0.453592d ;
double horsepower = row . getDouble ( "Horsepower" );
return horsepower / weightKG > 0.1d ;
}). cols (). add ( "MPG(Highway/City)" , Double . class , v -> {
double cityMpg = v . row (). getDouble ( "MPG.city" );
double 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 的功能性质,其中许多方法返回类型实际上是DataFrame
,因此允许这种形式的方法链接。在此示例中,方法csv()
、 select()
、 add()
和sort()
均返回一个框架。在某些情况下,该方法所操作的帧是同一帧,或者在其他情况下,是所操作的帧的过滤器或浅表副本。本示例中转换后的数据集的前 10 行如下所示,新添加的列出现在框架的最右侧。
索引 |制造商 |型号|类型 |最低价格 |价格|最高价格 | MPG.city | MPG. 公路 |安全气囊 |传动系统 |气缸 |发动机尺寸 |马力 |转速|每英里转速 | Man.trans.avail |油箱容量 |乘客 |长度 |轴距|宽度|转圈|后座空间 |行李室 |重量 |产地 |制作| MPG(高速公路/城市)| -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- 9 |凯迪拉克|德维尔 |大| 33.0000 | 34.7000 | 36.3000 | 16 | 16 25 | 25仅司机 |前| 8 | 4.9000 | 200 | 200 4100 | 4100 1510 | 1510没有 | 18.0000 | 6 | 206 | 206 114 | 114 73 | 73 43 | 43 35 | 35 18 | 18 3620 | 3620美国 |凯迪拉克帝维尔 | 1.5625 | 1.5625 10 | 10凯迪拉克|塞维利亚 |中型| 37.5000 | 40.1000 | 42.7000 | 16 | 16 25 | 25司机和乘客|前| 8 | 4.6000 | 295 | 295 6000 | 1985 |没有 | 20.0000 | 5 | 204 | 204 111 | 111 74 | 74 44 | 44 31 | 14 | 14 3935 | 3935美国 |凯迪拉克塞维利亚 | 1.5625 | 1.5625 70 | 70奥兹莫比尔 |八十八 |大| 19.5000 | 20.7000 | 20.7000 21.9000 | 19 | 19 28 | 28仅司机 |前| 6 | 3.8000 | 170 | 170 4800 | 1570 | 1570没有 | 18.0000 | 6 | 201 | 201 111 | 111 74 | 74 42 | 42 31.5 | 31.5 17 | 17 3470 | 3470美国 |奥兹莫比尔八十八 | 1.47368421 | 74 | 74庞蒂亚克|火鸟 |运动型 | 14.0000 | 17.7000 | 21.4000 | 19 | 19 28 | 28司机和乘客|后部| 6 | 3.4000 | 160 | 160 4600 | 4600 1805 | 1805是的 | 15.5000 | 15.5000 4 | 196 | 196 101 | 101 75 | 75 43 | 43 25 | 25 13 | 3240 | 3240美国 |庞蒂亚克火鸟 | 1.47368421 | 6 |别克|勒萨布雷 |大| 19.9000 | 20.8000 | 21.7000 | 19 | 19 28 | 28仅司机 |前| 6 | 3.8000 | 170 | 170 4800 | 1570 | 1570没有 | 18.0000 | 6 | 200 | 200 111 | 111 74 | 74 42 | 42 30.5 | 30.5 17 | 17 3470 | 3470美国 |别克LeSabre | 1.47368421 | 13 |雪佛兰 |科迈罗|运动型 | 13.4000 | 15.1000 | 16.8000 | 19 | 19 28 | 28司机和乘客|后部| 6 | 3.4000 | 160 | 160 4600 | 4600 1805 | 1805是的 | 15.5000 | 15.5000 4 | 193 | 193 101 | 101 74 | 74 43 | 43 25 | 25 13 | 3240 | 3240美国 |雪佛兰科迈罗 | 1.47368421 | 76 | 76庞蒂亚克|博纳维尔 |大| 19.4000 | 24.4000 | 29.4000 | 19 | 19 28 | 28司机和乘客|前| 6 | 3.8000 | 170 | 170 4800 | 1565 | 1565没有 | 18.0000 | 6 | 177 | 177 111 | 111 74 | 74 43 | 43 30.5 | 30.5 18 | 18 3495 | 3495美国 |庞蒂亚克博纳维尔| 1.47368421 | 56 | 56马自达| RX-7 |运动型 | 32.5000 | 32.5000 | 32.5000 | 17 | 17 25 | 25仅司机 |后部|旋转| 1.3000 | 1.3000 255 | 255 6500 | 2325 | 2325是的 | 20.0000 | 2 | 169 | 169 96 | 96 69 | 69 37 | 37不适用 |不适用 | 2895 | 2895非美国 |马自达RX-7 | 1.47058824 | 18 | 18雪佛兰 |克尔维特 |运动型 | 34.6000 | 38.0000 | 41.5000 | 17 | 17 25 | 25仅司机 |后部| 8 | 5.7000 | 300 | 300 5000 | 1450 | 1450是的 | 20.0000 | 2 | 179 | 179 96 | 96 74 | 74 43 | 43不适用 |不适用 | 3380 | 3380美国 |雪佛兰克尔维特 | 1.47058824 | 51 | 51林肯 |镇_车 |大| 34.4000 | 36.1000 | 37.8000 | 18 | 18 26 | 26司机和乘客|后部| 8 | 4.6000 | 210 | 210 4600 | 4600 1840 | 1840没有 | 20.0000 | 6 | 219 | 219 117 | 117 77 | 77 45 | 45 31.5 | 31.5 22 | 22 4055 | 4055美国 |林肯镇_汽车 | 1.44444444 |
Morpheus API 包含一个回归接口,以便使用 OLS、WLS 或 GLS 将数据拟合到线性模型。下面的代码使用上一个示例中引入的相同汽车数据集,并在EngineSize上回归马力。该代码示例将模型结果打印到标准输出(如下所示),然后创建一个清晰显示回归线的散点图。
//Load the data
DataFrame < Integer , String > data = DataFrame . read (). csv ( options -> {
options . setResource ( "http://zavtech.com/data/samples/cars93.csv" );
options . setExcludeColumnIndexes ( 0 );
});
//Run OLS regression and plot
String regressand = "Horsepower" ;
String regressor = "EngineSize" ;
data . regress (). ols ( regressand , regressor , true , model -> {
System . out . println ( model );
DataFrame < Integer , String > 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 ();
});
=================================================== ============================================= 线性回归结果 =================================================== ============================================= 型号:OLS R 平方:0.5360 观察值:93 R 平方(调整后):0.5309 DF 模型:1 F 统计量:105.1204 DF 残差:91 F 统计量(概率):1.11E-16 标准错误:35.8717 运行时间(毫秒)52 杜宾-沃森:1.9591 =================================================== ============================================= 索引 |参数| STD_错误| T_STAT | P_值 | CI_LOWER | CI_上部 | -------------------------------------------------- -------------------------------------------------------- 拦截| 45.2195 | 45.2195 10.3119 | 4.3852 | 3.107E-5 | 3.107E-5 24.736 | 24.736 65.7029 | 65.7029 发动机尺寸 | 36.9633 | 36.9633 3.6052 | 3.6052 10.2528 | 7.573E-17 | 7.573E-17 29.802 | 29.802 44.1245 | 44.1245 =================================================== =============================================
通过英国政府开放数据计划,可以访问从 1995 年至今的所有英国住宅房地产交易记录。数据以CSV格式呈现,包含众多列,包括交易日期、支付价格、完全合格的地址(包括邮政编码)、房产类型、租赁类型等信息。
首先,我们编写一个函数来从 Amazon S3 存储桶加载这些 CSV 文件,由于它们每年存储一个文件,因此我们相应地提供了一个参数化函数。鉴于我们的分析需求,不需要加载文件中的所有列,因此下面我们只选择读取索引为1、2、4和11处的列。此外,由于文件不包含标题,我们将列重新命名为更有意义的名称,以使后续访问更加清晰。
/**
* 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 ) {
String 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
DataFrame < Year , String > 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 );
DataFrame < Integer , String > prices = loadHousePrices ( year );
prices . rows (). select ( row -> {
//Filter rows to include only apartments in the relevant cities
final String propType = row . getValue ( "PropertyType" );
final String city = row . getValue ( "City" );
final String 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
final String city = groupKey . item ( 0 );
final double priceStat = group . colAt ( "PricePaid" ). stats (). median ();
results . data (). setDouble ( year , city , priceStat );
});
});
//Map row keys to LocalDates, and map values to be percentage changes from start date
final DataFrame < LocalDate , String > plotFrame = results . mapToDoubles ( v -> {
final double firstValue = v . col (). getDouble ( 0 );
final double currentValue = v . getDouble ();
return ( currentValue / firstValue - 1d ) * 100d ;
}). rows (). mapKeys ( row -> {
final Year 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 的更多详细信息,请参阅此处有关可视化的部分和此处的代码。
Morpheus 已发布到 Maven Central,因此可以轻松地将其添加为您选择的构建工具中的依赖项。代码库目前分为 5 个存储库,以允许每个模块独立发展。核心模块被恰当地命名为 morpheus-core,它是所有其他模块所依赖的基础库。各种 Maven 工件如下:
睡眠核心
包含 Morpheus Arrays、DataFrames 和其他关键接口和实现的基础库。
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-core</ artifactId >
< version >${VERSION}</ version >
</ dependency >
睡眠可视化
用于在图表和表格中显示DataFrames
可视化组件。
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-viz</ artifactId >
< version >${VERSION}</ version >
</ dependency >
睡眠库德尔
从 Quandl 加载数据的适配器
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-quandl</ artifactId >
< version >${VERSION}</ version >
</ dependency >
睡眠谷歌
从 Google Finance 加载数据的适配器
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-google</ artifactId >
< version >${VERSION}</ version >
</ dependency >
雅虎睡眠
从雅虎财经加载数据的适配器
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-yahoo</ artifactId >
< version >${VERSION}</ version >
</ dependency >
已使用 Google 网上论坛设置了问答论坛,可在此处访问
可以在此处在线访问 Morpheus Javadocs。
可以在此处访问持续集成构建服务器,该服务器在每次合并后构建代码。
Morpheus 是根据 Apache Software Foundation License Version 2 发布的。