From 263ec67ea8ec2fc2e3b2c41aefd60aa7557550b0 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Wed, 13 Apr 2022 03:36:20 +0900 Subject: [PATCH 01/21] Change icon & menu item, block colors --- css/boardFrame.css | 20 +-- css/menuFrame.css | 15 +- data/libraries.json | 128 +++++++++++++----- ...{libraries_new.json => libraries_old.json} | 84 ++---------- img/apps/apps_visualize.svg | 8 ++ 5 files changed, 133 insertions(+), 122 deletions(-) rename data/{libraries_new.json => libraries_old.json} (98%) create mode 100644 img/apps/apps_visualize.svg diff --git a/css/boardFrame.css b/css/boardFrame.css index 0614f1da..33b38983 100644 --- a/css/boardFrame.css +++ b/css/boardFrame.css @@ -239,39 +239,39 @@ background-color: rgb(253, 177, 133); } .vp-block.visualization .vp-block-header { - background-color: rgb(249, 227, 214); + background-color: #E8ECD0; } .vp-block.visualization.vp-focus .vp-block-header, .vp-block.visualization.vp-focus-child .vp-block-header { - background-color: rgb(253, 177, 133); + background-color: #C6CE94; } .vp-block.machine_learning .vp-block-header { - background-color: #E8ECD0; + background-color: #E5EEF8; } .vp-block.machine_learning.vp-focus .vp-block-header, .vp-block.machine_learning.vp-focus-child .vp-block-header { - background-color: #C6CE94; + background-color: #BFD5F0; } .vp-block.logic-define .vp-block-header { - background-color: rgb(213, 231, 222); + /* background-color: rgb(213, 231, 222); */ } .vp-block.logic-define.vp-focus .vp-block-header, .vp-block.logic-define.vp-focus-child .vp-block-header { - background-color: rgb(138, 214, 176); + /* background-color: rgb(138, 214, 176); */ } .vp-block.logic-control .vp-block-header { - background-color: rgb(253, 239, 221); + /* background-color: rgb(253, 239, 221); */ } .vp-block.logic-control.vp-focus .vp-block-header, .vp-block.logic-control.vp-focus-child .vp-block-header { - background-color: rgb(255, 207, 115); + /* background-color: rgb(255, 207, 115); */ } .vp-block.library .vp-block-header { - background-color: rgb(249, 227, 214); + /* background-color: rgb(249, 227, 214); */ } .vp-block.library.vp-focus .vp-block-header, .vp-block.library.vp-focus-child .vp-block-header { - background-color: rgb(253, 177, 133); + /* background-color: rgb(253, 177, 133); */ } /* block button group */ .vp-block-button-group { diff --git a/css/menuFrame.css b/css/menuFrame.css index f8bcdbf3..54b7882f 100644 --- a/css/menuFrame.css +++ b/css/menuFrame.css @@ -230,13 +230,16 @@ background: #E56139; } .vp-menuitem.apps.vp-color-apps5 { - background: #BEB727; + background: #97AA4E; } .vp-menuitem.apps.vp-color-apps6 { - background: #91A541; + background: #88B4E9; } .vp-menuitem.apps.vp-color-apps7 { - background: #718E41; + background: #6C9BD1; +} +.vp-menuitem.apps.vp-color-apps8 { + background: #578BC7; } .vp-menuitem.apps.vp-color-preparing { background: var(--gray-color); @@ -252,14 +255,14 @@ } /* MenuItem - Logic */ .vp-menuitem.logic-define { - background-color: rgb(213, 231, 222); + /* background-color: rgb(213, 231, 222); */ } .vp-menuitem.logic-control { - background-color: rgb(253, 239, 221); + /* background-color: rgb(253, 239, 221); */ } /* MenuItem - Library */ .vp-menuitem.library { - background-color: rgb(249, 227, 214); + /* background-color: rgb(249, 227, 214); */ } /* Task Bar */ diff --git a/data/libraries.json b/data/libraries.json index 22985d2d..114467cf 100644 --- a/data/libraries.json +++ b/data/libraries.json @@ -3047,20 +3047,6 @@ "icon": "apps/apps_markdown.svg" } }, - { - "id" : "visualize_chart", - "type" : "function", - "level": 1, - "name" : "Chart", - "tag" : "MATPLOTLIB,CHART,VISUALIZATION,VISUALIZE", - "path" : "visualpython - visualization - matplotlib", - "desc" : "Matplotlib chart creation", - "file" : "m_apps/Chart", - "apps" : { - "color": 3, - "icon": "apps/apps_chart.svg" - } - }, { "id" : "apps_pdf", "type" : "function", @@ -3071,7 +3057,7 @@ "desc" : "PDF", "file" : "m_apps/PDF", "apps" : { - "color": 4, + "color": 3, "icon": "apps/apps_pymupdf.svg" } }, @@ -3091,6 +3077,74 @@ } ] }, + { + "id" : "pkg_visualize", + "type" : "package", + "level": 0, + "name" : "Visualization", + "path" : "visualpython - visualization", + "desc" : "Visualization modules", + "open" : true, + "grid" : true, + "item" : [ + { + "id" : "visualize_chartStyle", + "type" : "function", + "level": 1, + "name" : "Chart Style", + "tag" : "CHART STYLE SETTING,IMPORT CHART,VISUALIZATION,VISUALIZE", + "path" : "visualpython - visualization - chartsstyle", + "desc" : "Chart style setting", + "file" : "m_visualize/ChartSetting", + "apps" : { + "color": 5, + "icon": "apps/apps_style.svg" + } + }, + { + "id" : "pd_plot", + "type" : "function", + "level": 1, + "name" : "Pandas", + "tag" : "PANDAS PLOT,PANDAS", + "path" : "visualpython - library - pandas - plot", + "desc" : "Pandas plot creation", + "file" : "m_library/m_pandas/plot", + "apps" : { + "color": 5, + "icon": "apps/apps_visualize.svg" + } + }, + { + "id" : "visualize_chart", + "type" : "function", + "level": 1, + "name" : "Matplotlib", + "tag" : "MATPLOTLIB,CHART,VISUALIZATION,VISUALIZE", + "path" : "visualpython - visualization - matplotlib", + "desc" : "Matplotlib chart creation", + "file" : "m_apps/Chart", + "apps" : { + "color": 5, + "icon": "apps/apps_visualize.svg" + } + }, + { + "id" : "visualize_seaborn", + "type" : "function", + "level": 1, + "name" : "Seaborn", + "tag" : "SEABORN,CHART,VISUALIZATION,VISUALIZE", + "path" : "visualpython - visualization - seaborn", + "desc" : "Seaborn chart creation", + "file" : "m_visualize/Seaborn", + "apps" : { + "color": 5, + "icon": "apps/apps_visualize.svg" + } + } + ] + }, { "id" : "pkg_ml", "type" : "package", @@ -3111,7 +3165,7 @@ "desc" : "Data sets for machine learning", "file" : "m_ml/DataSets", "apps" : { - "color": 5, + "color": 6, "icon": "apps/apps_dataset.svg" } }, @@ -3125,7 +3179,7 @@ "desc" : "Data preparation for machine learning", "file" : "m_ml/DataPrep", "apps" : { - "color": 5, + "color": 6, "icon": "apps/apps_dataprep.svg" } }, @@ -3139,10 +3193,24 @@ "desc" : "Data split for machine learning", "file" : "m_ml/dataSplit", "apps" : { - "color": 5, + "color": 6, "icon": "apps/apps_datasplit.svg" } }, + { + "id" : "ml_evaluation", + "type" : "function", + "level": 1, + "name" : "Evaluation", + "tag" : "PERFORMANCE EVALUATION,MACHINE LEARNING,ML", + "path" : "visualpython - machine_learning - evaluation", + "desc" : "Performance evaluation for machine learning", + "file" : "m_ml/evaluation", + "apps" : { + "color": 6, + "icon": "apps/apps_evaluate.svg" + } + }, { "id" : "ml_regression", "type" : "function", @@ -3153,7 +3221,7 @@ "desc" : "Regression model for machine learning", "file" : "m_ml/Regression", "apps" : { - "color": 5, + "color": 7, "icon": "apps/apps_regression.svg" } }, @@ -3167,7 +3235,7 @@ "desc" : "Classification model for machine learning", "file" : "m_ml/Classification", "apps" : { - "color": 6, + "color": 7, "icon": "apps/apps_classification.svg" } }, @@ -3181,7 +3249,7 @@ "desc" : "Clustering model for machine learning", "file" : "m_ml/Clustering", "apps" : { - "color": 6, + "color": 7, "icon": "apps/apps_clustering.svg" } }, @@ -3195,7 +3263,7 @@ "desc" : "Dimension reduction model for machine learning", "file" : "m_ml/DimensionReduction", "apps" : { - "color": 6, + "color": 7, "icon": "apps/apps_dimension.svg" } }, @@ -3209,23 +3277,9 @@ "desc" : "AutoML model for machine learning", "file" : "m_ml/AutoML", "apps" : { - "color": 6, + "color": 8, "icon": "apps/apps_automl.svg" } - }, - { - "id" : "ml_evaluation", - "type" : "function", - "level": 1, - "name" : "Evaluation", - "tag" : "PERFORMANCE EVALUATION,MACHINE LEARNING,ML", - "path" : "visualpython - machine_learning - evaluation", - "desc" : "Performance evaluation for machine learning", - "file" : "m_ml/evaluation", - "apps" : { - "color": 7, - "icon": "apps/apps_evaluate.svg" - } } ] } diff --git a/data/libraries_new.json b/data/libraries_old.json similarity index 98% rename from data/libraries_new.json rename to data/libraries_old.json index 6fc6b0c5..22985d2d 100644 --- a/data/libraries_new.json +++ b/data/libraries_old.json @@ -3047,6 +3047,20 @@ "icon": "apps/apps_markdown.svg" } }, + { + "id" : "visualize_chart", + "type" : "function", + "level": 1, + "name" : "Chart", + "tag" : "MATPLOTLIB,CHART,VISUALIZATION,VISUALIZE", + "path" : "visualpython - visualization - matplotlib", + "desc" : "Matplotlib chart creation", + "file" : "m_apps/Chart", + "apps" : { + "color": 3, + "icon": "apps/apps_chart.svg" + } + }, { "id" : "apps_pdf", "type" : "function", @@ -3057,7 +3071,7 @@ "desc" : "PDF", "file" : "m_apps/PDF", "apps" : { - "color": 3, + "color": 4, "icon": "apps/apps_pymupdf.svg" } }, @@ -3077,74 +3091,6 @@ } ] }, - { - "id" : "pkg_visualize", - "type" : "package", - "level": 0, - "name" : "Visualization", - "path" : "visualpython - visualization", - "desc" : "Visualization modules", - "open" : true, - "grid" : true, - "item" : [ - { - "id" : "visualize_chartStyle", - "type" : "function", - "level": 1, - "name" : "Chart Style", - "tag" : "CHART STYLE SETTING,IMPORT CHART,VISUALIZATION,VISUALIZE", - "path" : "visualpython - visualization - chartsstyle", - "desc" : "Chart style setting", - "file" : "m_visualize/ChartSetting", - "apps" : { - "color": 1, - "icon": "apps/apps_style.svg" - } - }, - { - "id" : "pd_plot", - "type" : "function", - "level": 1, - "name" : "Pandas Plot", - "tag" : "PANDAS PLOT,PANDAS", - "path" : "visualpython - library - pandas - plot", - "desc" : "Pandas plot creation", - "file" : "m_library/m_pandas/plot", - "apps" : { - "color": 1, - "icon": "apps/apps_chart.svg" - } - }, - { - "id" : "visualize_chart", - "type" : "function", - "level": 1, - "name" : "Matplotlib", - "tag" : "MATPLOTLIB,CHART,VISUALIZATION,VISUALIZE", - "path" : "visualpython - visualization - matplotlib", - "desc" : "Matplotlib chart creation", - "file" : "m_apps/Chart", - "apps" : { - "color": 1, - "icon": "apps/apps_chart.svg" - } - }, - { - "id" : "visualize_seaborn", - "type" : "function", - "level": 1, - "name" : "Seaborn", - "tag" : "SEABORN,CHART,VISUALIZATION,VISUALIZE", - "path" : "visualpython - visualization - seaborn", - "desc" : "Seaborn chart creation", - "file" : "m_visualize/Seaborn", - "apps" : { - "color": 1, - "icon": "apps/apps_chart.svg" - } - } - ] - }, { "id" : "pkg_ml", "type" : "package", diff --git a/img/apps/apps_visualize.svg b/img/apps/apps_visualize.svg new file mode 100644 index 00000000..28711332 --- /dev/null +++ b/img/apps/apps_visualize.svg @@ -0,0 +1,8 @@ + + + + + + + + From aaafc564cc277e65669c3a10696414a9a61fe74b Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Thu, 14 Apr 2022 14:27:45 +0900 Subject: [PATCH 02/21] Seaborn - change layout #1 --- css/m_visualize/seaborn.css | 10 ++- html/m_visualize/seaborn.html | 146 +++++++++++++++++++--------------- js/m_visualize/Seaborn.js | 4 +- 3 files changed, 94 insertions(+), 66 deletions(-) diff --git a/css/m_visualize/seaborn.css b/css/m_visualize/seaborn.css index 36f84855..a5ff5ed0 100644 --- a/css/m_visualize/seaborn.css +++ b/css/m_visualize/seaborn.css @@ -34,7 +34,8 @@ width: 100%; } .vp-tab-page-box.plot { - height: calc(100% - 30px); + /* height: calc(100% - 30px); */ + min-height: 310px; align-content: baseline; } .vp-chart-plot-box { @@ -47,7 +48,7 @@ } .vp-chart-left-box { display: grid; - grid-row-gap: 3px; + grid-template-rows: 30px calc(100% - 30px); } .vp-chart-left-box > label { margin-bottom: 0px; @@ -66,4 +67,9 @@ .vp-chart-preview-box { min-height: 150px; width: 100%; + height: calc(100% - 30px); +} + +.vp-tab-page label { + margin-bottom: 0px; } \ No newline at end of file diff --git a/html/m_visualize/seaborn.html b/html/m_visualize/seaborn.html index a8301186..31efb90e 100644 --- a/html/m_visualize/seaborn.html +++ b/html/m_visualize/seaborn.html @@ -44,59 +44,62 @@
- +
-
- NOTE: Options selected from this tab only apply to subplot 1. -
- - - - -
-
- - -
-
- - -
-
- - -
- -
-
Info
+
Data
+
Info
Style
Setting
-
- +
+
+ NOTE: Options selected from this tab only apply to subplot 1. +
+ + + +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+ - -
-
-
-
-
- Chart Preview - - - - - - - - -
-
- -
- +
+
+ Chart Preview + + + + + + + + +
+
+ +
+ +
+
+
diff --git a/js/m_visualize/Seaborn.js b/js/m_visualize/Seaborn.js index 8135a90c..10136ef1 100644 --- a/js/m_visualize/Seaborn.js +++ b/js/m_visualize/Seaborn.js @@ -29,7 +29,7 @@ define([ super._init(); this.config.dataview = false; - this.config.sizeLevel = 3; + this.config.size = { width: 900, height: 550 }; this.state = { chartType: 'scatterplot', @@ -88,7 +88,7 @@ define([ // change tab $(this.wrapSelector('.vp-tab-item')).on('click', function() { let level = $(this).parent().data('level'); - let type = $(this).data('type'); // info / element / figure + let type = $(this).data('type'); // data / info / element / figure $(that.wrapSelector(com_util.formatString('.vp-tab-bar.{0} .vp-tab-item', level))).removeClass('vp-focus'); $(this).addClass('vp-focus'); From 771c89354531089d87c811208db60150386cb0bb Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Fri, 15 Apr 2022 14:55:12 +0900 Subject: [PATCH 03/21] Seaborn - change layout #2 --- css/m_visualize/seaborn.css | 11 +- html/m_visualize/seaborn.html | 249 +++++++++++++++++----------------- js/m_visualize/Seaborn.js | 99 +++++++++----- 3 files changed, 198 insertions(+), 161 deletions(-) diff --git a/css/m_visualize/seaborn.css b/css/m_visualize/seaborn.css index a5ff5ed0..24750b65 100644 --- a/css/m_visualize/seaborn.css +++ b/css/m_visualize/seaborn.css @@ -39,6 +39,9 @@ align-content: baseline; } .vp-chart-plot-box { + +} +.vp-chart-body { display: grid; grid-template-columns: calc(50% - 8px) calc(50% - 8px); grid-row-gap: 5px; @@ -47,8 +50,7 @@ align-content: center; } .vp-chart-left-box { - display: grid; - grid-template-rows: 30px calc(100% - 30px); + } .vp-chart-left-box > label { margin-bottom: 0px; @@ -72,4 +74,9 @@ .vp-tab-page label { margin-bottom: 0px; +} +.vp-chart-setting-footer { + position: absolute; + left: 20px; + bottom: 15px; } \ No newline at end of file diff --git a/html/m_visualize/seaborn.html b/html/m_visualize/seaborn.html index 31efb90e..9f09f4df 100644 --- a/html/m_visualize/seaborn.html +++ b/html/m_visualize/seaborn.html @@ -36,140 +36,143 @@
-
-
Plot 1
-
Plot 2
-
Plot 3
-
-
-
-
- -
-
-
Data
-
Info
-
Style
-
Setting
-
-
-
-
- NOTE: Options selected from this tab only apply to subplot 1. -
- - - -
- - +
+
+
+
Plot 1
+
Plot 2
+
Plot 3
+
+
+
+
+ +
+
Data
+
Info
+
Style
+
Setting
-
-
- - + -
-
- - + +
+
+
+ + +
+
+ + +
+
+ +
-
- - -
- -
-
- Chart Preview - - - - - - - - -
-
- -
- -
-
+
+
+
+
+ Chart Preview + + + + + + + + +
+
+ +
+
+ \ No newline at end of file diff --git a/js/m_visualize/Seaborn.js b/js/m_visualize/Seaborn.js index 10136ef1..68a132c2 100644 --- a/js/m_visualize/Seaborn.js +++ b/js/m_visualize/Seaborn.js @@ -93,7 +93,7 @@ define([ $(that.wrapSelector(com_util.formatString('.vp-tab-bar.{0} .vp-tab-item', level))).removeClass('vp-focus'); $(this).addClass('vp-focus'); - $(that.wrapSelector(com_util.formatString('.vp-tab-page-box.{0} .vp-tab-page', level))).hide(); + $(that.wrapSelector(com_util.formatString('.vp-tab-page-box.{0} > .vp-tab-page', level))).hide(); $(that.wrapSelector(com_util.formatString('.vp-tab-page[data-type="{0}"]', type))).show(); }); @@ -161,19 +161,26 @@ define([ } templateForSettingBox() { - return `
- -
- - + return `
+ +
+ + +
+ + + + + +
- - - - - - -
`; + + `; } render() { @@ -191,10 +198,10 @@ define([ // set size $(this.wrapSelector('.vp-inner-popup-box')).css({ width: 400, height: 260}); - this.renderImportOptions(); + this.bindImportOptions(); } - renderImportOptions() { + bindImportOptions() { //==================================================================== // Stylesheet suggestinput //==================================================================== @@ -243,6 +250,20 @@ define([ return suggestInput.toTagString(); }); }); + + let that = this; + // setting popup - set default + $(this.wrapSelector('#setDefault')).on('change', function() { + let checked = $(this).prop('checked'); + + if (checked) { + // disable input + $(that.wrapSelector('.vp-chart-setting-body input')).prop('disabled', true); + } else { + // enable input + $(that.wrapSelector('.vp-chart-setting-body input')).prop('disabled', false); + } + }); } handleInnerOk() { @@ -292,27 +313,33 @@ define([ var code = new com_String(); // get parameters - var figWidth = $(this.wrapSelector('#figureWidth')).val(); - var figHeight = $(this.wrapSelector('#figureHeight')).val(); - var styleName = $(this.wrapSelector('#styleSheet')).val(); - var fontName = $(this.wrapSelector('#fontName')).val(); - var fontSize = $(this.wrapSelector('#fontSize')).val(); - - code.appendLine('import matplotlib.pyplot as plt'); - code.appendFormatLine("plt.rc('figure', figsize=({0}, {1}))", figWidth, figHeight); - if (styleName && styleName.length > 0) { - code.appendFormatLine("plt.style.use('{0}')", styleName); - } - code.appendLine(); - - code.appendLine('from matplotlib import rcParams'); - if (fontName && fontName.length > 0) { - code.appendFormatLine("rcParams['font.family'] = '{0}'", fontName); - } - if (fontSize && fontSize.length > 0) { - code.appendFormatLine("rcParams['font.size'] = {0}", fontSize); + let setDefault = $(this.wrapSelector('#setDefault')).prop('checked'); + if (setDefault == true) { + code.appendLine('from matplotlib import rcParams, rcParamsDefault'); + code.append('rcParams.update(rcParamsDefault)'); + } else { + var figWidth = $(this.wrapSelector('#figureWidth')).val(); + var figHeight = $(this.wrapSelector('#figureHeight')).val(); + var styleName = $(this.wrapSelector('#styleSheet')).val(); + var fontName = $(this.wrapSelector('#fontName')).val(); + var fontSize = $(this.wrapSelector('#fontSize')).val(); + + code.appendLine('import matplotlib.pyplot as plt'); + code.appendFormatLine("plt.rc('figure', figsize=({0}, {1}))", figWidth, figHeight); + if (styleName && styleName.length > 0) { + code.appendFormatLine("plt.style.use('{0}')", styleName); + } + code.appendLine(); + + code.appendLine('from matplotlib import rcParams'); + if (fontName && fontName.length > 0) { + code.appendFormatLine("rcParams['font.family'] = '{0}'", fontName); + } + if (fontSize && fontSize.length > 0) { + code.appendFormatLine("rcParams['font.size'] = {0}", fontSize); + } + code.append("rcParams['axes.unicode_minus'] = False"); } - code.append("rcParams['axes.unicode_minus'] = False"); return code.toString(); } @@ -327,7 +354,7 @@ define([ let convertedData = data; if (preview) { // set figure size for preview chart - code.appendLine('plt.figure(figsize=(6, 4))'); + code.appendLine('plt.figure(figsize=(4, 3))'); if (useSampling) { // data sampling code for preview convertedData = data + '.sample(n=30, random_state=0)'; From 9ade0c80bc0e17f12b84626256567f54d5508d4e Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Mon, 18 Apr 2022 14:52:41 +0900 Subject: [PATCH 04/21] Seaborn - changes --- js/m_visualize/Seaborn.js | 118 +++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 8 deletions(-) diff --git a/js/m_visualize/Seaborn.js b/js/m_visualize/Seaborn.js index 68a132c2..f1505f56 100644 --- a/js/m_visualize/Seaborn.js +++ b/js/m_visualize/Seaborn.js @@ -39,6 +39,7 @@ define([ figColumn: 0, shareX: false, shareY: false, + useData: true, // FIXME: use data default? data: '', x: '', y: '', @@ -96,11 +97,51 @@ define([ $(that.wrapSelector(com_util.formatString('.vp-tab-page-box.{0} > .vp-tab-page', level))).hide(); $(that.wrapSelector(com_util.formatString('.vp-tab-page[data-type="{0}"]', type))).show(); }); + + // use data or not + $(this.wrapSelector('#useData')).on('change', function() { + let useData = $(this).prop('checked'); + if (useData) { + // use data + $(that.wrapSelector('#data')).prop('disabled', false); + + $(that.wrapSelector('#x')).closest('.vp-vs-box').replaceWith(''); + $(that.wrapSelector('#y')).closest('.vp-vs-box').replaceWith(''); + $(that.wrapSelector('#hue')).closest('.vp-vs-box').replaceWith(''); + } else { + // not use data + // disable data selection + $(that.wrapSelector('#data')).prop('disabled', true); + $(that.wrapSelector('#data')).val(''); + that.state.data = ''; + that.state.x = ''; + that.state.y = ''; + that.state.hue = ''; + + let varSelectorX = new VarSelector2(that.wrapSelector(), ['DataFrame', 'Series', 'list']); + varSelectorX.setComponentID('x'); + varSelectorX.addClass('vp-state vp-input'); + varSelectorX.setValue(that.state.x); + $(that.wrapSelector('#x')).replaceWith(varSelectorX.toTagString()); + + let varSelectorY = new VarSelector2(that.wrapSelector(), ['DataFrame', 'Series', 'list']); + varSelectorY.setComponentID('y'); + varSelectorY.addClass('vp-state vp-input'); + varSelectorY.setValue(that.state.y); + $(that.wrapSelector('#y')).replaceWith(varSelectorY.toTagString()); + + let varSelectorHue = new VarSelector2(that.wrapSelector(), ['DataFrame', 'Series', 'list']); + varSelectorHue.setComponentID('hue'); + varSelectorHue.addClass('vp-state vp-input'); + varSelectorHue.setValue(that.state.hue); + $(that.wrapSelector('#hue')).replaceWith(varSelectorHue.toTagString()); + } + }); // bind column by dataframe - $(document).on('change', this.wrapSelector('#data'), function() { - com_generator.vp_bindColumnSource(that.wrapSelector(), this, ['x', 'y', 'hue'], 'select'); - }); + // $(this.wrapSelector('#data')).on('change', function() { + // com_generator.vp_bindColumnSource(that.wrapSelector(), this, ['x', 'y', 'hue'], 'select'); + // }); // preview refresh $(this.wrapSelector('#previewRefresh')).on('click', function() { @@ -108,6 +149,7 @@ define([ }); $(this.wrapSelector('.vp-state')).on('change', function() { if (that.state.autoRefresh && that.state.data != '') { + console.log('refresh'); that.loadPreview(); } }); @@ -142,8 +184,39 @@ define([ varSelector.setComponentID('data'); varSelector.addClass('vp-state vp-input'); varSelector.setValue(this.state.featureData); + varSelector.setSelectEvent(function (value, item) { + $(this.wrapSelector()).val(value); + + if (item.dtype == 'DataFrame') { + $(that.wrapSelector('#x')).prop('disabled', false); + $(that.wrapSelector('#y')).prop('disabled', false); + $(that.wrapSelector('#hue')).prop('disabled', false); + + com_generator.vp_bindColumnSource(that.wrapSelector(), $(that.wrapSelector('#data')), ['x', 'y', 'hue'], 'select'); + } else { + $(that.wrapSelector('#x')).prop('disabled', true); + $(that.wrapSelector('#y')).prop('disabled', true); + $(that.wrapSelector('#hue')).prop('disabled', true); + } + }); $(page).find('#data').replaceWith(varSelector.toTagString()); + // legend position + let legendPosList = [ + 'best', 'upper right', 'upper left', 'lower left', 'lower right', + 'center left', 'center right', 'lower center', 'upper center', 'center' + ]; + let legendPosTag = new com_String(); + legendPosList.forEach(pos => { + let selectedFlag = ''; + if (pos == that.state.legendPos) { + selectedFlag = 'selected'; + } + legendPosTag.appendFormatLine('', + pos, selectedFlag, pos, pos == 'best'?' (default)':''); + }); + $(page).find('#legendPos').html(legendPosTag.toString()); + // preview sample count let sampleCountList = [30, 50, 100, 300, 500, 700, 1000]; let sampleCountTag = new com_String(); @@ -345,19 +418,26 @@ define([ } generateCode(preview=false) { - let { chartType, data, x, y, userOption='', allocateTo='', useSampling } = this.state; + let { + chartType, data, userOption='', + title, x_label, y_label, useLegend, legendPos, + useGrid, useMarker, markerStyle, + x_limit_from, x_limit_to, y_limit_from, y_limit_to, + useSampling, sampleCount + } = this.state; let code = new com_String(); let config = this.chartConfig[chartType]; + let state = JSON.parse(JSON.stringify(this.state)); - let chartCode = com_generator.vp_codeGenerator(this, config, this.state, (userOption != ''? ', ' + userOption : '')); + let chartCode = com_generator.vp_codeGenerator(this, config, state, (userOption != ''? ', ' + userOption : '')); let convertedData = data; - if (preview) { + if (preview && data != '') { // set figure size for preview chart - code.appendLine('plt.figure(figsize=(4, 3))'); + code.appendLine('plt.figure(figsize=(6, 5))'); if (useSampling) { // data sampling code for preview - convertedData = data + '.sample(n=30, random_state=0)'; + convertedData = data + '.sample(n=' + sampleCount + ', random_state=0)'; } } @@ -365,6 +445,28 @@ define([ chartCode = chartCode.replace(data, convertedData); code.appendLine(chartCode); + + // // Info + // if (title && title != '') { + // code.appendFormatLine("plt.title('{0}')", title); + // } + // if (x_label && x_label != '') { + // code.appendFormatLine("plt.xlabel('{0}')", x_label); + // } + // if (y_label && y_label != '') { + // code.appendFormatLine("plt.ylabel('{0}')", y_label); + // } + // if (x_limit_from != '' && x_limit_to != '') { + // code.appendFormatLine("plt.xlim(({0}, {1}))", x_limit_from, x_limit_to); + // } + // if (y_limit_from != '' && y_limit_to != '') { + // code.appendFormatLine("plt.ylim(({0}, {1}))", y_limit_from, y_limit_to); + // } + // if (useLegend && legendPos != '') { + // code.appendFormatLine("plt.legend(loc='{0}')", legendPos); + // } + + code.append('plt.show()'); return code.toString(); From 94f2f605a547f8a6f250c4cae46a8d9575558e09 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Mon, 18 Apr 2022 15:14:02 +0900 Subject: [PATCH 05/21] Change menu sorting --- css/{m_apps => m_visualize}/chart.css | 0 data/libraries.json | 88 +++++++++---------------- html/{m_apps => m_visualize}/chart.html | 0 js/{m_apps => m_visualize}/Chart.js | 4 +- 4 files changed, 32 insertions(+), 60 deletions(-) rename css/{m_apps => m_visualize}/chart.css (100%) rename html/{m_apps => m_visualize}/chart.html (100%) rename js/{m_apps => m_visualize}/Chart.js (99%) diff --git a/css/m_apps/chart.css b/css/m_visualize/chart.css similarity index 100% rename from css/m_apps/chart.css rename to css/m_visualize/chart.css diff --git a/data/libraries.json b/data/libraries.json index 114467cf..efc8cd6a 100644 --- a/data/libraries.json +++ b/data/libraries.json @@ -2860,14 +2860,14 @@ "file": "m_matplotlib/import" }, { - "id": "mp_plot", + "id": "mp_chart", "type": "function", "level": 2, "name": "Create chart", "path": "visualpython - library - matplotlib - plot", "desc": "", "tag": "MATPLOTLIB, PLOT, CHART", - "file": "m_matplotlib/plot" + "file": "m_visualize/Chart" }, { "id": "mp_figure", @@ -3101,34 +3101,6 @@ "icon": "apps/apps_style.svg" } }, - { - "id" : "pd_plot", - "type" : "function", - "level": 1, - "name" : "Pandas", - "tag" : "PANDAS PLOT,PANDAS", - "path" : "visualpython - library - pandas - plot", - "desc" : "Pandas plot creation", - "file" : "m_library/m_pandas/plot", - "apps" : { - "color": 5, - "icon": "apps/apps_visualize.svg" - } - }, - { - "id" : "visualize_chart", - "type" : "function", - "level": 1, - "name" : "Matplotlib", - "tag" : "MATPLOTLIB,CHART,VISUALIZATION,VISUALIZE", - "path" : "visualpython - visualization - matplotlib", - "desc" : "Matplotlib chart creation", - "file" : "m_apps/Chart", - "apps" : { - "color": 5, - "icon": "apps/apps_visualize.svg" - } - }, { "id" : "visualize_seaborn", "type" : "function", @@ -3169,20 +3141,6 @@ "icon": "apps/apps_dataset.svg" } }, - { - "id" : "ml_dataPrep", - "type" : "function", - "level": 1, - "name" : "Data Prep", - "tag" : "DATA PREPARATION,MACHINE LEARNING,ML", - "path" : "visualpython - machine_learning - data prep", - "desc" : "Data preparation for machine learning", - "file" : "m_ml/DataPrep", - "apps" : { - "color": 6, - "icon": "apps/apps_dataprep.svg" - } - }, { "id" : "ml_dataSplit", "type" : "function", @@ -3198,17 +3156,31 @@ } }, { - "id" : "ml_evaluation", + "id" : "ml_dataPrep", "type" : "function", "level": 1, - "name" : "Evaluation", - "tag" : "PERFORMANCE EVALUATION,MACHINE LEARNING,ML", - "path" : "visualpython - machine_learning - evaluation", - "desc" : "Performance evaluation for machine learning", - "file" : "m_ml/evaluation", + "name" : "Data Prep", + "tag" : "DATA PREPARATION,MACHINE LEARNING,ML", + "path" : "visualpython - machine_learning - data prep", + "desc" : "Data preparation for machine learning", + "file" : "m_ml/DataPrep", "apps" : { "color": 6, - "icon": "apps/apps_evaluate.svg" + "icon": "apps/apps_dataprep.svg" + } + }, + { + "id" : "ml_autoML", + "type" : "function", + "level": 1, + "name" : "AutoML", + "tag" : "AUTO ML,MODEL,MACHINE LEARNING,ML", + "path" : "visualpython - machine_learning - automl", + "desc" : "AutoML model for machine learning", + "file" : "m_ml/AutoML", + "apps" : { + "color": 6, + "icon": "apps/apps_automl.svg" } }, { @@ -3268,17 +3240,17 @@ } }, { - "id" : "ml_autoML", + "id" : "ml_evaluation", "type" : "function", "level": 1, - "name" : "AutoML", - "tag" : "AUTO ML,MODEL,MACHINE LEARNING,ML", - "path" : "visualpython - machine_learning - automl", - "desc" : "AutoML model for machine learning", - "file" : "m_ml/AutoML", + "name" : "Evaluation", + "tag" : "PERFORMANCE EVALUATION,MACHINE LEARNING,ML", + "path" : "visualpython - machine_learning - evaluation", + "desc" : "Performance evaluation for machine learning", + "file" : "m_ml/evaluation", "apps" : { "color": 8, - "icon": "apps/apps_automl.svg" + "icon": "apps/apps_evaluate.svg" } } ] diff --git a/html/m_apps/chart.html b/html/m_visualize/chart.html similarity index 100% rename from html/m_apps/chart.html rename to html/m_visualize/chart.html diff --git a/js/m_apps/Chart.js b/js/m_visualize/Chart.js similarity index 99% rename from js/m_apps/Chart.js rename to js/m_visualize/Chart.js index 76ed858e..53c4a62d 100644 --- a/js/m_apps/Chart.js +++ b/js/m_visualize/Chart.js @@ -13,8 +13,8 @@ // [CLASS] Chart //============================================================================ define([ - 'text!vp_base/html/m_apps/chart.html!strip', - 'css!vp_base/css/m_apps/chart.css', + 'text!vp_base/html/m_visualize/chart.html!strip', + 'css!vp_base/css/m_visualize/chart.css', 'vp_base/js/com/com_String', 'vp_base/js/com/com_Const', 'vp_base/js/com/com_util', From 68f55ad0e404f5de9fb4637d6d0ac7426547dfdd Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Mon, 18 Apr 2022 15:44:28 +0900 Subject: [PATCH 06/21] Fix Markdown bug --- js/board/Block.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/js/board/Block.js b/js/board/Block.js index 24d4c2d0..725eb9c9 100644 --- a/js/board/Block.js +++ b/js/board/Block.js @@ -199,10 +199,11 @@ define([ this.focusItem(); } + // Deprecated: show markdown as other DA blocks // if markdown, set its height to fit-content - if (this.id == 'apps_markdown') { - $(this.wrapSelector()).addClass('vp-block-markdown'); - } + // if (this.id == 'apps_markdown') { + // $(this.wrapSelector()).addClass('vp-block-markdown'); + // } // if viewDepthNumber, show it let viewDepthNumber = this.prop.parent.state.viewDepthNumber; @@ -304,9 +305,10 @@ define([ if (this._getMenuGroupRootType() == 'logic') { header = this.task.generateCode(); } - if (this.id == 'apps_markdown') { - header = this.task.getPreview(); - } + // Deprecated: show markdown as other blocks + // if (this.id == 'apps_markdown') { + // header = this.task.getPreview(); + // } this.state.header = header; return header; } From 94a2ad983920e348021228476285bdf825ad2a25 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Sun, 24 Apr 2022 05:02:56 +0900 Subject: [PATCH 07/21] Update evaluation changes --- html/m_ml/evaluation.html | 56 +++++++++++++++++++++++++------------ js/m_ml/evaluation.js | 58 ++++++++++++++++++++++++++++++++------- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/html/m_ml/evaluation.html b/html/m_ml/evaluation.html index c08859b7..a3774fa0 100644 --- a/html/m_ml/evaluation.html +++ b/html/m_ml/evaluation.html @@ -1,20 +1,26 @@
-
- - - - - - - - +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
@@ -33,13 +39,29 @@ +
+
+ + +
- - +
+ + +
+
+ + +
+ + +
diff --git a/js/m_ml/evaluation.js b/js/m_ml/evaluation.js index 41ab3be5..71efb29c 100644 --- a/js/m_ml/evaluation.js +++ b/js/m_ml/evaluation.js @@ -41,8 +41,12 @@ define([ confusion_matrix: true, report: true, accuracy: false, precision: false, recall: false, f1_score: false, roc_curve: false, auc: false, + model: '', // clustering - silhouetteScore: true, ari: false, nm: false, + clusteredIndex: 'clusters', + silhouetteScore: true, ari: false, nmi: false, + featureData2: 'X', + targetData2: 'y', ...this.state } } @@ -62,14 +66,23 @@ define([ let modelType = $(this).val(); that.state.modelType = modelType; + $(that.wrapSelector('.vp-upper-box')).hide(); + $(that.wrapSelector('.vp-upper-box.' + modelType)).show(); + $(that.wrapSelector('.vp-eval-box')).hide(); $(that.wrapSelector('.vp-eval-'+modelType)).show(); - if (modelType == 'clf') { + if (modelType == 'rgs') { + // Regression + + } else if (modelType == 'clf') { // Classification - model selection if (that.checkToShowModel() == true) { $(that.wrapSelector('.vp-ev-model')).show(); } + } else { + // Clustering + } }); @@ -118,6 +131,25 @@ define([ varSelector.setValue(this.state.targetData); $(page).find('#targetData').replaceWith(varSelector.toTagString()); + // Clustering - data selection + varSelector = new VarSelector2(this.wrapSelector(), ['DataFrame', 'list', 'str']); + varSelector.setComponentID('clusteredIndex'); + varSelector.addClass('vp-state vp-input'); + varSelector.setValue(this.state.clusteredIndex); + $(page).find('#clusteredIndex').replaceWith(varSelector.toTagString()); + + varSelector = new VarSelector2(this.wrapSelector(), ['DataFrame', 'list', 'str']); + varSelector.setComponentID('featureData2'); + varSelector.addClass('vp-state vp-input'); + varSelector.setValue(this.state.featureData2); + $(page).find('#featureData2').replaceWith(varSelector.toTagString()); + + varSelector = new VarSelector2(this.wrapSelector(), ['DataFrame', 'list', 'str']); + varSelector.setComponentID('targetData2'); + varSelector.addClass('vp-state vp-input'); + varSelector.setValue(this.state.targetData2); + $(page).find('#targetData2').replaceWith(varSelector.toTagString()); + // model // set model list let modelOptionTag = new com_String(); @@ -169,7 +201,12 @@ define([ } }); - if (this.state.modelType == 'clf') { + $(page).find('.vp-upper-box').hide(); + $(page).find('.vp-upper-box.' + this.state.modelType).show(); + + if (this.state.modelType == 'rgs') { + + } else if (this.state.modelType == 'clf') { if (this.state.roc_curve == true || this.state.auc == true) { $(page).find('.vp-ev-model').show(); } else { @@ -197,7 +234,8 @@ define([ // regression coefficient, intercept, r_squared, mae, mape, rmse, scatter_plot, // clustering - sizeOfClusters, silhouetteScore, ari, nm + sizeOfClusters, silhouetteScore, ari, nmi, + clusteredIndex, featureData2, targetData2 } = this.state; //==================================================================== @@ -317,19 +355,19 @@ define([ if (silhouetteScore) { code = new com_String(); code.appendLine("# Silhouette score"); - code.appendFormat("print(f'Silhouette score: {metrics.cluster.silhouette_score({0}, {1})}')", targetData, predictData); + code.appendFormat("print(f'Silhouette score: {metrics.cluster.silhouette_score({0}, {1})}')", featureData2, clusteredIndex); codeCells.push(code.toString()); } if (ari) { code = new com_String(); - code.appendLine("# ARI"); - code.appendFormat("print(f'ARI: {metrics.cluster.adjusted_rand_score({0}, {1})}')", targetData, predictData); + code.appendLine("# ARI(Adjusted Rand score)"); + code.appendFormat("print(f'ARI: {metrics.cluster.adjusted_rand_score({0}, {1})}')", targetData2, clusteredIndex); codeCells.push(code.toString()); } - if (nm) { + if (nmi) { code = new com_String(); - code.appendLine("# NM"); - code.appendFormat("print(f'NM: {metrics.cluster.normalized_mutual_info_score({0}, {1})}')", targetData, predictData); + code.appendLine("# NMI(Normalized Mutual Info Score)"); + code.appendFormat("print(f'NM: {metrics.cluster.normalized_mutual_info_score({0}, {1})}')", targetData2, clusteredIndex); codeCells.push(code.toString()); } } From 83aa7fae80c90a6a95a5570b6d39a7a663c2808b Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Sun, 24 Apr 2022 05:34:23 +0900 Subject: [PATCH 08/21] Seaborn - control preview size using range --- css/m_visualize/seaborn.css | 1 + css/root.css | 3 +++ html/m_visualize/seaborn.html | 23 ++++++++++++++--------- js/m_visualize/Seaborn.js | 10 +++++++++- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/css/m_visualize/seaborn.css b/css/m_visualize/seaborn.css index 24750b65..ddbda857 100644 --- a/css/m_visualize/seaborn.css +++ b/css/m_visualize/seaborn.css @@ -48,6 +48,7 @@ grid-column-gap: 15px; align-items: baseline; align-content: center; + height: 100%; } .vp-chart-left-box { diff --git a/css/root.css b/css/root.css index 567a75ef..6048662e 100644 --- a/css/root.css +++ b/css/root.css @@ -336,6 +336,9 @@ hr.vp-extra-menu-line { width: 10px !important; } /* temporary margin */ +.mt5 { + margin-top: 5px; +} .mb5 { margin-bottom: 5px; } diff --git a/html/m_visualize/seaborn.html b/html/m_visualize/seaborn.html index 9f09f4df..256ea652 100644 --- a/html/m_visualize/seaborn.html +++ b/html/m_visualize/seaborn.html @@ -1,6 +1,6 @@ - -
+ +
-
Plot 1
-
Plot 2
-
Plot 3
+
Plot
+ +
@@ -68,13 +69,13 @@
-
+
-
+
+
@@ -172,6 +173,10 @@
+
+ + +
diff --git a/js/m_visualize/Seaborn.js b/js/m_visualize/Seaborn.js index f1505f56..d26f39c2 100644 --- a/js/m_visualize/Seaborn.js +++ b/js/m_visualize/Seaborn.js @@ -153,6 +153,11 @@ define([ that.loadPreview(); } }); + + // set preview size + $(this.wrapSelector('#previewSize')).on('change', function() { + that.loadPreview(); + }); } @@ -434,7 +439,10 @@ define([ let convertedData = data; if (preview && data != '') { // set figure size for preview chart - code.appendLine('plt.figure(figsize=(6, 5))'); + let defaultWidth = 5; + let defaultHeight = 4; + let previewSize = parseInt($(this.wrapSelector('#previewSize')).val()); + code.appendFormatLine('plt.figure(figsize=({0}, {1}))', defaultWidth + previewSize, defaultHeight + previewSize); if (useSampling) { // data sampling code for preview convertedData = data + '.sample(n=' + sampleCount + ', random_state=0)'; From a003a16944d39bd5e6d8291e210a0dfdf40e1de3 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Sun, 24 Apr 2022 05:35:35 +0900 Subject: [PATCH 09/21] small changes --- html/m_visualize/seaborn.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/html/m_visualize/seaborn.html b/html/m_visualize/seaborn.html index 256ea652..0bfb8acb 100644 --- a/html/m_visualize/seaborn.html +++ b/html/m_visualize/seaborn.html @@ -91,11 +91,11 @@
-
+
-
+
From 758ce70b911ac0eb8a7c8be4aa0983be00d742b1 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Sun, 24 Apr 2022 05:50:36 +0900 Subject: [PATCH 10/21] Action/Info app prototype --- data/libraries.json | 28 ++++++ html/m_ml/fitPredict.html | 18 ++++ html/m_ml/model.html | 29 +----- html/m_ml/modelInfo.html | 0 js/com/component/ModelEditor.js | 14 ++- js/m_ml/FitPredict.js | 158 ++++++++++++++++++++++++++++++++ js/m_ml/ModelInfo.js | 158 ++++++++++++++++++++++++++++++++ 7 files changed, 378 insertions(+), 27 deletions(-) create mode 100644 html/m_ml/fitPredict.html create mode 100644 html/m_ml/modelInfo.html create mode 100644 js/m_ml/FitPredict.js create mode 100644 js/m_ml/ModelInfo.js diff --git a/data/libraries.json b/data/libraries.json index efc8cd6a..5765c393 100644 --- a/data/libraries.json +++ b/data/libraries.json @@ -3239,6 +3239,34 @@ "icon": "apps/apps_dimension.svg" } }, + { + "id" : "ml_fitPredict", + "type" : "function", + "level": 1, + "name" : "Fit/Predict", + "tag" : "FIT,PREDICT,MACHINE LEARNING,ML", + "path" : "visualpython - machine_learning - fit_predict", + "desc" : "Model fit/predict for machine learning", + "file" : "m_ml/FitPredict", + "apps" : { + "color": 8, + "icon": "apps/apps_evaluate.svg" + } + }, + { + "id" : "ml_modelInfo", + "type" : "function", + "level": 1, + "name" : "Model Info", + "tag" : "MODEL INFO,INFORMATION,MACHINE LEARNING,ML", + "path" : "visualpython - machine_learning - model_info", + "desc" : "Model information for machine learning", + "file" : "m_ml/ModelInfo", + "apps" : { + "color": 8, + "icon": "apps/apps_evaluate.svg" + } + }, { "id" : "ml_evaluation", "type" : "function", diff --git a/html/m_ml/fitPredict.html b/html/m_ml/fitPredict.html new file mode 100644 index 00000000..034991cd --- /dev/null +++ b/html/m_ml/fitPredict.html @@ -0,0 +1,18 @@ + +
+
+ + +
+
+
+ +
+
+
+ +
+
+ \ No newline at end of file diff --git a/html/m_ml/model.html b/html/m_ml/model.html index 4c26c1c4..739aa2e0 100644 --- a/html/m_ml/model.html +++ b/html/m_ml/model.html @@ -1,9 +1,8 @@
- +
+ +
@@ -24,27 +23,5 @@
-
-
- - -
-
-
- -
-
-
- -
- -
\ No newline at end of file diff --git a/html/m_ml/modelInfo.html b/html/m_ml/modelInfo.html new file mode 100644 index 00000000..e69de29b diff --git a/js/com/component/ModelEditor.js b/js/com/component/ModelEditor.js index 9b6fa738..3b5d9b50 100644 --- a/js/com/component/ModelEditor.js +++ b/js/com/component/ModelEditor.js @@ -883,8 +883,20 @@ define([ }); } - show() { + /** + * Show Model Editor + * @param {*} showType all / action / info + */ + show(showType='all') { $(this.wrapSelector()).show(); + + // show type + if (showType == 'action') { + $(this.wrapSelector('')) + } else if (showType == 'info') { + + } + this.reload(); } diff --git a/js/m_ml/FitPredict.js b/js/m_ml/FitPredict.js new file mode 100644 index 00000000..c794dcd8 --- /dev/null +++ b/js/m_ml/FitPredict.js @@ -0,0 +1,158 @@ +/* + * Project Name : Visual Python + * Description : GUI-based Python code generator + * File Name : FitPredict.js + * Author : Black Logic + * Note : Model fit / predict + * License : GNU GPLv3 with Visual Python special exception + * Date : 2022. 04. 20 + * Change Date : + */ + +//============================================================================ +// [CLASS] FitPredict +//============================================================================ +define([ + 'text!vp_base/html/m_ml/fitPredict.html!strip', + 'vp_base/js/com/com_util', + 'vp_base/js/com/com_interface', + 'vp_base/js/com/com_String', + 'vp_base/js/com/com_generatorV2', + 'vp_base/data/m_ml/mlLibrary', + 'vp_base/js/com/component/PopupComponent', + 'vp_base/js/com/component/VarSelector2', + 'vp_base/js/com/component/ModelEditor' +], function(msHtml, com_util, com_interface, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, ModelEditor) { + + /** + * FitPredict + */ + class FitPredict extends PopupComponent { + _init() { + super._init(); + this.config.sizeLevel = 2; + this.config.dataview = false; + + this.state = { + // model selection + model: '', + method: '', + ...this.state + } + + this.modelConfig = ML_LIBRARIES; + } + + _bindEvent() { + super._bindEvent(); + /** Implement binding events */ + var that = this; + + // change model + $(this.wrapSelector('#model')).on('change', function() { + that.modelEditor.reload(); + }); + } + + templateForBody() { + let page = $(msHtml); + + let that = this; + + //================================================================ + // Model selection + //================================================================ + // set model list + let modelOptionTag = new com_String(); + vpKernel.getModelList().then(function(resultObj) { + let { result } = resultObj; + var modelList = JSON.parse(result); + modelList && modelList.forEach(model => { + let selectFlag = ''; + if (model.varName == that.state.model) { + selectFlag = 'selected'; + } + modelOptionTag.appendFormatLine('', + model.varName, model.varType, selectFlag, model.varName, model.varType); + }); + $(page).find('#model').html(modelOptionTag.toString()); + $(that.wrapSelector('#model')).html(modelOptionTag.toString()); + + if (!that.state.model || that.state.model == '') { + that.state.model = $(that.wrapSelector('#model')).val(); + } + + that.modelEditor.show('action'); + }); + + //================================================================ + // Load state + //================================================================ + Object.keys(this.state).forEach(key => { + let tag = $(page).find('#' + key); + let tagName = $(tag).prop('tagName'); // returns with UpperCase + let value = that.state[key]; + if (value == undefined) { + return; + } + switch(tagName) { + case 'INPUT': + let inputType = $(tag).prop('type'); + if (inputType == 'text' || inputType == 'number' || inputType == 'hidden') { + $(tag).val(value); + break; + } + if (inputType == 'checkbox') { + $(tag).prop('checked', value); + break; + } + break; + case 'TEXTAREA': + case 'SELECT': + default: + $(tag).val(value); + break; + } + }); + + return page; + } + + templateForOption(modelType) { + let config = this.modelConfig[modelType]; + let state = this.state; + + let optBox = new com_String(); + // render tag + config.options.forEach(opt => { + optBox.appendFormatLine('' + , opt.name, opt.name, com_util.optionToLabel(opt.name)); + let content = com_generator.renderContent(this, opt.component[0], opt, state); + optBox.appendLine(content[0].outerHTML); + }); + // render user option + optBox.appendFormatLine('', 'userOption', 'User option'); + optBox.appendFormatLine('', + 'userOption', 'key=value, ...', this.state.userOption); + return optBox.toString(); + } + + render() { + super.render(); + + // Model Editor + this.modelEditor = new ModelEditor(this, "model", "instanceEditor"); + } + + generateCode() { + let { model } = this.state; + let code = new com_String(); + code.append(this.modelEditor.getCode({'${model}': model})); + + return code.toString(); + } + + } + + return FitPredict; +}); \ No newline at end of file diff --git a/js/m_ml/ModelInfo.js b/js/m_ml/ModelInfo.js new file mode 100644 index 00000000..1660ce50 --- /dev/null +++ b/js/m_ml/ModelInfo.js @@ -0,0 +1,158 @@ +/* + * Project Name : Visual Python + * Description : GUI-based Python code generator + * File Name : ModelInfo.js + * Author : Black Logic + * Note : Model information + * License : GNU GPLv3 with Visual Python special exception + * Date : 2022. 04. 20 + * Change Date : + */ + +//============================================================================ +// [CLASS] FitPredict +//============================================================================ +define([ + 'text!vp_base/html/m_ml/fitPredict.html!strip', + 'vp_base/js/com/com_util', + 'vp_base/js/com/com_interface', + 'vp_base/js/com/com_String', + 'vp_base/js/com/com_generatorV2', + 'vp_base/data/m_ml/mlLibrary', + 'vp_base/js/com/component/PopupComponent', + 'vp_base/js/com/component/VarSelector2', + 'vp_base/js/com/component/ModelEditor' +], function(msHtml, com_util, com_interface, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, ModelEditor) { + + /** + * ModelInfo + */ + class ModelInfo extends PopupComponent { + _init() { + super._init(); + this.config.sizeLevel = 2; + this.config.dataview = false; + + this.state = { + // model selection + model: '', + method: '', + ...this.state + } + + this.modelConfig = ML_LIBRARIES; + } + + _bindEvent() { + super._bindEvent(); + /** Implement binding events */ + var that = this; + + // change model + $(this.wrapSelector('#model')).on('change', function() { + that.modelEditor.reload(); + }); + } + + templateForBody() { + let page = $(msHtml); + + let that = this; + + //================================================================ + // Model selection + //================================================================ + // set model list + let modelOptionTag = new com_String(); + vpKernel.getModelList().then(function(resultObj) { + let { result } = resultObj; + var modelList = JSON.parse(result); + modelList && modelList.forEach(model => { + let selectFlag = ''; + if (model.varName == that.state.model) { + selectFlag = 'selected'; + } + modelOptionTag.appendFormatLine('', + model.varName, model.varType, selectFlag, model.varName, model.varType); + }); + $(page).find('#model').html(modelOptionTag.toString()); + $(that.wrapSelector('#model')).html(modelOptionTag.toString()); + + if (!that.state.model || that.state.model == '') { + that.state.model = $(that.wrapSelector('#model')).val(); + } + + that.modelEditor.show(); + }); + + //================================================================ + // Load state + //================================================================ + Object.keys(this.state).forEach(key => { + let tag = $(page).find('#' + key); + let tagName = $(tag).prop('tagName'); // returns with UpperCase + let value = that.state[key]; + if (value == undefined) { + return; + } + switch(tagName) { + case 'INPUT': + let inputType = $(tag).prop('type'); + if (inputType == 'text' || inputType == 'number' || inputType == 'hidden') { + $(tag).val(value); + break; + } + if (inputType == 'checkbox') { + $(tag).prop('checked', value); + break; + } + break; + case 'TEXTAREA': + case 'SELECT': + default: + $(tag).val(value); + break; + } + }); + + return page; + } + + templateForOption(modelType) { + let config = this.modelConfig[modelType]; + let state = this.state; + + let optBox = new com_String(); + // render tag + config.options.forEach(opt => { + optBox.appendFormatLine('' + , opt.name, opt.name, com_util.optionToLabel(opt.name)); + let content = com_generator.renderContent(this, opt.component[0], opt, state); + optBox.appendLine(content[0].outerHTML); + }); + // render user option + optBox.appendFormatLine('', 'userOption', 'User option'); + optBox.appendFormatLine('', + 'userOption', 'key=value, ...', this.state.userOption); + return optBox.toString(); + } + + render() { + super.render(); + + // Model Editor + this.modelEditor = new ModelEditor(this, "model", "instanceEditor"); + } + + generateCode() { + let { model } = this.state; + let code = new com_String(); + code.append(this.modelEditor.getCode({'${model}': model})); + + return code.toString(); + } + + } + + return ModelInfo; +}); \ No newline at end of file From 215a28818a922f6cfe13615be37fb60069a89637 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Mon, 25 Apr 2022 02:10:31 +0900 Subject: [PATCH 11/21] Fix Fit/predict & Model info ML Apps --- css/m_ml/fitPredict.css | 43 +++ css/m_ml/modelInfo.css | 43 +++ data/libraries.json | 4 +- html/m_ml/fitPredict.html | 21 +- html/m_ml/modelInfo.html | 33 ++ js/com/component/ModelEditor.js | 9 +- js/m_ml/FitPredict.js | 512 +++++++++++++++++++++++++++++++- js/m_ml/ModelInfo.js | 462 +++++++++++++++++++++++++++- 8 files changed, 1096 insertions(+), 31 deletions(-) create mode 100644 css/m_ml/fitPredict.css create mode 100644 css/m_ml/modelInfo.css diff --git a/css/m_ml/fitPredict.css b/css/m_ml/fitPredict.css new file mode 100644 index 00000000..871aec8d --- /dev/null +++ b/css/m_ml/fitPredict.css @@ -0,0 +1,43 @@ +.vp-ins-select-title { + font-weight: bold; + color: var(--font-hightlight); + padding: 5px 5px 5px 0px; +} +.vp-ins-select-container input.vp-ins-search { + width: 100%; +} +.vp-ins-select-container .vp-ins-search-icon { + position: absolute; + color: #C4C4C4; + right: 7px; + top: 7px; +} +.vp-ins-select-box { + margin-top: 5px; + border: 0.25px solid var(--border-gray-color); +} +.vp-ins-select-list { + height: 145px; + width: 100%; + list-style: none; + margin: 0px; + padding: 0px 5px 0px 5px; + color: #696969; + overflow: auto; +} +.vp-ins-select-item { + padding-top: 3px; + padding-bottom: 3px; + cursor: pointer; + border-bottom: 0.25px solid var(--light-gray-color); +} +.vp-ins-select-item.selected { + color: var(--font-hightlight); + background: var(--light-gray-color); +} +.vp-ins-select-item:hover { + background: var(--light-gray-color); +} +.vp-ins-parameter-box { + grid-column: 1/3; +} diff --git a/css/m_ml/modelInfo.css b/css/m_ml/modelInfo.css new file mode 100644 index 00000000..871aec8d --- /dev/null +++ b/css/m_ml/modelInfo.css @@ -0,0 +1,43 @@ +.vp-ins-select-title { + font-weight: bold; + color: var(--font-hightlight); + padding: 5px 5px 5px 0px; +} +.vp-ins-select-container input.vp-ins-search { + width: 100%; +} +.vp-ins-select-container .vp-ins-search-icon { + position: absolute; + color: #C4C4C4; + right: 7px; + top: 7px; +} +.vp-ins-select-box { + margin-top: 5px; + border: 0.25px solid var(--border-gray-color); +} +.vp-ins-select-list { + height: 145px; + width: 100%; + list-style: none; + margin: 0px; + padding: 0px 5px 0px 5px; + color: #696969; + overflow: auto; +} +.vp-ins-select-item { + padding-top: 3px; + padding-bottom: 3px; + cursor: pointer; + border-bottom: 0.25px solid var(--light-gray-color); +} +.vp-ins-select-item.selected { + color: var(--font-hightlight); + background: var(--light-gray-color); +} +.vp-ins-select-item:hover { + background: var(--light-gray-color); +} +.vp-ins-parameter-box { + grid-column: 1/3; +} diff --git a/data/libraries.json b/data/libraries.json index 5765c393..eefddfce 100644 --- a/data/libraries.json +++ b/data/libraries.json @@ -3250,7 +3250,7 @@ "file" : "m_ml/FitPredict", "apps" : { "color": 8, - "icon": "apps/apps_evaluate.svg" + "icon": "apps/apps_fit.svg" } }, { @@ -3264,7 +3264,7 @@ "file" : "m_ml/ModelInfo", "apps" : { "color": 8, - "icon": "apps/apps_evaluate.svg" + "icon": "apps/apps_predict.svg" } }, { diff --git a/html/m_ml/fitPredict.html b/html/m_ml/fitPredict.html index 034991cd..7ac864ff 100644 --- a/html/m_ml/fitPredict.html +++ b/html/m_ml/fitPredict.html @@ -7,12 +7,27 @@
-
- + + +
+
Action
+
+ + + +
+
+
    + +
+
-
+
+
Options
+
+
\ No newline at end of file diff --git a/html/m_ml/modelInfo.html b/html/m_ml/modelInfo.html index e69de29b..faf57c37 100644 --- a/html/m_ml/modelInfo.html +++ b/html/m_ml/modelInfo.html @@ -0,0 +1,33 @@ + +
+
+ + +
+
+ + +
+
Info
+
+ + + +
+
+
    + +
+
+
+
+
+
Options
+
+ +
+
+
+ \ No newline at end of file diff --git a/js/com/component/ModelEditor.js b/js/com/component/ModelEditor.js index 3b5d9b50..2eee09cf 100644 --- a/js/com/component/ModelEditor.js +++ b/js/com/component/ModelEditor.js @@ -887,16 +887,9 @@ define([ * Show Model Editor * @param {*} showType all / action / info */ - show(showType='all') { + show() { $(this.wrapSelector()).show(); - // show type - if (showType == 'action') { - $(this.wrapSelector('')) - } else if (showType == 'info') { - - } - this.reload(); } diff --git a/js/m_ml/FitPredict.js b/js/m_ml/FitPredict.js index c794dcd8..ed0cb641 100644 --- a/js/m_ml/FitPredict.js +++ b/js/m_ml/FitPredict.js @@ -14,15 +14,15 @@ //============================================================================ define([ 'text!vp_base/html/m_ml/fitPredict.html!strip', + 'css!vp_base/css/m_ml/fitPredict.css', 'vp_base/js/com/com_util', - 'vp_base/js/com/com_interface', 'vp_base/js/com/com_String', 'vp_base/js/com/com_generatorV2', 'vp_base/data/m_ml/mlLibrary', 'vp_base/js/com/component/PopupComponent', 'vp_base/js/com/component/VarSelector2', - 'vp_base/js/com/component/ModelEditor' -], function(msHtml, com_util, com_interface, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, ModelEditor) { + 'vp_base/js/com/component/SuggestInput' +], function(msHtml, msCss, com_util, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, SuggestInput) { /** * FitPredict @@ -37,6 +37,9 @@ define([ // model selection model: '', method: '', + action: {}, + config: {}, + userOption: '', ...this.state } @@ -50,7 +53,15 @@ define([ // change model $(this.wrapSelector('#model')).on('change', function() { - that.modelEditor.reload(); + that.reload(); + }); + + // click option + $(this.wrapSelector('.vp-ins-select-item')).on('click', function() { + let name = $(this).data('var-name'); + let type = $(this).data('var-type'); + + that.renderOptionPage(type, name); }); } @@ -82,7 +93,7 @@ define([ that.state.model = $(that.wrapSelector('#model')).val(); } - that.modelEditor.show('action'); + that.reload(); }); //================================================================ @@ -140,18 +151,503 @@ define([ render() { super.render(); - // Model Editor - this.modelEditor = new ModelEditor(this, "model", "instanceEditor"); + this.reload(); + } + + reload() { + let modelTag = $(this.wrapSelector('#model')); + let model = $(modelTag).val(); + let modelType = $(modelTag).find('option:selected').data('type'); + + let actions = this.getAction(modelType); + // let infos = this.getInfo(modelType); + this.state.action = { ...actions }; + // this.state.info = { ...infos }; + + var actListTag = new com_String(); + // var infoListTag = new com_String(); + + Object.keys(actions).forEach(actKey => { + let titleText = actions[actKey].description; + if (actions[actKey].name != actions[actKey].label) { + titleText = actions[actKey].name + ': ' + titleText; + } + actListTag.appendFormatLine('
  • {4}
  • ', + 'vp-ins-select-item', actKey, 'action', titleText, actions[actKey].label); + }); + // Object.keys(infos).forEach(infoKey => { + // let titleText = infos[infoKey].description; + // if (infos[infoKey].name != infos[infoKey].label) { + // titleText = infos[infoKey].name + ': ' + titleText; + // } + // infoListTag.appendFormatLine('
  • {4}
  • ', + // 'vp-ins-select-item', infoKey, 'info', titleText, infos[infoKey].label); + // }); + + $(this.wrapSelector('.vp-ins-select-list.action')).html(actListTag.toString()); + // $(this.wrapSelector('.vp-ins-select-list.info')).html(infoListTag.toString()); + + let that = this; + // action search suggest + var suggestInput = new SuggestInput(); + suggestInput.addClass('vp-input'); + suggestInput.addClass('vp-ins-search'); + suggestInput.setPlaceholder("Search Action"); + suggestInput.setSuggestList(function () { return Object.keys(actions); }); + suggestInput.setSelectEvent(function (value, item) { + $(this.wrapSelector()).val(value); + $(that.wrapSelector('.vp-ins-type.action')).val(value); + + $(that.wrapSelector('.vp-ins-select-item[data-var-name="' + value + '"]')).click(); + }); + $(that.wrapSelector('.vp-ins-search')).replaceWith(function () { + return suggestInput.toTagString(); + }); + + // info search suggest + // suggestInput = new SuggestInput(); + // suggestInput.addClass('vp-input'); + // suggestInput.addClass('vp-ins-search'); + // suggestInput.setPlaceholder("Search info"); + // suggestInput.setSuggestList(function () { return Object.keys(infos); }); + // suggestInput.setSelectEvent(function (value, item) { + // $(this.wrapSelector()).val(value); + // $(that.wrapSelector('.vp-ins-type.info')).val(value); + + // $(that.wrapSelector('.vp-ins-select-item[data-var-name="' + value + '"]')).click(); + // }); + // $(that.wrapSelector('.vp-ins-search')).replaceWith(function () { + // return suggestInput.toTagString(); + // }); + + // bind event + this._bindEvent(); + + // load once on initializing page + if (this.loaded == false) { + let { modelEditorType, modelEditorName } = this.pageThis.state; + if (modelEditorType != '' && modelEditorName != '') { + // render option page for saved state + that.renderOptionPage(modelEditorType, modelEditorName); + } + // set loaded true + this.loaded = true; + } + } + + /** + * Render option page for selected option + * @param {String} type action / info + * @param {String} name option name (ex. fit/predict/...) + */ + renderOptionPage(type, name) { + if (this.state[type] != undefined && this.state[type][name] != undefined) { + let config = this.state[type][name]; + let optBox = new com_String(); + // render tag + config && config.options && config.options.forEach(opt => { + let label = opt.name; + if (opt.label != undefined) { + label = opt.label; + } + // fix label + label = com_util.optionToLabel(label); + optBox.appendFormatLine('' + , opt.name, opt.name, label); + let content = com_generator.renderContent(this, opt.component[0], opt, this.state); + optBox.appendLine(content[0].outerHTML); + }); + // replace option box + $(this.wrapSelector('.vp-ins-parameter-box')).html(optBox.toString()); + + this.state.config = config; + + // add selection + $(this.wrapSelector('.vp-ins-select-item')).removeClass('selected'); + let typeClass = '.vp-ins-select-list.' + type; + let nameClass = '.vp-ins-select-item[data-var-name="' + name + '"]'; + $(this.wrapSelector(typeClass + ' ' + nameClass)).addClass('selected'); + // set state + $(this.wrapSelector('#modelEditorType')).val(type); + $(this.wrapSelector('#modelEditorName')).val(name); + this.state.modelEditorType = type; + this.state.modelEditorName = name; + } } generateCode() { let { model } = this.state; let code = new com_String(); - code.append(this.modelEditor.getCode({'${model}': model})); + let replaceDict = {'${model}': model}; + + if (this.state.config.import != undefined) { + code.appendLine(this.state.config.import); + code.appendLine(); + } + let modelCode = com_generator.vp_codeGenerator(this.pageThis, this.state.config, this.pageThis.state); + Object.keys(replaceDict).forEach(key => { + modelCode = modelCode.replace(key, replaceDict[key]); + }); + code.append(modelCode); + + let allocateIdx = modelCode.indexOf(' = '); + if (allocateIdx >= 0) { + let allocateCode = modelCode.substr(0, allocateIdx); + code.appendLine(); + code.append(allocateCode); + } return code.toString(); } + getModelCategory(modelType) { + let mlDict = vpConfig.getMLDataDict(); + let keys = Object.keys(mlDict); + let modelCategory = ''; + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + if (mlDict[key].includes(modelType)) { + modelCategory = key; + break; + } + } + return modelCategory; + } + + getAction(modelType) { + let category = this.getModelCategory(modelType); + let defaultActions = { + 'fit': { + name: 'fit', + label: 'Fit', + code: '${model}.fit(${fit_featureData}, ${fit_targetData})', + description: 'Perform modeling from features, or distance matrix.', + options: [ + { name: 'fit_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X_train' }, + { name: 'fit_targetData', label: 'Target Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'y_train' } + ] + }, + 'predict': { + name: 'predict', + label: 'Predict', + code: '${pred_allocate} = ${model}.predict(${pred_featureData})', + description: 'Predict the closest target data X belongs to.', + options: [ + { name: 'pred_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X_test' }, + { name: 'pred_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'pred' } + ] + }, + 'predict_proba': { + name: 'predict_proba', + label: 'Predict probability', + code: '${pred_prob_allocate} = ${model}.predict_proba(${pred_prob_featureData})', + description: 'Predict class probabilities for X.', + options: [ + { name: 'pred_prob_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X_test' }, + { name: 'pred_prob_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'pred' } + ] + }, + 'transform': { + name: 'transform', + label: 'Transform', + code: '${trans_allocate} = ${model}.transform(${trans_featureData})', + description: 'Apply dimensionality reduction to X.', + options: [ + { name: 'trans_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'trans_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'trans' } + ] + } + }; + let actions = {}; + switch (category) { + case 'Data Preparation': + actions = { + 'fit': { + name: 'fit', + label: 'Fit', + code: '${model}.fit(${fit_featureData})', + description: 'Fit Encoder/Scaler to X.', + options: [ + { name: 'fit_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' } + ] + }, + 'fit_transform': { + name: 'fit_transform', + label: 'Fit and transform', + code: '${fit_trans_allocate} = ${model}.fit_transform(${fit_trans_featureData})', + description: 'Fit Encoder/Scaler to X, then transform X.', + options: [ + { name: 'fit_trans_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'fit_trans_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'trans' } + ] + }, + 'transform': { + ...defaultActions['transform'], + description: 'Transform labels to normalized encoding.' + } + } + + if (modelType != 'ColumnTransformer') { + actions = { + ...actions, + 'inverse_transform': { + name: 'inverse_transform', + label: 'Inverse transform', + code: '${inverse_allocate} = ${model}.inverse_transform(${inverse_featureData})', + description: 'Transform binary labels back to multi-class labels.', + options: [ + { name: 'inverse_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'inverse_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'inv_trans' } + ] + } + } + } + break; + case 'Regression': + actions = { + 'fit': defaultActions['fit'], + 'predict': defaultActions['predict'] + } + break; + case 'Classification': + actions = { + 'fit': defaultActions['fit'], + 'predict': defaultActions['predict'], + 'predict_proba': defaultActions['predict_proba'], + } + if (['LogisticRegression', 'SVC', 'GradientBoostingClassifier'].includes(modelType)) { + actions = { + ...actions, + 'decision_function': { + name: 'decision_function', + label: 'Decision function', + code: '${dec_allocate} = ${model}.decision_function(${dec_featureData})', + description: 'Compute the decision function of X.', + options: [ + { name: 'dec_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'dec_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable' } + ] + } + } + } + break; + case 'Auto ML': + actions = { + 'fit': defaultActions['fit'], + 'predict': defaultActions['predict'], + 'fit_predict': { + name: 'fit_predict', + label: 'Fit and predict', + code: '${fit_pred_allocate} = ${model}.fit_predict(${fit_pred_featureData})', + description: 'Fit and predict.', + options: [ + { name: 'fit_pred_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'fit_pred_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'pred' } + ] + }, + 'predict_proba': defaultActions['predict_proba'] + } + break; + case 'Clustering': + if (modelType == 'AgglomerativeClustering' + || modelType == 'DBSCAN') { + actions = { + 'fit': { + name: 'fit', + label: 'Fit', + code: '${model}.fit(${fit_featureData})', + description: 'Perform clustering from features, or distance matrix.', + options: [ + { name: 'fit_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' } + ] + }, + 'fit_predict': { + name: 'fit_predict', + label: 'Fit and predict', + code: '${fit_pred_allocate} = ${model}.fit_predict(${fit_pred_featureData})', + description: 'Compute clusters from a data or distance matrix and predict labels.', + options: [ + { name: 'fit_pred_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'fit_pred_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'pred' } + ] + } + } + break; + } + actions = { + 'fit': { + name: 'fit', + label: 'Fit', + code: '${model}.fit(${fit_featureData})', + description: 'Compute clustering.', + options: [ + { name: 'fit_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' } + ] + }, + 'predict': { + name: 'predict', + label: 'Predict', + code: '${pred_allocate} = ${model}.predict(${pred_featureData})', + description: 'Predict the closest target data X belongs to.', + options: [ + { name: 'pred_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'pred_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'pred' } + ] + }, + 'fit_predict': { + name: 'fit_predict', + label: 'Fit and predict', + code: '${fit_pred_allocate} = ${model}.fit_predict(${fit_pred_featureData})', + description: 'Compute cluster centers and predict cluster index for each sample.', + options: [ + { name: 'fit_pred_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'fit_pred_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'pred' } + ] + } + } + if (modelType == 'KMeans') { + actions = { + ...actions, + 'fit_transform': { + name: 'fit_transform', + label: 'Fit and transform', + code: '${fit_trans_allocate} = ${model}.fit_transform(${fit_trans_featureData})', + description: 'Compute clustering and transform X to cluster-distance space.', + options: [ + { name: 'fit_trans_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'fit_trans_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'trans' } + ] + }, + 'transform': { + name: 'transform', + label: 'Transform', + code: '${trans_allocate} = ${model}.transform(${trans_featureData})', + description: 'Transform X to a cluster-distance space.', + options: [ + { name: 'trans_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'trans_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'trans' } + ] + } + } + } + break; + case 'Dimension Reduction': + if (modelType == 'TSNE') { + actions = { + 'fit': { + name: 'fit', + label: 'Fit', + code: '${model}.fit(${fit_featureData})', + description: 'Fit X into an embedded space.', + options: [ + { name: 'fit_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' } + ] + }, + 'fit_transform': { + name: 'fit_transform', + label: 'Fit and transform', + code: '${fit_trans_allocate} = ${model}.fit_transform(${fit_trans_featureData})', + description: 'Fit X into an embedded space and return that transformed output.', + options: [ + { name: 'fit_trans_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'fit_trans_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'trans' } + ] + } + } + break; + } + if (modelType == 'LinearDiscriminantAnalysis') { // LDA + actions = { + 'fit': { + name: 'fit', + label: 'Fit', + code: '${model}.fit(${fit_featureData}, ${fit_targetData})', + description: 'Fit the Linear Discriminant Analysis model.', + options: [ + { name: 'fit_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'fit_targetData', label: 'Target Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'y' } + ] + }, + 'fit_transform': { + name: 'fit_transform', + label: 'Fit and transform', + code: '${fit_trans_allocate} = ${model}.fit_transform(${fit_trans_featureData}${fit_trans_targetData})', + description: 'Fit to data, then transform it.', + options: [ + { name: 'fit_trans_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'fit_trans_targetData', label: 'Target Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'y' }, + { name: 'fit_trans_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'trans' } + ] + }, + 'predict': { + name: 'predict', + label: 'Predict', + code: '${pred_allocate} = ${model}.predict(${pred_featureData})', + description: 'Predict class labels for samples in X.', + options: [ + { name: 'pred_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'pred_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'pred' } + ] + }, + 'transform': { + name: 'transform', + label: 'Transform', + code: '${trans_allocate} = ${model}.transform(${trans_featureData})', + description: 'Project data to maximize class separation.', + options: [ + { name: 'trans_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'trans_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'trans' } + ] + } + } + break; + } + actions = { + 'fit': { + name: 'fit', + label: 'Fit', + code: '${model}.fit(${fit_featureData})', + description: 'Fit X into an embedded space.', + options: [ + { name: 'fit_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' } + ] + }, + 'fit_transform': { + name: 'fit_transform', + label: 'Fit and transform', + code: '${fit_trans_allocate} = ${model}.fit_transform(${fit_trans_featureData})', + description: 'Fit the model with X and apply the dimensionality reduction on X.', + options: [ + { name: 'fit_trans_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'fit_trans_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'trans' } + ] + }, + 'inverse_transform': { + name: 'inverse_transform', + label: 'Inverse transform', + code: '${inverse_allocate} = ${model}.inverse_transform(${inverse_featureData})', + description: 'Transform data back to its original space.', + options: [ + { name: 'inverse_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'inverse_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'inv_trans' } + ] + }, + 'transform': { + name: 'transform', + label: 'Transform', + code: '${trans_allocate} = ${model}.transform(${trans_featureData})', + description: 'Apply dimensionality reduction to X.', + options: [ + { name: 'trans_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'trans_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'trans' } + ] + } + } + break; + } + return actions; + } + } return FitPredict; diff --git a/js/m_ml/ModelInfo.js b/js/m_ml/ModelInfo.js index 1660ce50..860e1548 100644 --- a/js/m_ml/ModelInfo.js +++ b/js/m_ml/ModelInfo.js @@ -10,19 +10,19 @@ */ //============================================================================ -// [CLASS] FitPredict +// [CLASS] ModelInfo //============================================================================ define([ - 'text!vp_base/html/m_ml/fitPredict.html!strip', + 'text!vp_base/html/m_ml/modelInfo.html!strip', + 'css!vp_base/css/m_ml/modelInfo.css', 'vp_base/js/com/com_util', - 'vp_base/js/com/com_interface', 'vp_base/js/com/com_String', 'vp_base/js/com/com_generatorV2', 'vp_base/data/m_ml/mlLibrary', 'vp_base/js/com/component/PopupComponent', 'vp_base/js/com/component/VarSelector2', - 'vp_base/js/com/component/ModelEditor' -], function(msHtml, com_util, com_interface, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, ModelEditor) { + 'vp_base/js/com/component/SuggestInput' +], function(msHtml, msCss, com_util, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, SuggestInput) { /** * ModelInfo @@ -37,6 +37,9 @@ define([ // model selection model: '', method: '', + info: {}, + config: {}, + userOption: '', ...this.state } @@ -50,7 +53,15 @@ define([ // change model $(this.wrapSelector('#model')).on('change', function() { - that.modelEditor.reload(); + that.reload(); + }); + + // click option + $(this.wrapSelector('.vp-ins-select-item')).on('click', function() { + let name = $(this).data('var-name'); + let type = $(this).data('var-type'); + + that.renderOptionPage(type, name); }); } @@ -82,7 +93,7 @@ define([ that.state.model = $(that.wrapSelector('#model')).val(); } - that.modelEditor.show(); + that.reload(); }); //================================================================ @@ -140,18 +151,449 @@ define([ render() { super.render(); - // Model Editor - this.modelEditor = new ModelEditor(this, "model", "instanceEditor"); + this.reload(); + } + + reload() { + let modelTag = $(this.wrapSelector('#model')); + let model = $(modelTag).val(); + let modelType = $(modelTag).find('option:selected').data('type'); + + let infos = this.getInfo(modelType); + this.state.info = { ...infos }; + + var infoListTag = new com_String(); + + Object.keys(infos).forEach(infoKey => { + let titleText = infos[infoKey].description; + if (infos[infoKey].name != infos[infoKey].label) { + titleText = infos[infoKey].name + ': ' + titleText; + } + infoListTag.appendFormatLine('
  • {4}
  • ', + 'vp-ins-select-item', infoKey, 'info', titleText, infos[infoKey].label); + }); + + // $(this.wrapSelector('.vp-ins-select-list.action')).html(actListTag.toString()); + $(this.wrapSelector('.vp-ins-select-list.info')).html(infoListTag.toString()); + + let that = this; + // action search suggest + // var suggestInput = new SuggestInput(); + // suggestInput.addClass('vp-input'); + // suggestInput.addClass('vp-ins-search'); + // suggestInput.setPlaceholder("Search Action"); + // suggestInput.setSuggestList(function () { return Object.keys(actions); }); + // suggestInput.setSelectEvent(function (value, item) { + // $(this.wrapSelector()).val(value); + // $(that.wrapSelector('.vp-ins-type.action')).val(value); + + // $(that.wrapSelector('.vp-ins-select-item[data-var-name="' + value + '"]')).click(); + // }); + // $(that.wrapSelector('.vp-ins-search')).replaceWith(function () { + // return suggestInput.toTagString(); + // }); + + // info search suggest + let suggestInput = new SuggestInput(); + suggestInput.addClass('vp-input'); + suggestInput.addClass('vp-ins-search'); + suggestInput.setPlaceholder("Search info"); + suggestInput.setSuggestList(function () { return Object.keys(infos); }); + suggestInput.setSelectEvent(function (value, item) { + $(this.wrapSelector()).val(value); + $(that.wrapSelector('.vp-ins-type.info')).val(value); + + $(that.wrapSelector('.vp-ins-select-item[data-var-name="' + value + '"]')).click(); + }); + $(that.wrapSelector('.vp-ins-search')).replaceWith(function () { + return suggestInput.toTagString(); + }); + + // bind event + this._bindEvent(); + + // load once on initializing page + if (this.loaded == false) { + let { modelEditorType, modelEditorName } = this.pageThis.state; + if (modelEditorType != '' && modelEditorName != '') { + // render option page for saved state + that.renderOptionPage(modelEditorType, modelEditorName); + } + // set loaded true + this.loaded = true; + } + } + + /** + * Render option page for selected option + * @param {String} type action / info + * @param {String} name option name (ex. fit/predict/...) + */ + renderOptionPage(type, name) { + if (this.state[type] != undefined && this.state[type][name] != undefined) { + let config = this.state[type][name]; + let optBox = new com_String(); + // render tag + config && config.options && config.options.forEach(opt => { + let label = opt.name; + if (opt.label != undefined) { + label = opt.label; + } + // fix label + label = com_util.optionToLabel(label); + optBox.appendFormatLine('' + , opt.name, opt.name, label); + let content = com_generator.renderContent(this, opt.component[0], opt, this.state); + optBox.appendLine(content[0].outerHTML); + }); + // replace option box + $(this.wrapSelector('.vp-ins-parameter-box')).html(optBox.toString()); + + this.state.config = config; + + // add selection + $(this.wrapSelector('.vp-ins-select-item')).removeClass('selected'); + let typeClass = '.vp-ins-select-list.' + type; + let nameClass = '.vp-ins-select-item[data-var-name="' + name + '"]'; + $(this.wrapSelector(typeClass + ' ' + nameClass)).addClass('selected'); + // set state + $(this.wrapSelector('#modelEditorType')).val(type); + $(this.wrapSelector('#modelEditorName')).val(name); + this.state.modelEditorType = type; + this.state.modelEditorName = name; + } } generateCode() { let { model } = this.state; let code = new com_String(); - code.append(this.modelEditor.getCode({'${model}': model})); + let replaceDict = {'${model}': model}; + + if (this.state.config.import != undefined) { + code.appendLine(this.state.config.import); + code.appendLine(); + } + let modelCode = com_generator.vp_codeGenerator(this.pageThis, this.state.config, this.pageThis.state); + Object.keys(replaceDict).forEach(key => { + modelCode = modelCode.replace(key, replaceDict[key]); + }); + code.append(modelCode); + + let allocateIdx = modelCode.indexOf(' = '); + if (allocateIdx >= 0) { + let allocateCode = modelCode.substr(0, allocateIdx); + code.appendLine(); + code.append(allocateCode); + } return code.toString(); } + getModelCategory(modelType) { + let mlDict = vpConfig.getMLDataDict(); + let keys = Object.keys(mlDict); + let modelCategory = ''; + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + if (mlDict[key].includes(modelType)) { + modelCategory = key; + break; + } + } + return modelCategory; + } + + getInfo(modelType) { + let category = this.getModelCategory(modelType); + let infos = {}; + let defaultInfos = { + 'score': { + name: 'score', + label: 'Score', + code: '${score_allocate} = ${model}.score(${score_featureData}, ${score_targetData})', + description: '', + options: [ + { name: 'score_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'score_targetData', label: 'Target Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'y' }, + { name: 'score_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'scores' } + ] + }, + 'get_params': { + name: 'get_params', + label: 'Get parameters', + code: '${param_allocate} = ${model}.get_params(${deep})', + description: 'Get parameters for this estimator.', + options: [ + { name: 'deep', component: ['bool_select'], default: 'True', usePair: true }, + { name: 'param_allocate', label: 'Allocate to', component: ['input'], value: 'params' } + ] + }, + 'permutation_importance': { + name: 'permutation_importance', + label: 'Permutation importance', + import: 'from sklearn.inspection import permutation_importance', + code: '${importance_allocate} = permutation_importance(${model}, ${importance_featureData}, ${importance_targetData}${scoring}${random_state}${etc})', + description: 'Permutation importance for feature evaluation.', + options: [ + { name: 'importance_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X_train' }, + { name: 'importance_targetData', label: 'Target Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'y_train' }, + { name: 'scoring', component: ['input'], usePair: true }, + { name: 'random_state', component: ['input_number'], placeholder: '123', usePair: true }, + { name: 'importance_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'importances' } + ] + } + } + switch (category) { + case 'Data Preparation': + if (modelType == 'OneHotEncoder') { + infos = { + 'categories_': { // TODO: + name: 'categories_', + label: 'Categories', + code: '${categories_allocate} = ${model}.categories_', + description: 'The categories of each feature determined during fitting', + options: [ + { name: 'categories_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'categories' } + ] + }, + 'get_feature_names_out': { + name: 'get_feature_names_out', + label: 'Get feature names', + code: '${feature_names_allocate} = ${model}.get_feature_names_out()', + description: 'Get output feature names.', + options: [ + { name: 'feature_names_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'features' } + ] + } + } + } + if (modelType == 'LabelEncoder') { + infos = { + 'classes_': { + name: 'classes_', + label: 'Classes', + code: '${classes_allocate} = ${model}.classes_', + description: 'Holds the label for each class.', + options: [ + { name: 'classes_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'classes' } + ] + } + } + } + if (modelType == 'KBinsDiscretizer') { + infos = { + 'bin_edges': { // TODO: + name: 'bin_edges', + label: 'Bin edges', + code: '${bin_edges_allocate} = ${model}.bin_edges_', + description: 'The edges of each bin. Contain arrays of varying shapes', + options: [ + { name: 'bin_edges_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'bin_edges' } + ] + } + } + } + if (modelType == 'ColumnTransformer') { + infos = { + 'transformers_': { + name: 'transformers_', + label: 'Transformers_', + code: '${transformers_allocate} = ${model}.transformers_', + description: 'The collection of fitted transformers as tuples of (name, fitted_transformer, column).', + options: [ + { name: 'transformers_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'classes' } + ] + }, + 'get_feature_names_out': { + name: 'get_feature_names_out', + label: 'Get feature names', + code: '${feature_names_allocate} = ${model}.get_feature_names_out()', + description: 'Get output feature names.', + options: [ + { name: 'feature_names_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'features' } + ] + } + } + } + infos = { + ...infos, + 'get_params': defaultInfos['get_params'] + } + break; + case 'Regression': + infos = { + 'score': { + ...defaultInfos['score'], + description: 'Return the coefficient of determination of the prediction.' + }, + 'cross_val_score': { + name: 'cross_val_score', + label: 'Cross validation score', + import: 'from sklearn.model_selection import cross_val_score', + code: '${cvs_allocate} = cross_val_score(${model}, ${cvs_featureData}, ${cvs_targetData}${scoring}${cv})', + description: 'Evaluate a score by cross-validation.', + options: [ + { name: 'cvs_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'cvs_targetData', label: 'Target Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'y' }, + { name: 'scoring', component: ['option_select'], usePair: true, type: 'text', + options: [ + '', + 'explained_variance', 'max_error', 'neg_mean_absolute_error', 'neg_mean_squared_error', 'neg_root_mean_squared_error', + 'neg_mean_squared_log_error', 'neg_median_absolute_error', 'r2', 'neg_mean_poisson_deviance', 'neg_mean_gamma_deviance', + 'neg_mean_absolute_percentage_error' + ] }, + { name: 'cv', label: 'Cross Validation', component: ['input_number'], placeholder: '1 ~ 10', default: 5, usePair: true }, + { name: 'cvs_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'scores' } + ] + }, + 'permutation_importance': defaultInfos['permutation_importance'], + 'Coefficient': { + name: 'coef_', + label: 'Coefficient', + code: '${coef_allocate} = ${model}.coef_', + description: 'Weights assigned to the features.', + options: [ + { name: 'coef_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'coef' } + ] + }, + 'Intercept': { + name: 'intercept_', + label: 'Intercept', + code: '${intercept_allocate} = ${model}.intercept_', + description: 'Constants in decision function.', + options: [ + { name: 'intercept_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'intercepts' } + ] + } + } + break; + case 'Classification': + infos = { + 'score': { + ...defaultInfos['score'], + description: 'Return the mean accuracy on the given test data and labels.' + }, + 'cross_val_score': { + name: 'cross_val_score', + label: 'Cross validation score', + import: 'from sklearn.model_selection import cross_val_score', + code: '${cvs_allocate} = cross_val_score(${model}, ${cvs_featureData}, ${cvs_targetData}${scoring}${cv})', + description: 'Evaluate a score by cross-validation.', + options: [ + { name: 'cvs_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'cvs_targetData', label: 'Target Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'y' }, + { name: 'scoring', component: ['option_select'], usePair: true, type: 'text', + options: [ + '', + 'accuracy', 'balanced_accuracy', 'top_k_accuracy', 'average_precision', 'neg_brier_score', + 'f1', 'f1_micro', 'f1_macro', 'f1_weighted', 'f1_samples', 'neg_log_loss', 'precision', 'recall', 'jaccard', + 'roc_auc', 'roc_auc_ovr', 'roc_auc_ovo', 'roc_auc_ovr_weighted', 'roc_auc_ovo_weighted' + ] }, + { name: 'cv', label: 'Cross Validation', component: ['input_number'], placeholder: '1 ~ 10', default: 5, usePair: true }, + { name: 'cvs_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'scores' } + ] + }, + 'permutation_importance': defaultInfos['permutation_importance'] + } + break; + case 'Auto ML': + infos = { + 'score': { + ...defaultInfos['score'], + description: 'Return the mean accuracy on the given test data and labels.' + }, + 'get_params': { + ...defaultInfos['get_params'] + } + } + break; + case 'Clustering': + infos = { + // 'Size of clusters': { + // name: 'Size of clusters', + // code: "print(f'Size of clusters: {np.bincount(pred)}')", // FIXME: model.cluster_centers_ / use model info or hide it + // options: [] + // } + } + + if (modelType == 'KMeans') { + infos = { + ...infos, + 'cluster_centers_': { + name: 'cluster_centers', + label: 'Cluster centers', + code: '${centers_allocate} = ${model}.cluster_centers_', + description: 'Coordinates of cluster centers.', + options: [ + { name: 'centers_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'cluster_centers' } + ] + } + } + } + + if (modelType == 'AgglomerativeClustering') { + infos = { + ...infos, + 'Dendrogram': { // FIXME: + name: 'dendrogram', + label: 'Dendrogram', + code: "# import\nfrom scipy.cluster.hierarchy import dendrogram, ward\n\nlinkage_array = ward(${dendro_data})\ndendrogram(linkage_array, p=3, truncate_mode='level', no_labels=True)\nplt.show()", + description: 'Draw a dendrogram', + options: [ + { name: 'dendro_data', label: 'Data', component: ['var_select'], var_type: ['DataFrame'] } + ] + } + } + } + break; + case 'Dimension Reduction': + if (modelType == 'LDA') { + infos = { + 'score': { + name: 'score', + label: 'Score', + code: '${score_allocate} = ${model}.score(${score_featureData}, ${score_targetData})', + description: 'Return the average log-likelihood of all samples.', + options: [ + { name: 'score_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'score_targetData', label: 'Target Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'y' }, + { name: 'score_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'scores' } + ] + } + } + break; + } + if (modelType == 'PCA') { + infos = { + 'explained_variance_ratio_': { + name: 'explained_variance_ratio_', + label: 'Explained variance ratio', + code: '${ratio_allocate} = ${model}.explained_variance_ratio_', + description: 'Percentage of variance explained by each of the selected components.', + options: [ + { name: 'ratio_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'ratio' } + ] + } + } + } + infos = { + ...infos, + 'score': { + name: 'score', + label: 'Score', + code: '${score_allocate} = ${model}.score(${score_featureData})', + description: 'Return the average log-likelihood of all samples.', + options: [ + { name: 'score_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], value: 'X' }, + { name: 'score_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', value: 'scores' } + ] + } + } + break; + } + return infos; + } + } return ModelInfo; From caac6b43e847ec15402e8cfcf815b26fe6b2726e Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Mon, 25 Apr 2022 02:19:37 +0900 Subject: [PATCH 12/21] Fix Fit/predict & Model info ML Apps --- js/m_ml/FitPredict.js | 3 +++ js/m_ml/ModelInfo.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/js/m_ml/FitPredict.js b/js/m_ml/FitPredict.js index ed0cb641..f1998f5e 100644 --- a/js/m_ml/FitPredict.js +++ b/js/m_ml/FitPredict.js @@ -155,6 +155,9 @@ define([ } reload() { + // reset option page + $(this.wrapSelector('.vp-ins-parameter-box')).html(''); + let modelTag = $(this.wrapSelector('#model')); let model = $(modelTag).val(); let modelType = $(modelTag).find('option:selected').data('type'); diff --git a/js/m_ml/ModelInfo.js b/js/m_ml/ModelInfo.js index 860e1548..8af9cd69 100644 --- a/js/m_ml/ModelInfo.js +++ b/js/m_ml/ModelInfo.js @@ -155,6 +155,9 @@ define([ } reload() { + // reset option page + $(this.wrapSelector('.vp-ins-parameter-box')).html(''); + let modelTag = $(this.wrapSelector('#model')); let model = $(modelTag).val(); let modelType = $(modelTag).find('option:selected').data('type'); From 2c5a6b60f7fa68621312e4767c265beb05c7fde3 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Mon, 25 Apr 2022 04:18:53 +0900 Subject: [PATCH 13/21] Fit/predict & Model info completion --- css/m_ml/fitPredict.css | 3 + html/m_ml/fitPredict.html | 23 ++++- html/m_ml/modelInfo.html | 23 ++++- js/com/com_Config.js | 28 +++--- js/m_ml/FitPredict.js | 196 ++++++++++++++++++++++---------------- js/m_ml/ModelInfo.js | 187 ++++++++++++++++++++++-------------- 6 files changed, 280 insertions(+), 180 deletions(-) diff --git a/css/m_ml/fitPredict.css b/css/m_ml/fitPredict.css index 871aec8d..b14cb17a 100644 --- a/css/m_ml/fitPredict.css +++ b/css/m_ml/fitPredict.css @@ -1,3 +1,6 @@ +.vp-model-select-box { + grid-column-gap: 5px; +} .vp-ins-select-title { font-weight: bold; color: var(--font-hightlight); diff --git a/html/m_ml/fitPredict.html b/html/m_ml/fitPredict.html index 7ac864ff..f80d7559 100644 --- a/html/m_ml/fitPredict.html +++ b/html/m_ml/fitPredict.html @@ -1,10 +1,23 @@
    -
    - - +
    +
    Model
    + +
    + +
    +
      + +
    +
    + +
    +
      + +
    +
    +
    diff --git a/html/m_ml/modelInfo.html b/html/m_ml/modelInfo.html index faf57c37..58abd6eb 100644 --- a/html/m_ml/modelInfo.html +++ b/html/m_ml/modelInfo.html @@ -1,10 +1,23 @@
    -
    - - +
    +
    Model
    + +
    + +
    +
      + +
    +
    + +
    +
      + +
    +
    +
    diff --git a/js/com/com_Config.js b/js/com/com_Config.js index 127ac6e4..70a38466 100644 --- a/js/com/com_Config.js +++ b/js/com/com_Config.js @@ -448,6 +448,10 @@ define([ return Config.ML_DATA_TYPES; } + getMLCategories() { + return Object.keys(Config.ML_DATA_DICT); + } + } //======================================================================== @@ -482,38 +486,38 @@ define([ * Data types using for searching model variables */ Config.ML_DATA_DICT = { + 'Data Preparation': [ + /** Encoding */ + 'OneHotEncoder', 'LabelEncoder', 'OrdinalEncoder', 'TargetEncoder', 'SMOTE', + /** Scaling */ + 'StandardScaler', 'RobustScaler', 'MinMaxScaler', 'Normalizer', 'FunctionTransformer', 'PolynomialFeatures', 'KBinsDiscretizer', + /** ETC */ + 'ColumnTransformer' + ], 'Regression': [ 'LinearRegression', 'Ridge', 'Lasso', 'ElasticNet', 'SVR', 'DecisionTreeRegressor', 'RandomForestRegressor', 'GradientBoostingRegressor', 'XGBRegressor', 'LGBMRegressor', 'CatBoostRegressor', ], 'Classification': [ 'LogisticRegression', 'BernoulliNB', 'MultinomialNB', 'GaussianNB', 'SVC', 'DecisionTreeClassifier', 'RandomForestClassifier', 'GradientBoostingClassifier', 'XGBClassifier', 'LGBMClassifier', 'CatBoostClassifier', ], - 'Auto ML': [ - 'AutoSklearnRegressor', 'AutoSklearnClassifier', 'TPOTRegressor', 'TPOTClassifier' - ], 'Clustering': [ 'KMeans', 'AgglomerativeClustering', 'GaussianMixture', 'DBSCAN', ], 'Dimension Reduction': [ 'PCA', 'LinearDiscriminantAnalysis', 'TruncatedSVD', 'NMF', 'TSNE' ], - 'Data Preparation': [ - /** Encoding */ - 'OneHotEncoder', 'LabelEncoder', 'OrdinalEncoder', 'TargetEncoder', 'SMOTE', - /** Scaling */ - 'StandardScaler', 'RobustScaler', 'MinMaxScaler', 'Normalizer', 'FunctionTransformer', 'PolynomialFeatures', 'KBinsDiscretizer', - /** ETC */ - 'ColumnTransformer' + 'Auto ML': [ + 'AutoSklearnRegressor', 'AutoSklearnClassifier', 'TPOTRegressor', 'TPOTClassifier' ] }; Config.ML_DATA_TYPES = [ + ...Config.ML_DATA_DICT['Data Preparation'], ...Config.ML_DATA_DICT['Regression'], ...Config.ML_DATA_DICT['Classification'], - ...Config.ML_DATA_DICT['Auto ML'], ...Config.ML_DATA_DICT['Clustering'], ...Config.ML_DATA_DICT['Dimension Reduction'], - ...Config.ML_DATA_DICT['Data Preparation'] + ...Config.ML_DATA_DICT['Auto ML'] ]; return Config; diff --git a/js/m_ml/FitPredict.js b/js/m_ml/FitPredict.js index f1998f5e..1366c913 100644 --- a/js/m_ml/FitPredict.js +++ b/js/m_ml/FitPredict.js @@ -35,33 +35,44 @@ define([ this.state = { // model selection + category: 'All', model: '', + modelType: '', method: '', action: {}, - config: {}, + optionConfig: {}, userOption: '', ...this.state } + // categories : Data Preparation / Regression / Classification / Clustering / Dimension Reduction / Auto ML + this.modelCategories = [ + 'All', + ...vpConfig.getMLCategories() + ] + this.modelConfig = ML_LIBRARIES; + + this.loaded = false; + } _bindEvent() { super._bindEvent(); /** Implement binding events */ var that = this; - - // change model - $(this.wrapSelector('#model')).on('change', function() { - that.reload(); - }); + + // click category + $(this.wrapSelector('.vp-ins-select-list.category .vp-ins-select-item')).on('click', function() { + let category = $(this).data('var-name'); - // click option - $(this.wrapSelector('.vp-ins-select-item')).on('click', function() { - let name = $(this).data('var-name'); - let type = $(this).data('var-type'); - - that.renderOptionPage(type, name); + that.state.category = category; + + $(that.wrapSelector('.vp-ins-select-list.category .vp-ins-select-item')).removeClass('selected'); + $(this).addClass('selected'); + + // load model list for this category + that.loadModelList(category); }); } @@ -73,28 +84,17 @@ define([ //================================================================ // Model selection //================================================================ - // set model list - let modelOptionTag = new com_String(); - vpKernel.getModelList().then(function(resultObj) { - let { result } = resultObj; - var modelList = JSON.parse(result); - modelList && modelList.forEach(model => { - let selectFlag = ''; - if (model.varName == that.state.model) { - selectFlag = 'selected'; - } - modelOptionTag.appendFormatLine('', - model.varName, model.varType, selectFlag, model.varName, model.varType); - }); - $(page).find('#model').html(modelOptionTag.toString()); - $(that.wrapSelector('#model')).html(modelOptionTag.toString()); - - if (!that.state.model || that.state.model == '') { - that.state.model = $(that.wrapSelector('#model')).val(); + // set model category list + let modelCategoryTag = new com_String(); + this.modelCategories.forEach(category => { + let selected = ''; + if (category == that.state.category) { + selected = 'selected'; } - - that.reload(); + modelCategoryTag.appendFormatLine('
  • {5}
  • ', + 'vp-ins-select-item', selected, category, 'category', category, category); }); + $(page).find('.vp-ins-select-list.category').html(modelCategoryTag.toString()); //================================================================ // Load state @@ -130,12 +130,12 @@ define([ } templateForOption(modelType) { - let config = this.modelConfig[modelType]; + let optionConfig = this.modelConfig[modelType]; let state = this.state; let optBox = new com_String(); // render tag - config.options.forEach(opt => { + optionConfig.options.forEach(opt => { optBox.appendFormatLine('' , opt.name, opt.name, com_util.optionToLabel(opt.name)); let content = com_generator.renderContent(this, opt.component[0], opt, state); @@ -151,24 +151,69 @@ define([ render() { super.render(); + this.loadModelList(this.state.category); + this.reload(); } + loadModelList(category='') { + // reset page + try { + $(this.wrapSelector('.vp-ins-search')).autocomplete("destroy"); + } catch { ; } + $(this.wrapSelector('.vp-ins-select-list.action')).html(''); + $(this.wrapSelector('.vp-ins-parameter-box')).html(''); + + if (category == 'All') { + category = ''; + } + // set model list + let that = this; + let modelOptionTag = new com_String(); + vpKernel.getModelList(category).then(function(resultObj) { + let { result } = resultObj; + var modelList = JSON.parse(result); + modelList && modelList.forEach(model => { + let selectFlag = ''; + if (model.varName == that.state.model) { + selectFlag = 'selected'; + } + modelOptionTag.appendFormatLine('
  • {5} ({6})
  • ', + 'vp-ins-select-item', selectFlag, model.varName, model.varType, model.varName, model.varName, model.varType); + }); + $(that.wrapSelector('.vp-ins-select-list.model')).html(modelOptionTag.toString()); + + // click model + $(that.wrapSelector('.vp-ins-select-list.model .vp-ins-select-item')).on('click', function() { + let model = $(this).data('var-name'); + let modelType = $(this).data('var-type'); + + that.state.model = model; + that.state.modelType = modelType; + + $(that.wrapSelector('.vp-ins-select-list.model .vp-ins-select-item')).removeClass('selected'); + $(this).addClass('selected'); + + that.reload(); + }); + }); + } + reload() { // reset option page + try { + $(this.wrapSelector('.vp-ins-search')).autocomplete("destroy"); + } catch { ; } + $(this.wrapSelector('.vp-ins-select-list.action')).html(''); $(this.wrapSelector('.vp-ins-parameter-box')).html(''); - let modelTag = $(this.wrapSelector('#model')); - let model = $(modelTag).val(); - let modelType = $(modelTag).find('option:selected').data('type'); + let model = this.state.model; + let modelType = this.state.modelType; let actions = this.getAction(modelType); - // let infos = this.getInfo(modelType); this.state.action = { ...actions }; - // this.state.info = { ...infos }; var actListTag = new com_String(); - // var infoListTag = new com_String(); Object.keys(actions).forEach(actKey => { let titleText = actions[actKey].description; @@ -178,17 +223,8 @@ define([ actListTag.appendFormatLine('
  • {4}
  • ', 'vp-ins-select-item', actKey, 'action', titleText, actions[actKey].label); }); - // Object.keys(infos).forEach(infoKey => { - // let titleText = infos[infoKey].description; - // if (infos[infoKey].name != infos[infoKey].label) { - // titleText = infos[infoKey].name + ': ' + titleText; - // } - // infoListTag.appendFormatLine('
  • {4}
  • ', - // 'vp-ins-select-item', infoKey, 'info', titleText, infos[infoKey].label); - // }); $(this.wrapSelector('.vp-ins-select-list.action')).html(actListTag.toString()); - // $(this.wrapSelector('.vp-ins-select-list.info')).html(infoListTag.toString()); let that = this; // action search suggest @@ -207,28 +243,18 @@ define([ return suggestInput.toTagString(); }); - // info search suggest - // suggestInput = new SuggestInput(); - // suggestInput.addClass('vp-input'); - // suggestInput.addClass('vp-ins-search'); - // suggestInput.setPlaceholder("Search info"); - // suggestInput.setSuggestList(function () { return Object.keys(infos); }); - // suggestInput.setSelectEvent(function (value, item) { - // $(this.wrapSelector()).val(value); - // $(that.wrapSelector('.vp-ins-type.info')).val(value); - - // $(that.wrapSelector('.vp-ins-select-item[data-var-name="' + value + '"]')).click(); - // }); - // $(that.wrapSelector('.vp-ins-search')).replaceWith(function () { - // return suggestInput.toTagString(); - // }); - // bind event - this._bindEvent(); + // click option + $(this.wrapSelector('.vp-ins-select-list.action .vp-ins-select-item')).on('click', function() { + let name = $(this).data('var-name'); + let type = $(this).data('var-type'); + + that.renderOptionPage(type, name); + }); // load once on initializing page if (this.loaded == false) { - let { modelEditorType, modelEditorName } = this.pageThis.state; + let { modelEditorType, modelEditorName } = this.state; if (modelEditorType != '' && modelEditorName != '') { // render option page for saved state that.renderOptionPage(modelEditorType, modelEditorName); @@ -245,10 +271,10 @@ define([ */ renderOptionPage(type, name) { if (this.state[type] != undefined && this.state[type][name] != undefined) { - let config = this.state[type][name]; + let optionConfig = this.state[type][name]; let optBox = new com_String(); // render tag - config && config.options && config.options.forEach(opt => { + optionConfig && optionConfig.options && optionConfig.options.forEach(opt => { let label = opt.name; if (opt.label != undefined) { label = opt.label; @@ -263,12 +289,12 @@ define([ // replace option box $(this.wrapSelector('.vp-ins-parameter-box')).html(optBox.toString()); - this.state.config = config; + this.state.optionConfig = optionConfig; // add selection - $(this.wrapSelector('.vp-ins-select-item')).removeClass('selected'); let typeClass = '.vp-ins-select-list.' + type; let nameClass = '.vp-ins-select-item[data-var-name="' + name + '"]'; + $(this.wrapSelector(typeClass + ' ' + '.vp-ins-select-item')).removeClass('selected'); $(this.wrapSelector(typeClass + ' ' + nameClass)).addClass('selected'); // set state $(this.wrapSelector('#modelEditorType')).val(type); @@ -283,21 +309,23 @@ define([ let code = new com_String(); let replaceDict = {'${model}': model}; - if (this.state.config.import != undefined) { - code.appendLine(this.state.config.import); + if (this.state.optionConfig.import != undefined) { + code.appendLine(this.state.optionConfig.import); code.appendLine(); } - let modelCode = com_generator.vp_codeGenerator(this.pageThis, this.state.config, this.pageThis.state); - Object.keys(replaceDict).forEach(key => { - modelCode = modelCode.replace(key, replaceDict[key]); - }); - code.append(modelCode); - - let allocateIdx = modelCode.indexOf(' = '); - if (allocateIdx >= 0) { - let allocateCode = modelCode.substr(0, allocateIdx); - code.appendLine(); - code.append(allocateCode); + let modelCode = com_generator.vp_codeGenerator(this, this.state.optionConfig, this.state); + if (modelCode) { + Object.keys(replaceDict).forEach(key => { + modelCode = modelCode.replace(key, replaceDict[key]); + }); + code.append(modelCode); + + let allocateIdx = modelCode.indexOf(' = '); + if (allocateIdx >= 0) { + let allocateCode = modelCode.substr(0, allocateIdx); + code.appendLine(); + code.append(allocateCode); + } } return code.toString(); diff --git a/js/m_ml/ModelInfo.js b/js/m_ml/ModelInfo.js index 8af9cd69..682973a5 100644 --- a/js/m_ml/ModelInfo.js +++ b/js/m_ml/ModelInfo.js @@ -35,33 +35,44 @@ define([ this.state = { // model selection + category: 'All', model: '', + modelType: '', method: '', info: {}, - config: {}, + optionConfig: {}, userOption: '', ...this.state } + // categories : Data Preparation / Regression / Classification / Clustering / Dimension Reduction / Auto ML + this.modelCategories = [ + 'All', + ...vpConfig.getMLCategories() + ] + this.modelConfig = ML_LIBRARIES; + + this.loaded = false; + } _bindEvent() { super._bindEvent(); /** Implement binding events */ var that = this; - - // change model - $(this.wrapSelector('#model')).on('change', function() { - that.reload(); - }); + + // click category + $(this.wrapSelector('.vp-ins-select-list.category .vp-ins-select-item')).on('click', function() { + let category = $(this).data('var-name'); - // click option - $(this.wrapSelector('.vp-ins-select-item')).on('click', function() { - let name = $(this).data('var-name'); - let type = $(this).data('var-type'); - - that.renderOptionPage(type, name); + that.state.category = category; + + $(that.wrapSelector('.vp-ins-select-list.category .vp-ins-select-item')).removeClass('selected'); + $(this).addClass('selected'); + + // load model list for this category + that.loadModelList(category); }); } @@ -73,28 +84,17 @@ define([ //================================================================ // Model selection //================================================================ - // set model list - let modelOptionTag = new com_String(); - vpKernel.getModelList().then(function(resultObj) { - let { result } = resultObj; - var modelList = JSON.parse(result); - modelList && modelList.forEach(model => { - let selectFlag = ''; - if (model.varName == that.state.model) { - selectFlag = 'selected'; - } - modelOptionTag.appendFormatLine('', - model.varName, model.varType, selectFlag, model.varName, model.varType); - }); - $(page).find('#model').html(modelOptionTag.toString()); - $(that.wrapSelector('#model')).html(modelOptionTag.toString()); - - if (!that.state.model || that.state.model == '') { - that.state.model = $(that.wrapSelector('#model')).val(); + // set model category list + let modelCategoryTag = new com_String(); + this.modelCategories.forEach(category => { + let selected = ''; + if (category == that.state.category) { + selected = 'selected'; } - - that.reload(); + modelCategoryTag.appendFormatLine('
  • {5}
  • ', + 'vp-ins-select-item', selected, category, 'category', category, category); }); + $(page).find('.vp-ins-select-list.category').html(modelCategoryTag.toString()); //================================================================ // Load state @@ -130,12 +130,12 @@ define([ } templateForOption(modelType) { - let config = this.modelConfig[modelType]; + let optionConfig = this.modelConfig[modelType]; let state = this.state; let optBox = new com_String(); // render tag - config.options.forEach(opt => { + optionConfig.options.forEach(opt => { optBox.appendFormatLine('' , opt.name, opt.name, com_util.optionToLabel(opt.name)); let content = com_generator.renderContent(this, opt.component[0], opt, state); @@ -151,16 +151,64 @@ define([ render() { super.render(); + this.loadModelList(this.state.category); + this.reload(); } + loadModelList(category='') { + // reset page + try { + $(this.wrapSelector('.vp-ins-search')).autocomplete("destroy"); + } catch { ; } + $(this.wrapSelector('.vp-ins-select-list.info')).html(''); + $(this.wrapSelector('.vp-ins-parameter-box')).html(''); + + if (category == 'All') { + category = ''; + } + // set model list + let that = this; + let modelOptionTag = new com_String(); + vpKernel.getModelList(category).then(function(resultObj) { + let { result } = resultObj; + var modelList = JSON.parse(result); + modelList && modelList.forEach(model => { + let selectFlag = ''; + if (model.varName == that.state.model) { + selectFlag = 'selected'; + } + modelOptionTag.appendFormatLine('
  • {5} ({6})
  • ', + 'vp-ins-select-item', selectFlag, model.varName, model.varType, model.varName, model.varName, model.varType); + }); + $(that.wrapSelector('.vp-ins-select-list.model')).html(modelOptionTag.toString()); + + // click model + $(that.wrapSelector('.vp-ins-select-list.model .vp-ins-select-item')).on('click', function() { + let model = $(this).data('var-name'); + let modelType = $(this).data('var-type'); + + that.state.model = model; + that.state.modelType = modelType; + + $(that.wrapSelector('.vp-ins-select-list.model .vp-ins-select-item')).removeClass('selected'); + $(this).addClass('selected'); + + that.reload(); + }); + }); + } + reload() { // reset option page + try { + $(this.wrapSelector('.vp-ins-search')).autocomplete("destroy"); + } catch { ; } + $(this.wrapSelector('.vp-ins-select-list.info')).html(''); $(this.wrapSelector('.vp-ins-parameter-box')).html(''); - let modelTag = $(this.wrapSelector('#model')); - let model = $(modelTag).val(); - let modelType = $(modelTag).find('option:selected').data('type'); + let model = this.state.model; + let modelType = this.state.modelType; let infos = this.getInfo(modelType); this.state.info = { ...infos }; @@ -176,31 +224,14 @@ define([ 'vp-ins-select-item', infoKey, 'info', titleText, infos[infoKey].label); }); - // $(this.wrapSelector('.vp-ins-select-list.action')).html(actListTag.toString()); $(this.wrapSelector('.vp-ins-select-list.info')).html(infoListTag.toString()); let that = this; - // action search suggest - // var suggestInput = new SuggestInput(); - // suggestInput.addClass('vp-input'); - // suggestInput.addClass('vp-ins-search'); - // suggestInput.setPlaceholder("Search Action"); - // suggestInput.setSuggestList(function () { return Object.keys(actions); }); - // suggestInput.setSelectEvent(function (value, item) { - // $(this.wrapSelector()).val(value); - // $(that.wrapSelector('.vp-ins-type.action')).val(value); - - // $(that.wrapSelector('.vp-ins-select-item[data-var-name="' + value + '"]')).click(); - // }); - // $(that.wrapSelector('.vp-ins-search')).replaceWith(function () { - // return suggestInput.toTagString(); - // }); - // info search suggest let suggestInput = new SuggestInput(); suggestInput.addClass('vp-input'); suggestInput.addClass('vp-ins-search'); - suggestInput.setPlaceholder("Search info"); + suggestInput.setPlaceholder("Search information"); suggestInput.setSuggestList(function () { return Object.keys(infos); }); suggestInput.setSelectEvent(function (value, item) { $(this.wrapSelector()).val(value); @@ -213,11 +244,17 @@ define([ }); // bind event - this._bindEvent(); + // click option + $(this.wrapSelector('.vp-ins-select-list.info .vp-ins-select-item')).on('click', function() { + let name = $(this).data('var-name'); + let type = $(this).data('var-type'); + + that.renderOptionPage(type, name); + }); // load once on initializing page if (this.loaded == false) { - let { modelEditorType, modelEditorName } = this.pageThis.state; + let { modelEditorType, modelEditorName } = this.state; if (modelEditorType != '' && modelEditorName != '') { // render option page for saved state that.renderOptionPage(modelEditorType, modelEditorName); @@ -234,10 +271,10 @@ define([ */ renderOptionPage(type, name) { if (this.state[type] != undefined && this.state[type][name] != undefined) { - let config = this.state[type][name]; + let optionConfig = this.state[type][name]; let optBox = new com_String(); // render tag - config && config.options && config.options.forEach(opt => { + optionConfig && optionConfig.options && optionConfig.options.forEach(opt => { let label = opt.name; if (opt.label != undefined) { label = opt.label; @@ -252,12 +289,12 @@ define([ // replace option box $(this.wrapSelector('.vp-ins-parameter-box')).html(optBox.toString()); - this.state.config = config; + this.state.optionConfig = optionConfig; // add selection - $(this.wrapSelector('.vp-ins-select-item')).removeClass('selected'); let typeClass = '.vp-ins-select-list.' + type; let nameClass = '.vp-ins-select-item[data-var-name="' + name + '"]'; + $(this.wrapSelector(typeClass + ' ' + '.vp-ins-select-item')).removeClass('selected'); $(this.wrapSelector(typeClass + ' ' + nameClass)).addClass('selected'); // set state $(this.wrapSelector('#modelEditorType')).val(type); @@ -272,21 +309,23 @@ define([ let code = new com_String(); let replaceDict = {'${model}': model}; - if (this.state.config.import != undefined) { - code.appendLine(this.state.config.import); + if (this.state.optionConfig.import != undefined) { + code.appendLine(this.state.optionConfig.import); code.appendLine(); } - let modelCode = com_generator.vp_codeGenerator(this.pageThis, this.state.config, this.pageThis.state); - Object.keys(replaceDict).forEach(key => { - modelCode = modelCode.replace(key, replaceDict[key]); - }); - code.append(modelCode); - - let allocateIdx = modelCode.indexOf(' = '); - if (allocateIdx >= 0) { - let allocateCode = modelCode.substr(0, allocateIdx); - code.appendLine(); - code.append(allocateCode); + let modelCode = com_generator.vp_codeGenerator(this, this.state.optionConfig, this.state); + if (modelCode) { + Object.keys(replaceDict).forEach(key => { + modelCode = modelCode.replace(key, replaceDict[key]); + }); + code.append(modelCode); + + let allocateIdx = modelCode.indexOf(' = '); + if (allocateIdx >= 0) { + let allocateCode = modelCode.substr(0, allocateIdx); + code.appendLine(); + code.append(allocateCode); + } } return code.toString(); From e68ae2792fe13fc369ca912e9a0132d79e0705f7 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Mon, 25 Apr 2022 05:22:43 +0900 Subject: [PATCH 14/21] Seaborn - refresh fixed --- html/m_visualize/seaborn.html | 5 ++ js/com/component/PopupComponent.js | 116 ++++++++++------------------- js/m_visualize/Seaborn.js | 79 +++++++++++++------- 3 files changed, 100 insertions(+), 100 deletions(-) diff --git a/html/m_visualize/seaborn.html b/html/m_visualize/seaborn.html index 0bfb8acb..cc95c721 100644 --- a/html/m_visualize/seaborn.html +++ b/html/m_visualize/seaborn.html @@ -43,6 +43,11 @@ + + + + Setting +
    diff --git a/js/com/component/PopupComponent.js b/js/com/component/PopupComponent.js index 4c5dbac7..d41bb2c0 100644 --- a/js/com/component/PopupComponent.js +++ b/js/com/component/PopupComponent.js @@ -297,45 +297,7 @@ define([ // save state values $(document).on('change', this.wrapSelector('.vp-state'), function() { - let id = $(this)[0].id; - let customKey = $(this).data('key'); - let tagName = $(this).prop('tagName'); // returns with UpperCase - let newValue = ''; - switch(tagName) { - case 'INPUT': - let inputType = $(this).prop('type'); - if (inputType == 'text' || inputType == 'number' || inputType == 'hidden') { - newValue = $(this).val(); - break; - } - if (inputType == 'checkbox') { - newValue = $(this).prop('checked'); - break; - } - break; - case 'TEXTAREA': - case 'SELECT': - default: - newValue = $(this).val(); - if (!newValue) { - newValue = ''; - } - break; - } - - // if custom key is available, use it - if (customKey && customKey != '') { - // allow custom key until level 2 - let customKeys = customKey.split('.'); - if (customKeys.length == 2) { - that.state[customKeys[0]][customKeys[1]] = newValue; - } else { - that.state[customKey] = newValue; - } - } else { - that.state[id] = newValue; - } - vpLog.display(VP_LOG_TYPE.DEVELOP, 'saved state : ' + id+ '/'+tagName+'/'+newValue); + that._saveSingleState($(this)[0]); }); // Click buttons @@ -423,7 +385,7 @@ define([ }); // click on data selector input filter - $(this.wrapSelector('.vp-data-selector ')).on('click', function(evt) { + $(this.wrapSelector('.vp-data-selector')).on('click', function(evt) { }); } @@ -618,46 +580,50 @@ define([ /** Implementation needed */ let that = this; $(this.wrapSelector('.vp-state')).each((idx, tag) => { - let id = tag.id; - let customKey = $(tag).data('key'); - let tagName = $(tag).prop('tagName'); // returns with UpperCase - let newValue = ''; - switch(tagName) { - case 'INPUT': - let inputType = $(tag).prop('type'); - if (inputType == 'text' || inputType == 'number' || inputType == 'hidden') { - newValue = $(tag).val(); - break; - } - if (inputType == 'checkbox') { - newValue = $(tag).prop('checked'); - break; - } - break; - case 'TEXTAREA': - case 'SELECT': - default: + that._saveSingleState(tag); + }); + vpLog.display(VP_LOG_TYPE.DEVELOP, 'savedState', that.state); + } + + _saveSingleState(tag) { + let id = tag.id; + let customKey = $(tag).data('key'); + let tagName = $(tag).prop('tagName'); // returns with UpperCase + let newValue = ''; + switch(tagName) { + case 'INPUT': + let inputType = $(tag).prop('type'); + if (inputType == 'text' || inputType == 'number' || inputType == 'hidden') { newValue = $(tag).val(); - if (!newValue) { - newValue = ''; - } break; - } - - // if custom key is available, use it - if (customKey && customKey != '') { - // allow custom key until level 2 - let customKeys = customKey.split('.'); - if (customKeys.length == 2) { - that.state[customKeys[0]][customKeys[1]] = newValue; - } else { - that.state[customKey] = newValue; } + if (inputType == 'checkbox') { + newValue = $(tag).prop('checked'); + break; + } + break; + case 'TEXTAREA': + case 'SELECT': + default: + newValue = $(tag).val(); + if (!newValue) { + newValue = ''; + } + break; + } + + // if custom key is available, use it + if (customKey && customKey != '') { + // allow custom key until level 2 + let customKeys = customKey.split('.'); + if (customKeys.length == 2) { + this.state[customKeys[0]][customKeys[1]] = newValue; } else { - that.state[id] = newValue; + this.state[customKey] = newValue; } - }); - vpLog.display(VP_LOG_TYPE.DEVELOP, 'savedState', that.state); + } else { + this.state[id] = newValue; + } } run(execute=true, addcell=true) { diff --git a/js/m_visualize/Seaborn.js b/js/m_visualize/Seaborn.js index d26f39c2..7e7238e6 100644 --- a/js/m_visualize/Seaborn.js +++ b/js/m_visualize/Seaborn.js @@ -44,6 +44,22 @@ define([ x: '', y: '', hue: '', + // info options + title: '', + x_label: '', + y_label: '', + useLegend: 'False', + legendPos: '', + // style options + useGrid: 'False', + useMarker: 'False', + markerStyle: '', + // setting options + x_limit_from: '', + x_limit_to: '', + y_limit_from: '', + y_limit_to: '', + // preview options useSampling: true, sampleCount: 30, autoRefresh: true, @@ -60,10 +76,10 @@ define([ } _bindEvent() { - super._bindEvent(); - let that = this; + super._bindEvent(); + // setting popup $(this.wrapSelector('#chartSetting')).on('click', function() { // show popup box @@ -147,18 +163,21 @@ define([ $(this.wrapSelector('#previewRefresh')).on('click', function() { that.loadPreview(); }); - $(this.wrapSelector('.vp-state')).on('change', function() { - if (that.state.autoRefresh && that.state.data != '') { - console.log('refresh'); + // auto refresh + $(document).off('change', this.wrapSelector('.vp-state')); + $(document).on('change', this.wrapSelector('.vp-state'), function(evt) { + that._saveSingleState($(this)[0]); + if (that.state.autoRefresh) { that.loadPreview(); } + evt.stopPropagation(); }); // set preview size $(this.wrapSelector('#previewSize')).on('change', function() { that.loadPreview(); }); - + } templateForBody() { @@ -438,6 +457,8 @@ define([ let convertedData = data; if (preview && data != '') { + // set font for KR + code.appendLine("plt.rc('font', family='Gulim')"); // FIXME: is it ok for non-Korean? // set figure size for preview chart let defaultWidth = 5; let defaultHeight = 4; @@ -454,25 +475,33 @@ define([ code.appendLine(chartCode); - // // Info - // if (title && title != '') { - // code.appendFormatLine("plt.title('{0}')", title); - // } - // if (x_label && x_label != '') { - // code.appendFormatLine("plt.xlabel('{0}')", x_label); - // } - // if (y_label && y_label != '') { - // code.appendFormatLine("plt.ylabel('{0}')", y_label); - // } - // if (x_limit_from != '' && x_limit_to != '') { - // code.appendFormatLine("plt.xlim(({0}, {1}))", x_limit_from, x_limit_to); - // } - // if (y_limit_from != '' && y_limit_to != '') { - // code.appendFormatLine("plt.ylim(({0}, {1}))", y_limit_from, y_limit_to); - // } - // if (useLegend && legendPos != '') { - // code.appendFormatLine("plt.legend(loc='{0}')", legendPos); - // } + // Info + if (title && title != '') { + code.appendFormatLine("plt.title('{0}')", title); + } + if (x_label && x_label != '') { + code.appendFormatLine("plt.xlabel('{0}')", x_label); + } + if (y_label && y_label != '') { + code.appendFormatLine("plt.ylabel('{0}')", y_label); + } + if (x_limit_from != '' && x_limit_to != '') { + code.appendFormatLine("plt.xlim(({0}, {1}))", x_limit_from, x_limit_to); + } + if (y_limit_from != '' && y_limit_to != '') { + code.appendFormatLine("plt.ylim(({0}, {1}))", y_limit_from, y_limit_to); + } + if (useLegend == 'True' && legendPos != '') { + code.appendFormatLine("plt.legend(loc='{0}')", legendPos); + } + if (useGrid == 'True') { + code.appendLine("plt.grid(True)"); + // TODO: grid types + // plt.grid(True, axis='x', color='red', alpha=0.5, linestyle='--') + } + if (useMarker == 'True') { + // TODO: marker to seaborn argument (ex. marker='+' / markers={'Lunch':'s', 'Dinner':'X'}) + } code.append('plt.show()'); From 57cbf9912f4e6871ea55b7c8719ea4a537d9eed8 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Mon, 25 Apr 2022 05:46:16 +0900 Subject: [PATCH 15/21] Popup - add maximize button --- css/popupComponent.css | 27 +++++++++++++++++++++- html/popupComponent.html | 2 ++ img/maximize.svg | 6 +++++ img/returnSize.svg | 10 ++++++++ js/com/component/PopupComponent.js | 37 ++++++++++++++++++++++++++++-- 5 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 img/maximize.svg create mode 100644 img/returnSize.svg diff --git a/css/popupComponent.css b/css/popupComponent.css index 3fff2c90..a9fec2bd 100644 --- a/css/popupComponent.css +++ b/css/popupComponent.css @@ -65,7 +65,19 @@ font-family: 'AppleSDGothicNeo'; color: var(--font-hightlight); } -.vp-popup-toggle { +.vp-popup-maximize { + position: absolute; + width: 15px; + height: 20px; + top: 4px; + right: 32px; + z-index: 3; + line-height: 20px; + text-align: center; + cursor: pointer; + color: var(--gray-color); +} +.vp-popup-return { position: absolute; width: 15px; height: 20px; @@ -76,6 +88,19 @@ text-align: center; cursor: pointer; color: var(--gray-color); + display: none; +} +.vp-popup-toggle { + position: absolute; + width: 15px; + height: 20px; + top: 4px; + right: 58px; + z-index: 3; + line-height: 20px; + text-align: center; + cursor: pointer; + color: var(--gray-color); } .vp-popup-close { position: absolute; diff --git a/html/popupComponent.html b/html/popupComponent.html index 531b89dd..bf4a7a63 100644 --- a/html/popupComponent.html +++ b/html/popupComponent.html @@ -46,6 +46,8 @@ + +
    diff --git a/img/maximize.svg b/img/maximize.svg new file mode 100644 index 00000000..6e8d51f4 --- /dev/null +++ b/img/maximize.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/img/returnSize.svg b/img/returnSize.svg new file mode 100644 index 00000000..4fcf77ed --- /dev/null +++ b/img/returnSize.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/js/com/component/PopupComponent.js b/js/com/component/PopupComponent.js index d41bb2c0..81a154da 100644 --- a/js/com/component/PopupComponent.js +++ b/js/com/component/PopupComponent.js @@ -251,12 +251,39 @@ define([ }); // Toggle operation (minimize) $(this.wrapSelector('.vp-popup-toggle')).on('click', function(evt) { - // that.toggle(); $(that.eventTarget).trigger({ type: 'close_option_page', component: that }); }); + // Maximize operation + $(this.wrapSelector('.vp-popup-maximize')).on('click', function(evt) { + // save position + that.config.position = $(that.wrapSelector()).position(); + // maximize popup + $(that.wrapSelector()).css({ + width: '100%', + height: '100%', + top: 0, + left: 0 + }); + // show / hide buttons + $(this).hide(); + $(that.wrapSelector('.vp-popup-return')).show(); + }); + // Return operation + $(this.wrapSelector('.vp-popup-return')).on('click', function(evt) { + // return size + $(that.wrapSelector()).css({ + width: that.config.size.width + 'px', + height: that.config.size.height + 'px', + top: that.config.position.top, + left: that.config.position.left + }); + // show / hide buttons + $(this).hide(); + $(that.wrapSelector('.vp-popup-maximize')).show(); + }); // Click install package $(this.wrapSelector('#popupInstall')).on('click', function() { @@ -420,8 +447,14 @@ define([ } _bindResizable() { + let that = this; $(this.wrapSelector()).resizable({ - handles: 'all' + handles: 'all', + start: function(evt, ui) { + // show / hide buttons + $(that.wrapSelector('.vp-popup-return')).hide(); + $(that.wrapSelector('.vp-popup-maximize')).show(); + } }); } From b3de85bcab3fe790bc50d7c9b661fc15ba8f0ab7 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Tue, 26 Apr 2022 11:17:43 +0900 Subject: [PATCH 16/21] Popup - change maximize icon --- css/popupComponent.css | 3 +++ html/popupComponent.html | 4 ++-- img/max_window.svg | 3 +++ img/maximize.svg | 6 ------ img/min_window.svg | 4 ++++ img/returnSize.svg | 10 ---------- js/com/component/PopupComponent.js | 5 +++++ 7 files changed, 17 insertions(+), 18 deletions(-) create mode 100644 img/max_window.svg delete mode 100644 img/maximize.svg create mode 100644 img/min_window.svg delete mode 100644 img/returnSize.svg diff --git a/css/popupComponent.css b/css/popupComponent.css index a9fec2bd..389cca3d 100644 --- a/css/popupComponent.css +++ b/css/popupComponent.css @@ -119,6 +119,9 @@ padding: 15px; overflow: auto; } +.vp-popup-content { + min-height: calc(100% - 30px); +} .vp-popup-footer { position: relative; height: 50px; diff --git a/html/popupComponent.html b/html/popupComponent.html index bf4a7a63..c606ebc1 100644 --- a/html/popupComponent.html +++ b/html/popupComponent.html @@ -46,8 +46,8 @@ - - + +
    diff --git a/img/max_window.svg b/img/max_window.svg new file mode 100644 index 00000000..b2aa45e8 --- /dev/null +++ b/img/max_window.svg @@ -0,0 +1,3 @@ + + + diff --git a/img/maximize.svg b/img/maximize.svg deleted file mode 100644 index 6e8d51f4..00000000 --- a/img/maximize.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/img/min_window.svg b/img/min_window.svg new file mode 100644 index 00000000..f7578948 --- /dev/null +++ b/img/min_window.svg @@ -0,0 +1,4 @@ + + + + diff --git a/img/returnSize.svg b/img/returnSize.svg deleted file mode 100644 index 4fcf77ed..00000000 --- a/img/returnSize.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/js/com/component/PopupComponent.js b/js/com/component/PopupComponent.js index 81a154da..548adffe 100644 --- a/js/com/component/PopupComponent.js +++ b/js/com/component/PopupComponent.js @@ -260,6 +260,11 @@ define([ $(this.wrapSelector('.vp-popup-maximize')).on('click', function(evt) { // save position that.config.position = $(that.wrapSelector()).position(); + // save size + that.config.size = { + width: $(that.wrapSelector()).width(), + height: $(that.wrapSelector()).height() + } // maximize popup $(that.wrapSelector()).css({ width: '100%', From 27ac3181268d25ead53a7e4e2f406119e21b5879 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Tue, 26 Apr 2022 11:18:00 +0900 Subject: [PATCH 17/21] Fix evaluation --- html/m_ml/evaluation.html | 28 +++++++++++----------- js/m_ml/evaluation.js | 50 ++++++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/html/m_ml/evaluation.html b/html/m_ml/evaluation.html index a3774fa0..73129fb6 100644 --- a/html/m_ml/evaluation.html +++ b/html/m_ml/evaluation.html @@ -23,7 +23,7 @@
    - +
    @@ -40,27 +40,27 @@
    - - -
    + + +
    -
    - -
    - - + +
    + +

    - - -
    - - + + +
    + +
    diff --git a/js/m_ml/evaluation.js b/js/m_ml/evaluation.js index 71efb29c..26ea1eed 100644 --- a/js/m_ml/evaluation.js +++ b/js/m_ml/evaluation.js @@ -77,24 +77,36 @@ define([ } else if (modelType == 'clf') { // Classification - model selection - if (that.checkToShowModel() == true) { - $(that.wrapSelector('.vp-ev-model')).show(); + if (that.checkToShowModel('roc-auc') == true) { + $(that.wrapSelector('.vp-ev-model.roc-auc')).prop('disabled', false); + } else { + $(that.wrapSelector('.vp-ev-model.roc-auc')).prop('disabled', true); } } else { // Clustering - + if (that.checkToShowModel('silhouette') == true) { + $(that.wrapSelector('.vp-ev-model.silhouette')).prop('disabled', false); + } else { + $(that.wrapSelector('.vp-ev-model.silhouette')).prop('disabled', true); + } + if (that.checkToShowModel('ari-nmi') == true) { + $(that.wrapSelector('.vp-ev-model.ari-nmi')).prop('disabled', false); + } else { + $(that.wrapSelector('.vp-ev-model.ari-nmi')).prop('disabled', true); + } } }); // open model selection show $(this.wrapSelector('.vp-eval-check')).on('change', function() { let checked = $(this).prop('checked'); + let type = $(this).data('type'); if (checked) { - $(that.wrapSelector('.vp-ev-model')).show(); + $(that.wrapSelector('.vp-ev-model.' + type)).prop('disabled', false); } else { - if (that.checkToShowModel() == false) { - $(that.wrapSelector('.vp-ev-model')).hide(); + if (that.checkToShowModel(type) == false) { + $(that.wrapSelector('.vp-ev-model.' + type)).prop('disabled', true); } } }); @@ -104,8 +116,8 @@ define([ * Check if anything checked available ( > 0) * @returns */ - checkToShowModel() { - let checked = $(this.wrapSelector('.vp-eval-check:checked')).length; + checkToShowModel(type) { + let checked = $(this.wrapSelector('.vp-eval-check[data-type="' + type + '"]:checked')).length; if (checked > 0) { return true; } @@ -140,13 +152,13 @@ define([ varSelector = new VarSelector2(this.wrapSelector(), ['DataFrame', 'list', 'str']); varSelector.setComponentID('featureData2'); - varSelector.addClass('vp-state vp-input'); + varSelector.addClass('vp-state vp-input vp-ev-model silhouette'); varSelector.setValue(this.state.featureData2); $(page).find('#featureData2').replaceWith(varSelector.toTagString()); varSelector = new VarSelector2(this.wrapSelector(), ['DataFrame', 'list', 'str']); varSelector.setComponentID('targetData2'); - varSelector.addClass('vp-state vp-input'); + varSelector.addClass('vp-state vp-input vp-ev-model ari-nmi'); varSelector.setValue(this.state.targetData2); $(page).find('#targetData2').replaceWith(varSelector.toTagString()); @@ -205,15 +217,21 @@ define([ $(page).find('.vp-upper-box.' + this.state.modelType).show(); if (this.state.modelType == 'rgs') { - + // Regression + } else if (this.state.modelType == 'clf') { - if (this.state.roc_curve == true || this.state.auc == true) { - $(page).find('.vp-ev-model').show(); - } else { - $(page).find('.vp-ev-model').hide(); + // Classification + if (this.state.roc_curve == false && this.state.auc == false) { + $(page).find('.vp-ev-model.roc-auc').prop('disabled', true); } } else { - $(page).find('.vp-ev-model').hide(); + // Clustering + if (this.state.silhouetteScore == false) { + $(page).find('.vp-ev-model.silhouette').prop('disabled', true); + } + if (this.state.ari == false && this.state.nmi == false) { + $(page).find('.vp-ev-model.ari-nmi').prop('disabled', true); + } } return page; From afd7316fc9662617f6c9eeacf30335b4f79930cf Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Tue, 26 Apr 2022 11:18:49 +0900 Subject: [PATCH 18/21] Set Seaborn's default style --- html/m_visualize/seaborn.html | 2 +- js/m_visualize/ChartSetting.js | 2 +- js/m_visualize/Seaborn.js | 93 +++++++++++++++++++--------------- 3 files changed, 53 insertions(+), 44 deletions(-) diff --git a/html/m_visualize/seaborn.html b/html/m_visualize/seaborn.html index cc95c721..820b4056 100644 --- a/html/m_visualize/seaborn.html +++ b/html/m_visualize/seaborn.html @@ -71,7 +71,7 @@
    - +
    diff --git a/js/m_visualize/ChartSetting.js b/js/m_visualize/ChartSetting.js index ebcda860..a41ef089 100644 --- a/js/m_visualize/ChartSetting.js +++ b/js/m_visualize/ChartSetting.js @@ -31,7 +31,7 @@ define([ this.state = { figureWidth: 12, figureHeight: 8, - styleSheet: '', + styleSheet: 'seaborn-darkgrid', fontName: '', fontSize: 10, ...this.state diff --git a/js/m_visualize/Seaborn.js b/js/m_visualize/Seaborn.js index 7e7238e6..fb6cbf5d 100644 --- a/js/m_visualize/Seaborn.js +++ b/js/m_visualize/Seaborn.js @@ -29,7 +29,7 @@ define([ super._init(); this.config.dataview = false; - this.config.size = { width: 900, height: 550 }; + this.config.size = { width: 1064, height: 550 }; this.state = { chartType: 'scatterplot', @@ -39,7 +39,7 @@ define([ figColumn: 0, shareX: false, shareY: false, - useData: true, // FIXME: use data default? + setXY: false, data: '', x: '', y: '', @@ -115,17 +115,17 @@ define([ }); // use data or not - $(this.wrapSelector('#useData')).on('change', function() { - let useData = $(this).prop('checked'); - if (useData) { - // use data + $(this.wrapSelector('#setXY')).on('change', function() { + let setXY = $(this).prop('checked'); + if (setXY == false) { + // set Data $(that.wrapSelector('#data')).prop('disabled', false); $(that.wrapSelector('#x')).closest('.vp-vs-box').replaceWith(''); $(that.wrapSelector('#y')).closest('.vp-vs-box').replaceWith(''); $(that.wrapSelector('#hue')).closest('.vp-vs-box').replaceWith(''); } else { - // not use data + // set X Y indivisually // disable data selection $(that.wrapSelector('#data')).prop('disabled', true); $(that.wrapSelector('#data')).val(''); @@ -154,11 +154,6 @@ define([ } }); - // bind column by dataframe - // $(this.wrapSelector('#data')).on('change', function() { - // com_generator.vp_bindColumnSource(that.wrapSelector(), this, ['x', 'y', 'hue'], 'select'); - // }); - // preview refresh $(this.wrapSelector('#previewRefresh')).on('click', function() { that.loadPreview(); @@ -216,6 +211,7 @@ define([ $(that.wrapSelector('#y')).prop('disabled', false); $(that.wrapSelector('#hue')).prop('disabled', false); + // bind column source using selected dataframe com_generator.vp_bindColumnSource(that.wrapSelector(), $(that.wrapSelector('#data')), ['x', 'y', 'hue'], 'select'); } else { $(that.wrapSelector('#x')).prop('disabled', true); @@ -295,10 +291,10 @@ define([ // set size $(this.wrapSelector('.vp-inner-popup-box')).css({ width: 400, height: 260}); - this.bindImportOptions(); + this.bindSettingBox(); } - bindImportOptions() { + bindSettingBox() { //==================================================================== // Stylesheet suggestinput //==================================================================== @@ -317,6 +313,7 @@ define([ suggestInput.setComponentID('styleSheet'); suggestInput.setSuggestList(function() { return varList; }); suggestInput.setPlaceholder('style name'); + suggestInput.setValue('seaborn-darkgrid'); // set default (seaborn-darkgrid) // suggestInput.setNormalFilter(false); $(stylesheetTag).replaceWith(function() { return suggestInput.toTagString(); @@ -449,62 +446,74 @@ define([ x_limit_from, x_limit_to, y_limit_from, y_limit_to, useSampling, sampleCount } = this.state; + + let indent = ''; let code = new com_String(); let config = this.chartConfig[chartType]; let state = JSON.parse(JSON.stringify(this.state)); - let chartCode = com_generator.vp_codeGenerator(this, config, state, (userOption != ''? ', ' + userOption : '')); - - let convertedData = data; - if (preview && data != '') { - // set font for KR - code.appendLine("plt.rc('font', family='Gulim')"); // FIXME: is it ok for non-Korean? - // set figure size for preview chart - let defaultWidth = 5; - let defaultHeight = 4; - let previewSize = parseInt($(this.wrapSelector('#previewSize')).val()); - code.appendFormatLine('plt.figure(figsize=({0}, {1}))', defaultWidth + previewSize, defaultHeight + previewSize); - if (useSampling) { - // data sampling code for preview - convertedData = data + '.sample(n=' + sampleCount + ', random_state=0)'; - } - } - - // replace pre-defined options - chartCode = chartCode.replace(data, convertedData); + let chartCode = new com_String(); - code.appendLine(chartCode); + let generatedCode = com_generator.vp_codeGenerator(this, config, state, (userOption != ''? ', ' + userOption : '')); // Info if (title && title != '') { - code.appendFormatLine("plt.title('{0}')", title); + chartCode.appendFormatLine("plt.title('{0}')", title); } if (x_label && x_label != '') { - code.appendFormatLine("plt.xlabel('{0}')", x_label); + chartCode.appendFormatLine("plt.xlabel('{0}')", x_label); } if (y_label && y_label != '') { - code.appendFormatLine("plt.ylabel('{0}')", y_label); + chartCode.appendFormatLine("plt.ylabel('{0}')", y_label); } if (x_limit_from != '' && x_limit_to != '') { - code.appendFormatLine("plt.xlim(({0}, {1}))", x_limit_from, x_limit_to); + chartCode.appendFormatLine("plt.xlim(({0}, {1}))", x_limit_from, x_limit_to); } if (y_limit_from != '' && y_limit_to != '') { - code.appendFormatLine("plt.ylim(({0}, {1}))", y_limit_from, y_limit_to); + chartCode.appendFormatLine("plt.ylim(({0}, {1}))", y_limit_from, y_limit_to); } if (useLegend == 'True' && legendPos != '') { - code.appendFormatLine("plt.legend(loc='{0}')", legendPos); + chartCode.appendFormatLine("plt.legend(loc='{0}')", legendPos); } if (useGrid == 'True') { - code.appendLine("plt.grid(True)"); + chartCode.appendLine("plt.grid(True)"); // TODO: grid types // plt.grid(True, axis='x', color='red', alpha=0.5, linestyle='--') } if (useMarker == 'True') { // TODO: marker to seaborn argument (ex. marker='+' / markers={'Lunch':'s', 'Dinner':'X'}) } + chartCode.append('plt.show()'); + let convertedData = data; + if (preview) { + // set indent + indent = ' '.repeat(4); + + // Ignore warning + code.appendLine('import warnings'); + code.appendLine('with warnings.catch_warnings():'); + code.appendFormatLine("{0}warnings.simplefilter('ignore')", indent); + + // set figure size for preview chart + let defaultWidth = 5; + let defaultHeight = 4; + let previewSize = parseInt($(this.wrapSelector('#previewSize')).val()); + code.appendFormatLine('{0}plt.figure(figsize=({1}, {2}))', indent, defaultWidth + previewSize, defaultHeight + previewSize); + if (useSampling) { + // data sampling code for preview + convertedData = data + '.sample(n=' + sampleCount + ', random_state=0)'; + // replace pre-defined options + generatedCode = generatedCode.replace(data, convertedData); + } - code.append('plt.show()'); + code.appendFormatLine("{0}{1}", indent, generatedCode); + code.appendFormatLine("{0}{1}", indent, chartCode.toString().replaceAll('\n', '\n' + indent)); + + } else { + code.appendLine(generatedCode); + code.appendLine(chartCode.toString()); + } return code.toString(); } From a9304f468b69aad6111285eb690571e3cbb72ca2 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Tue, 26 Apr 2022 11:44:10 +0900 Subject: [PATCH 19/21] Add empty option for x, y, hue --- html/m_visualize/seaborn.html | 3 --- js/com/com_generatorV2.js | 22 +++++++++++++++++++--- js/m_visualize/Seaborn.js | 6 +++--- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/html/m_visualize/seaborn.html b/html/m_visualize/seaborn.html index 820b4056..69600db5 100644 --- a/html/m_visualize/seaborn.html +++ b/html/m_visualize/seaborn.html @@ -120,9 +120,6 @@