تم تصميم مكتبة Morpheus لتسهيل تطوير برامج تحليلية عالية الأداء تشتمل على مجموعات بيانات كبيرة للتحليل في الوقت الفعلي وغير المتصل بالإنترنت على Java Virtual Machine (JVM). تمت كتابة المكتبة بلغة Java 8 مع الاستخدام المكثف لـ lambdas، ولكن يمكن الوصول إليها بجميع لغات JVM.
للحصول على وثائق مفصلة مع الأمثلة، انظر هنا
في جوهره، يوفر Morpheus بنية بيانات جدولية متعددة الاستخدامات وثنائية الأبعاد وفعالة من حيث الذاكرة تسمى DataFrame
، على غرار تلك التي تم نشرها لأول مرة في R. في حين أن لغات الحوسبة العلمية المكتوبة ديناميكيًا مثل R وPython وMatlab تعتبر رائعة لإجراء الأبحاث، إلا أنها ليست جيدة مناسبة لأنظمة الإنتاج واسعة النطاق حيث تصبح صيانتها صعبة للغاية وخطيرة في إعادة البناء. تحاول مكتبة Morpheus الحفاظ على قوة وتعدد استخدامات مفهوم DataFrame
، مع توفير مجموعة من الواجهات الآمنة والذاتية للكتابة ، والتي من شأنها أن تجعل تطوير تعقيد التعليمات البرمجية وصيانته وتوسيع نطاقه أسهل بكثير.
ميزة أخرى لمكتبة Morpheus هي أنها جيدة للغاية في توسيع نطاق بنيات المعالجات متعددة النواة نظرًا لقدرات الترابط القوية لجهاز Java Virtual Machine. يمكن تشغيل العديد من العمليات على Morpheus DataFrame
بسلاسة بالتوازي عن طريق استدعاء parallel()
على الكيان الذي ترغب في العمل عليه، تمامًا كما هو الحال مع Java 8 Streams. داخليًا، تعتمد هذه التطبيقات المتوازية على إطار عمل Fork & Join، ويتم ملاحظة تحسينات خطية تقريبًا في الأداء لأنواع معينة من العمليات عند إضافة نوى وحدة المعالجة المركزية.
Morpheus DataFrame
عبارة عن بنية مخزن أعمدة حيث يتم تمثيل كل عمود بواسطة Array
Morpheus التي يوجد بها العديد من التطبيقات، بما في ذلك الإصدارات الكثيفة والمتفرقة والذاكرة المعينة. تم تحسين صفائف Morpheus ودعمها حيثما أمكن بمصفوفات Java الأصلية البدائية (حتى بالنسبة لأنواع مثل LocalDate
و LocalDateTime
وما إلى ذلك...) لأنها أكثر كفاءة بكثير من منظور التخزين والوصول وجمع البيانات المهملة. تسمح الذاكرة المعينة Morpheus Arrays
، رغم أنها لا تزال تجريبية، بإنشاء DataFrames
كبيرة جدًا باستخدام وحدة تخزين خارج الكومة مدعومة بالملفات.
في حين أن مجموعة الميزات الكاملة لـ Morpheus DataFrame
لا تزال تتطور، إلا أن هناك بالفعل العديد من واجهات برمجة التطبيقات القوية للتأثير على التحويلات المعقدة والعمليات التحليلية بسهولة. هناك وظائف قياسية لحساب الإحصائيات الموجزة، وتنفيذ أنواع مختلفة من الانحدارات الخطية، وتطبيق تحليل المكونات الرئيسية (PCA) على سبيل المثال لا الحصر. تتم فهرسة DataFrame
في كل من بُعد الصف والعمود، مما يسمح بفرز البيانات وتقطيعها وتجميعها وتجميعها بكفاءة على طول أي من المحورين.
يهدف Morpheus أيضًا إلى توفير آلية قياسية لتحميل مجموعات البيانات من موفري البيانات المختلفين. الأمل هو أن يتبنى المجتمع واجهة برمجة التطبيقات هذه من أجل تنمية كتالوج مصادر البيانات المدعومة. حاليًا، يتم تطبيق مقدمي الخدمات لتمكين تحميل البيانات من Quandl، والاحتياطي الفيدرالي، والبنك الدولي، وYahoo Finance، وGoogle Finance.
فكر في مجموعة بيانات لخصائص المركبات التي يمكن الوصول إليها هنا. يقوم الكود الموجود أدناه بتحميل بيانات 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، حيث أن العديد من أنواع إرجاع الطرق هي في الواقع DataFrame
وبالتالي تسمح بهذا الشكل من تسلسل الطرق. في هذا المثال، تقوم الطرق csv()
و select()
و add()
و sort()
جميعها بإرجاع إطار. في بعض الحالات نفس الإطار الذي تعمل عليه الطريقة، أو في حالات أخرى مرشح أو نسخة ضحلة من الإطار الذي يتم تشغيله عليه. تبدو الصفوف العشرة الأولى من مجموعة البيانات المحولة في هذا المثال كما يلي، مع ظهور العمود المضاف حديثًا في أقصى يمين الإطار.
الفهرس | الشركة المصنعة | نموذج | اكتب | الحد الأدنى للسعر | السعر | الحد الأقصى للسعر | ميلا في الغالون.مدينة | ميلا في الغالون. الطريق السريع | وسائد هوائية | قطار القيادة | اسطوانات | حجم المحرك | قوة حصان | دورة في الدقيقة | Rev.per.mile | Man.trans.avail | سعة خزان الوقود | الركاب | الطول | قاعدة العجلات | العرض | بدوره.دائرة | المقعد الخلفي.غرفة | الأمتعة.غرفة | الوزن | الأصل | اصنع | ميلا في الغالون (الطريق السريع/المدينة) | -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- 9 | كاديلاك | ديفيل | كبير | 33.0000 | 34.7000 | 36.3000 | 16 | 25 | سائق فقط | الجبهة | 8 | 4.9000 | 200 | 4100 | 1510 | لا | 18.0000 | 6 | 206 | 114 | 73 | 43 | 35 | 18 | 3620 | الولايات المتحدة الأمريكية | كاديلاك ديفيل | 1.5625 | 10 | كاديلاك | إشبيلية | متوسطة الحجم | 37.5000 | 40.1000 | 42.7000 | 16 | 25 | السائق والراكب | الجبهة | 8 | 4.6000 | 295 | 6000 | 1985 | لا | 20.0000 | 5 | 204 | 111 | 74 | 44 | 31 | 14 | 3935 | الولايات المتحدة الأمريكية | كاديلاك اشبيلية | 1.5625 | 70 | أولدزموبيل | ثمانية وثمانون | كبير | 19.5000 | 20.7000 | 21.9000 | 19 | 28 | سائق فقط | الجبهة | 6 | 3.8000 | 170 | 4800 | 1570 | لا | 18.0000 | 6 | 201 | 111 | 74 | 42 | 31.5 | 17 | 3470 | الولايات المتحدة الأمريكية | أولدزموبيل ثمانية وثمانون | 1.47368421 | 74 | بونتياك | فايربيرد | رياضي | 14.0000 | 17.7000 | 21.4000 | 19 | 28 | السائق والراكب | خلفي | 6 | 3.4000 | 160 | 4600 | 1805 | نعم | 15.5000 | 4 | 196 | 101 | 75 | 43 | 25 | 13 | 3240 | الولايات المتحدة الأمريكية | بونتياك فايربيرد | 1.47368421 | 6 | بويك | ليسابر | كبير | 19.9000 | 20.8000 | 21.7000 | 19 | 28 | سائق فقط | الجبهة | 6 | 3.8000 | 170 | 4800 | 1570 | لا | 18.0000 | 6 | 200 | 111 | 74 | 42 | 30.5 | 17 | 3470 | الولايات المتحدة الأمريكية | بويك ليسابر | 1.47368421 | 13 | شيفروليه | كمارو | رياضي | 13.4000 | 15.1000 | 16.8000 | 19 | 28 | السائق والراكب | خلفي | 6 | 3.4000 | 160 | 4600 | 1805 | نعم | 15.5000 | 4 | 193 | 101 | 74 | 43 | 25 | 13 | 3240 | الولايات المتحدة الأمريكية | شيفروليه كمارو | 1.47368421 | 76 | بونتياك | بونفيل | كبير | 19.4000 | 24.4000 | 29.4000 | 19 | 28 | السائق والراكب | الجبهة | 6 | 3.8000 | 170 | 4800 | 1565 | لا | 18.0000 | 6 | 177 | 111 | 74 | 43 | 30.5 | 18 | 3495 | الولايات المتحدة الأمريكية | بونتياك بونفيل | 1.47368421 | 56 | مازدا | آر إكس-7 | رياضي | 32.5000 | 32.5000 | 32.5000 | 17 | 25 | سائق فقط | خلفي | دوارة | 1.3000 | 255 | 6500 | 2325 | نعم | 20.0000 | 2 | 169 | 96 | 69 | 37 | غير متوفر | غير متوفر | 2895 | غير الولايات المتحدة الأمريكية | مازدا آر إكس-7 | 1.47058824 | 18 | شيفروليه | كورفيت | رياضي | 34.6000 | 38.0000 | 41.5000 | 17 | 25 | سائق فقط | خلفي | 8 | 5.7000 | 300 | 5000 | 1450 | نعم | 20.0000 | 2 | 179 | 96 | 74 | 43 | غير متوفر | غير متوفر | 3380 | الولايات المتحدة الأمريكية | شيفروليه كورفيت | 1.47058824 | 51 | لينكولن | تاون_كار | كبير | 34.4000 | 36.1000 | 37.8000 | 18 | 26 | السائق والراكب | خلفي | 8 | 4.6000 | 210 | 4600 | 1840 | لا | 20.0000 | 6 | 219 | 117 | 77 | 45 | 31.5 | 22 | 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-Squared: 0.5360 الملاحظات: 93 R-Squared (المعدل): 0.5309 نموذج DF: 1 F-الإحصائيات: 105.1204 بقايا DF: 91 F-Statistic(Prob): 1.11E-16 الخطأ القياسي: 35.8717 وقت التشغيل (مللي) 52 دوربين واتسون: 1.9591 ================================================================================================== =================================================================================== الفهرس | المعلمة | STD_ERROR | T_STAT | P_VALUE | CI_LOWER | CI_UPPER | -------------------------------------------------- -------------------------------------------- اعتراض | 45.2195 | 10.3119 | 4.3852 | 3.107E-5 | 24.736 | 65.7029 | حجم المحرك | 36.9633 | 3.6052 | 10.2528 | 7.573E-17 | 29.802 | 44.1245 | ================================================================================================== ===================================================================================
من الممكن الوصول إلى جميع سجلات المعاملات العقارية السكنية في المملكة المتحدة منذ عام 1995 وحتى اليوم الحالي عبر مبادرة البيانات المفتوحة لحكومة المملكة المتحدة. يتم تقديم البيانات بتنسيق CSV، وتحتوي على العديد من الأعمدة، بما في ذلك معلومات مثل تاريخ المعاملة والسعر المدفوع والعنوان المؤهل بالكامل (بما في ذلك الرمز البريدي) ونوع العقار ونوع الإيجار وما إلى ذلك.
لنبدأ بكتابة دالة لتحميل ملفات CSV هذه من حاويات Amazon S3، وبما أنه يتم تخزين ملف واحد سنويًا، فإننا نقدم وظيفة ذات معلمات وفقًا لذلك. نظرًا لمتطلبات تحليلنا، ليست هناك حاجة لتحميل جميع الأعمدة في الملف، لذلك نختار أدناه فقط قراءة الأعمدة في الفهرس 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 لمجموعة فرعية من أكبر المدن في المملكة المتحدة. هناك حوالي 20 مليون سجل في مجموعة البيانات غير المصفاة بين عامي 1993 و2014، وبينما يستغرق التحميل والتحليل وقتًا طويلاً إلى حد ما (حوالي 3.5 جيجابايت من البيانات)، ينفذ Morpheus الجزء التحليلي من التعليمات البرمجية في حوالي 5 ثوانٍ (لا يشمل ذلك وقت التحميل) على جهاز Apple Macbook Pro قياسي تم شراؤه في أواخر عام 2013. لاحظ كيف نستخدم المعالجة المتوازية لتحميل البيانات ومعالجتها عن طريق الاتصال 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 مقارنة بلندن. شيء واحد يجب ملاحظته هو أنه على الرغم من أن لندن لم تشهد أي انخفاض في الأسعار الاسمية، إلا أنه كان هناك بالتأكيد تصحيح حاد إلى حد ما من حيث اليورو والدولار الأمريكي منذ انخفاض قيمة الجنيه الإسترليني بشكل كبير مقابل هذه العملات خلال الأزمة المالية العالمية.
أصبح تصور البيانات في Morpheus DataFrames
أمرًا سهلاً عبر واجهة برمجة تطبيقات بسيطة لتجريد المخططات مع محولات تدعم كلاً من JFreeChart بالإضافة إلى مخططات Google (مع الآخرين لمتابعة الطلب الشائع). يتيح هذا التصميم إنشاء مخططات Java Swing تفاعلية بالإضافة إلى مخططات مستندة إلى متصفح HTML5 عبر نفس الواجهة البرمجية. لمزيد من التفاصيل حول كيفية استخدام واجهة برمجة التطبيقات هذه، راجع القسم الخاص بالتصور هنا، والكود هنا.
يتم نشر 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 >
مورفيوس ياهو
المحول لتحميل البيانات من Yahoo Finance
< dependency >
< groupId >com.zavtech</ groupId >
< artifactId >morpheus-yahoo</ artifactId >
< version >${VERSION}</ version >
</ dependency >
تم إعداد منتدى الأسئلة والأجوبة باستخدام مجموعات Google ويمكن الوصول إليه هنا
يمكن الوصول إلى Morpheus Javadocs عبر الإنترنت هنا.
يمكن الوصول إلى خادم بناء التكامل المستمر هنا، والذي يبني التعليمات البرمجية بعد كل عملية دمج.
تم إصدار Morpheus بموجب ترخيص Apache Software Foundation الإصدار 2.