Pustaka Morpheus dirancang untuk memfasilitasi pengembangan perangkat lunak analitik berkinerja tinggi yang melibatkan kumpulan data besar untuk analisis offline dan real-time di Java Virtual Machine (JVM). Pustaka ini ditulis dalam Java 8 dengan penggunaan lambda yang ekstensif, tetapi dapat diakses oleh semua bahasa JVM.
Untuk dokumentasi terperinci beserta contohnya, lihat di sini
Pada intinya, Morpheus menyediakan struktur data tabular efisien memori dua dimensi serbaguna yang disebut DataFrame
, mirip dengan yang pertama kali dipopulerkan di R. Meskipun bahasa komputasi ilmiah yang diketik secara dinamis seperti R, Python & Matlab bagus untuk melakukan penelitian, namun bahasa tersebut tidak bagus untuk digunakan. cocok untuk sistem produksi skala besar karena sangat sulit dipelihara dan berbahaya untuk difaktorkan ulang. Pustaka Morpheus berupaya untuk mempertahankan kekuatan dan keserbagunaan konsep DataFrame
, sekaligus menyediakan rangkaian antarmuka yang jauh lebih aman dan dapat dijelaskan sendiri , yang akan membuat pengembangan, pemeliharaan, dan penskalaan kompleksitas kode menjadi lebih mudah.
Keuntungan lain dari perpustakaan Morpheus adalah ia sangat baik dalam melakukan penskalaan pada arsitektur prosesor multi-core mengingat kemampuan threading yang kuat dari Java Virtual Machine. Banyak operasi pada Morpheus DataFrame
yang dapat dijalankan secara paralel secara mulus hanya dengan memanggil parallel()
pada entitas yang ingin Anda operasikan, seperti halnya dengan Java 8 Streams. Secara internal, implementasi paralel ini didasarkan pada kerangka Fork & Join, dan peningkatan kinerja yang hampir linier diamati untuk jenis operasi tertentu seiring dengan penambahan inti CPU.
Morpheus DataFrame
adalah struktur penyimpanan kolom di mana setiap kolom diwakili oleh Morpheus Array
yang memiliki banyak implementasi, termasuk versi padat, jarang, dan dipetakan memori. Array Morpheus dioptimalkan dan sedapat mungkin didukung oleh array Java asli primitif (bahkan untuk tipe seperti LocalDate
, LocalDateTime
dll...) karena ini jauh lebih efisien dari sudut pandang penyimpanan, akses dan pengumpulan sampah. Morpheus Arrays
yang dipetakan memori, meskipun masih eksperimental, memungkinkan DataFrames
yang sangat besar dibuat menggunakan penyimpanan off-heap yang didukung oleh file.
Meskipun rangkaian fitur lengkap Morpheus DataFrame
masih terus berkembang, sudah terdapat banyak API canggih yang dapat memengaruhi transformasi kompleks dan operasi analitis dengan mudah. Terdapat fungsi standar untuk menghitung statistik ringkasan, melakukan berbagai jenis Regresi Linier, menerapkan Analisis Komponen Utama (PCA), dan masih banyak lagi. DataFrame
diindeks dalam dimensi baris dan kolom, memungkinkan data diurutkan , diiris , dikelompokkan , dan diagregasi secara efisien di sepanjang sumbu mana pun.
Morpheus juga bertujuan untuk menyediakan mekanisme standar untuk memuat kumpulan data dari berbagai penyedia data. Harapannya, API ini dapat dimanfaatkan oleh komunitas guna mengembangkan katalog sumber data yang didukung. Saat ini, penyedia diterapkan untuk memungkinkan data dimuat dari Quandl, The Federal Reserve, Bank Dunia, Yahoo Finance, dan Google Finance.
Pertimbangkan kumpulan data karakteristik kendaraan bermotor yang dapat diakses di sini. Kode di bawah memuat data CSV ini ke dalam Morpheus DataFrame
, memfilter baris agar hanya menyertakan kendaraan yang memiliki rasio daya terhadap berat > 0,1 (di mana berat diubah menjadi kilogram), lalu menambahkan kolom untuk mencatat efisiensi relatif antara jalan raya dan jarak tempuh kota (MPG), mengurutkan baris berdasarkan kolom yang baru ditambahkan ini dalam urutan menurun, dan terakhir mencatat hasil transformasi ini ke file 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" );
});
Contoh ini menunjukkan sifat fungsional dari Morpheus API, di mana banyak tipe pengembalian metode sebenarnya adalah DataFrame
dan oleh karena itu memungkinkan bentuk rangkaian metode ini. Dalam contoh ini, metode csv()
, select()
, add()
, dan sort()
semuanya mengembalikan sebuah frame. Dalam beberapa kasus, frame yang sama dimana metode tersebut beroperasi, atau dalam kasus lain, filter atau salinan dangkal dari frame yang sedang dioperasikan. 10 baris pertama kumpulan data yang diubah dalam contoh ini terlihat seperti berikut, dengan kolom yang baru ditambahkan muncul di ujung kanan bingkai.
Indeks | Pabrikan | Model | Ketik | Harga Minimal | Harga | Harga Maks | MPG.kota | MPG.jalan raya | Kantong Udara | Kereta Penggerak | Silinder | Ukuran Mesin | Tenaga Kuda | RPM | Rev.per.mil | Man.trans.avail | Kapasitas Tangki Bahan Bakar | Penumpang | Panjang | Jarak sumbu roda | Lebar | Putar.lingkaran | Ruang Kursi Belakang | Bagasi.ruang | Berat | Asal | Buat | MPG (Jalan Raya/Kota) | --------------------------------------------------- --------------------------------------------------- --------------------------------------------------- --------------------------------------------------- --------------------------------------------------- --------------------------------------------------- --------------------------------------------------- --------------------------------------------------- --------------------------------------------------- 9 | Cadillac | DeVille | Besar | 33.0000 | 34.7000 | 36.3000 | 16 | 25 | Hanya pengemudi | Depan | 8 | 4.9000 | 200 | 4100 | 1510 | Tidak | 18.0000 | 6 | 206 | 114 | 73 | 43 | 35 | 18 | 3620 | Amerika Serikat | Cadillac DeVille | 1.5625 | 10 | Cadillac | Sevilla | Ukuran sedang | 37.5000 | 40.1000 | 42.7000 | 16 | 25 | Pengemudi & Penumpang | Depan | 8 | 4.6000 | 295 | 6000 | 1985 | Tidak | 20.0000 | 5 | 204 | 111 | 74 | 44 | 31 | 14 | 3935 | Amerika Serikat | Cadillac Sevilla | 1.5625 | 70 | ponsel lama | Delapan Puluh Delapan | Besar | 19.5000 | 20.7000 | 21.9000 | 19 | 28 | Hanya pengemudi | Depan | 6 | 3.8000 | 170 | 4800 | 1570 | Tidak | 18.0000 | 6 | 201 | 111 | 74 | 42 | 31.5 | 17 | 3470 | Amerika Serikat | Oldsmobile Delapan Puluh Delapan | 1.47368421 | 74 | Pontianak | Burung Api | Sporty | 14.0000 | 17.7000 | 21.4000 | 19 | 28 | Pengemudi & Penumpang | Belakang | 6 | 3.4000 | 160 | 4600 | 1805 | Ya | 15.5000 | 4 | 196 | 101 | 75 | 43 | 25 | 13 | 3240 | Amerika Serikat | Burung Api Pontiac | 1.47368421 | 6 | Buick | LeSabre | Besar | 19.9000 | 20.8000 | 21.7000 | 19 | 28 | Hanya pengemudi | Depan | 6 | 3.8000 | 170 | 4800 | 1570 | Tidak | 18.0000 | 6 | 200 | 111 | 74 | 42 | 30.5 | 17 | 3470 | Amerika Serikat | Buick LeSabre | 1.47368421 | 13 | Chevrolet | kamera | Sporty | 13.4000 | 15.1000 | 16.8000 | 19 | 28 | Pengemudi & Penumpang | Belakang | 6 | 3.4000 | 160 | 4600 | 1805 | Ya | 15.5000 | 4 | 193 | 101 | 74 | 43 | 25 | 13 | 3240 | Amerika Serikat | Chevrolet Camaro | 1.47368421 | 76 | Pontianak | Bonneville | Besar | 19.4000 | 24.4000 | 29.4000 | 19 | 28 | Pengemudi & Penumpang | Depan | 6 | 3.8000 | 170 | 4800 | 1565 | Tidak | 18.0000 | 6 | 177 | 111 | 74 | 43 | 30.5 | 18 | 3495 | Amerika Serikat | Pontiac Bonneville | 1.47368421 | 56 | Mazda | RX-7 | Sporty | 32.5000 | 32.5000 | 32.5000 | 17 | 25 | Hanya pengemudi | Belakang | putar | 1.3000 | 255 | 6500 | 2325 | Ya | 20.0000 | 2 | 169 | 96 | 69 | 37 | TIDAK | TIDAK | 2895 | non-AS | Mazda RX-7 | 1.47058824 | 18 | Chevrolet | Korvet | Sporty | 34.6000 | 38.0000 | 41.5000 | 17 | 25 | Hanya pengemudi | Belakang | 8 | 5.7000 | 300 | 5000 | 1450 | Ya | 20.0000 | 2 | 179 | 96 | 74 | 43 | TIDAK | TIDAK | 3380 | Amerika Serikat | Chevrolet Korvet | 1.47058824 | 51 | Lincoln | Kota_Mobil | Besar | 34.4000 | 36.1000 | 37.8000 | 18 | 26 | Pengemudi & Penumpang | Belakang | 8 | 4.6000 | 210 | 4600 | 1840 | Tidak | 20.0000 | 6 | 219 | 117 | 77 | 45 | 31.5 | 22 | 4055 | Amerika Serikat | Lincoln Town_Mobil | 1.44444444 |
Morpheus API menyertakan antarmuka regresi untuk menyesuaikan data ke model linier menggunakan OLS, WLS, atau GLS. Kode di bawah ini menggunakan kumpulan data mobil yang sama yang diperkenalkan pada contoh sebelumnya, dan melakukan regresi Horsepower pada EngineSize . Contoh kode mencetak hasil model ke standar, yang ditunjukkan di bawah ini, dan kemudian membuat diagram sebar dengan garis regresi ditampilkan dengan jelas.
//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 ();
});
==================== ============== Hasil Regresi Linier ==================== ============== Model: OLS R-Kuadrat: 0,5360 Pengamatan: 93 R-Squared (disesuaikan): 0,5309 Model DF: 1 F-Statistik: 105.1204 Residu DF: 91 F-Statistik (Masalah): 1.11E-16 Kesalahan Standar: 35.8717 Waktu Proses (milis) 52 Durbin-Watson: 1,9591 ==================== ============== Indeks | PARAMETER | STD_ERROR | T_STAT | P_VALUE | CI_RENDAH | CI_UPPER | --------------------------------------------------- -------------------------------------------- mencegat | 45.2195 | 10.3119 | 4.3852 | 3.107E-5 | 24.736 | 65.7029 | Ukuran Mesin | 36.9633 | 3.6052 | 10.2528 | 7.573E-17 | 29.802 | 44.1245 | ==================== ==============
Semua catatan transaksi real estat perumahan Inggris dari tahun 1995 hingga saat ini dapat diakses melalui inisiatif Data Terbuka Pemerintah Inggris. Data disajikan dalam format CSV, dan berisi berbagai kolom, termasuk informasi seperti tanggal transaksi, harga yang dibayarkan, alamat lengkap (termasuk kode pos), jenis properti, jenis sewa, dan sebagainya.
Mari kita mulai dengan menulis fungsi untuk memuat file CSV ini dari bucket Amazon S3, dan karena file tersebut disimpan satu file per tahun, kami menyediakan fungsi berparameter yang sesuai. Mengingat persyaratan analisis kami, tidak perlu memuat semua kolom dalam file, jadi di bawah ini kami hanya memilih untuk membaca kolom pada indeks 1, 2, 4, dan 11. Selain itu, karena file tidak menyertakan header , kami mengganti nama kolom menjadi sesuatu yang lebih bermakna untuk membuat akses selanjutnya sedikit lebih jelas.
/**
* 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 ;
}
});
});
}
Di bawah ini kami menggunakan data ini untuk menghitung harga nominal median (tidak disesuaikan dengan inflasi) sebuah apartemen untuk setiap tahun antara tahun 1995 hingga 2014 untuk sebagian kota terbesar di Inggris. Ada sekitar 20 juta catatan dalam kumpulan data tanpa filter antara tahun 1993 dan 2014, dan meskipun memerlukan waktu yang cukup lama untuk memuat dan menguraikan (sekitar 3,5 GB data), Morpheus mengeksekusi bagian analitis kode dalam waktu sekitar 5 detik (tidak termasuk waktu muat) pada Apple Macbook Pro standar yang dibeli pada akhir tahun 2013. Perhatikan bagaimana kami menggunakan pemrosesan paralel untuk memuat dan memproses data dengan menelepon 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 ();
});
Persentase perubahan harga rata-rata nominal apartemen di sebagian kota terpilih ditunjukkan pada grafik di bawah ini. Hal ini menunjukkan bahwa London tidak mengalami penurunan harga rumah secara nominal akibat Krisis Keuangan Global (GFC), namun tidak semua kota di Inggris terbukti memiliki ketahanan yang baik. Hal yang sedikit mengejutkan adalah bahwa beberapa kota di wilayah utara yang kurang makmur mengalami tingkat apresiasi yang lebih tinggi pada periode tahun 2003 hingga 2006 dibandingkan dengan London. Satu hal yang perlu diperhatikan adalah meskipun London tidak melihat adanya penurunan harga nominal, namun terdapat koreksi yang cukup parah pada EUR dan USD karena Pound Sterling terdepresiasi secara signifikan terhadap mata uang tersebut selama GFC.
Memvisualisasikan data di Morpheus DataFrames
menjadi mudah melalui API abstraksi bagan sederhana dengan adaptor yang mendukung JFreeChart serta Google Charts (dan yang lain mengikuti permintaan populer). Desain ini memungkinkan untuk menghasilkan grafik Java Swing interaktif serta grafik berbasis browser HTML5 melalui antarmuka program yang sama. Untuk detail lebih lanjut tentang cara menggunakan API ini, lihat bagian visualisasi di sini, dan kodenya di sini.
Morpheus dipublikasikan ke Maven Central sehingga dapat dengan mudah ditambahkan sebagai dependensi pada alat build pilihan Anda. Basis kode saat ini dibagi menjadi 5 repositori untuk memungkinkan setiap modul dikembangkan secara independen. Modul inti, yang diberi nama morpheus-core, adalah perpustakaan dasar yang menjadi sandaran semua modul lainnya. Berbagai artefak Maven adalah sebagai berikut:
Inti Morpheus
Pustaka dasar yang berisi Morpheus Arrays, DataFrames, dan antarmuka & implementasi utama lainnya.
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-core</ artifactId >
< version >${VERSION}</ version >
</ dependency >
Visualisasi Morpheus
Komponen visualisasi untuk menampilkan DataFrames
dalam bagan dan tabel.
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-viz</ artifactId >
< version >${VERSION}</ version >
</ dependency >
Morpheus Quandl
Adaptor untuk memuat data dari Quandl
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-quandl</ artifactId >
< version >${VERSION}</ version >
</ dependency >
Morpheus Google
Adaptor untuk memuat data dari Google Finance
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-google</ artifactId >
< version >${VERSION}</ version >
</ dependency >
Morpheus Yahoo
Adaptor untuk memuat data dari Yahoo Finance
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-yahoo</ artifactId >
< version >${VERSION}</ version >
</ dependency >
Forum Tanya Jawab telah disiapkan menggunakan Google Grup dan dapat diakses di sini
Morpheus Javadocs dapat diakses online di sini.
Server pembangunan Integrasi Berkelanjutan dapat diakses di sini, yang membuat kode setelah setiap penggabungan.
Morpheus dirilis di bawah Lisensi Apache Software Foundation Versi 2.