diff --git a/data/libraries.json b/data/libraries.json index 8a890400..5cd63821 100644 --- a/data/libraries.json +++ b/data/libraries.json @@ -2154,8 +2154,7 @@ "tag" : "PANDAS PLOT,PANDAS", "path" : "visualpython - library - pandas - plot", "desc" : "Pandas Plot", - "file" : "m_library/m_pandas/plot", - "useAuto" : true + "file" : "m_library/m_pandas/PandasPlot" }, { "id" : "pdPkg_inputOutput", diff --git a/data/m_library/pandasLibrary.js b/data/m_library/pandasLibrary.js index 304aa9d8..d1f38172 100644 --- a/data/m_library/pandasLibrary.js +++ b/data/m_library/pandasLibrary.js @@ -207,7 +207,7 @@ define([ { name:'i0', type:'var', - label: 'Target Variable', + label: 'DataFrame', component: 'var_select', var_type: ['DataFrame', 'Series'] }, @@ -2936,7 +2936,7 @@ define([ { name: 'i0', type:'var', - label: 'Target Variable', + label: 'DataFrame', component: 'var_select', var_type: ['DataFrame', 'Series'] } @@ -2974,7 +2974,7 @@ define([ { name: 'i0', type:'var', - label: 'Target Variable', + label: 'DataFrame', component: 'var_select', var_type: ['DataFrame', 'Series'] }, @@ -5017,22 +5017,17 @@ define([ name: 'Plot', library: 'pandas', description: 'create chart', - code: '${o0} = ${i0}.plot(${v}${etc})\nplt.show()', + code: '${i0}.plot(${v}${etc})\nplt.show()', input: [ { name: 'i0', - type:'var', - label: 'Pandas Object', - component: 'var_select', - var_type: ['DataFrame', 'Series'] + type: 'var', + label: 'DataFrame', + var_type: ['DataFrame', 'Series'], + required: true } ], output: [ - { - name:'o0', - type:'var', - label:'Allocate to' - } ], variable: [ { @@ -5046,7 +5041,7 @@ define([ }, { name: 'title', - type: ['text', 'list'], + type: 'text', label: 'Chart Title' }, { @@ -5058,6 +5053,7 @@ define([ { name: 'fontsize', type: 'int', + component: 'input_number', label: 'Font Size' }, { @@ -5089,16 +5085,17 @@ define([ { name: 'rot', type: 'int', + component: 'input_number', label: 'X Label Rotation' }, { name: 'xlabel', - type: 'list', + type: 'text', label: 'X Label' }, { name: 'ylabel', - type: 'list', + type: 'text', label: 'Y Label' }, { @@ -5210,7 +5207,7 @@ define([ { name: 'i0', type: 'var', - label: 'Target Variable', + label: 'DataFrame', component: 'var_select', var_type: ['DataFrame', 'Series'] }, diff --git a/data/m_visualize/plotlyLibrary.js b/data/m_visualize/plotlyLibrary.js index b9372b97..81f07f4b 100644 --- a/data/m_visualize/plotlyLibrary.js +++ b/data/m_visualize/plotlyLibrary.js @@ -25,68 +25,74 @@ define([ //======================================================================== 'scatter': { name: 'Scatter Plot', - code: '${allocateTo} = px.scatter(${data}${x}${y}${etc})', + code: '${allocateTo} = px.scatter(${data}${x}${y}${color}${etc})', description: 'Draw a scatter plot with possibility of several semantic groupings.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, 'line': { name: 'Line Plot', - code: '${allocateTo} = px.line(${data}${x}${y}${etc})', + code: '${allocateTo} = px.line(${data}${x}${y}${color}${etc})', description: 'Draw a line plot with possibility of several semantic groupings.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, 'area': { name: 'Line Plot', - code: '${allocateTo} = px.area(${data}${x}${y}${etc})', + code: '${allocateTo} = px.area(${data}${x}${y}${color}${etc})', description: 'Draw a area plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, 'bar': { name: 'Bar Plot', - code: '${allocateTo} = px.bar(${data}${x}${y}${etc})', + code: '${allocateTo} = px.bar(${data}${x}${y}${color}${etc})', description: 'Draw a bar plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, 'funnel': { name: 'Funnel Plot', - code: '${allocateTo} = px.funnel(${data}${x}${y}${etc})', + code: '${allocateTo} = px.funnel(${data}${x}${y}${color}${etc})', description: 'Draw a funnel plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, 'timeline': { name: 'Timeline Plot', - code: '${allocateTo} = px.timeline(${data}${x_start}${x_end}${y}${etc})', + code: '${allocateTo} = px.timeline(${data}${x_start}${x_end}${y}${color}${etc})', description: 'Draw a timeline plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x_start', label: 'X start', component: ['col_select'], usePair: true }, { name: 'x_end', label: 'X end', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, @@ -95,23 +101,25 @@ define([ //======================================================================== 'pie': { name: 'Pie Plot', - code: '${allocateTo} = px.pie(${data}${values}${names}${etc})', + code: '${allocateTo} = px.pie(${data}${values}${names}${color}${etc})', description: 'Draw a pie plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'values', label: 'Values', component: ['col_select'], usePair: true }, { name: 'names', label: 'Names', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, 'sunburst': { name: 'Sunburst', - code: '${allocateTo} = px.sunburst(${data}${values}${names}${parents}${path}${etc})', + code: '${allocateTo} = px.sunburst(${data}${values}${names}${color}${parents}${path}${etc})', description: 'Draw a sunburst plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'values', label: 'Values', component: ['col_select'], usePair: true }, { name: 'names', label: 'Names', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'parents', label: 'Parents', component: ['col_select'], usePair: true }, { name: 'path', label: 'Path', component: ['data_select'], var_type: ['ndarray', 'list'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } @@ -119,12 +127,13 @@ define([ }, 'treemap': { name: 'Treemap', - code: '${allocateTo} = px.treemap(${data}${values}${names}${parents}${path}${etc})', + code: '${allocateTo} = px.treemap(${data}${values}${names}${color}${parents}${path}${etc})', description: 'Draw a treemap plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'values', label: 'Values', component: ['col_select'], usePair: true }, { name: 'names', label: 'Names', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'parents', label: 'Parents', component: ['col_select'], usePair: true }, { name: 'path', label: 'Path', component: ['data_select'], var_type: ['ndarray', 'list'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } @@ -132,12 +141,13 @@ define([ }, 'icicle': { name: 'Icicle', - code: '${allocateTo} = px.icicle(${data}${values}${names}${parents}${path}${etc})', + code: '${allocateTo} = px.icicle(${data}${values}${names}${color}${parents}${path}${etc})', description: 'Draw a icicle plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'values', label: 'Values', component: ['col_select'], usePair: true }, { name: 'names', label: 'Names', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'parents', label: 'Parents', component: ['col_select'], usePair: true }, { name: 'path', label: 'Path', component: ['data_select'], var_type: ['ndarray', 'list'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } @@ -145,12 +155,13 @@ define([ }, 'funnel_area': { name: 'Funnel area', - code: '${allocateTo} = px.funnel_area(${data}${values}${names}${etc})', + code: '${allocateTo} = px.funnel_area(${data}${values}${names}${color}${etc})', description: 'Draw a funnel area.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'values', label: 'Values', component: ['col_select'], usePair: true }, { name: 'names', label: 'Names', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, @@ -159,56 +170,61 @@ define([ //======================================================================== 'histogram': { name: 'Histogram', - code: '${allocateTo} = px.histogram(${data}${x}${y}${etc})', + code: '${allocateTo} = px.histogram(${data}${x}${y}${color}${etc})', description: 'Draw a histogram plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, 'box': { name: 'Box plot', - code: '${allocateTo} = px.box(${data}${x}${y}${etc})', + code: '${allocateTo} = px.box(${data}${x}${y}${color}${etc})', description: 'Draw a box plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, 'violin': { name: 'Violin plot', - code: '${allocateTo} = px.violin(${data}${x}${y}${etc})', + code: '${allocateTo} = px.violin(${data}${x}${y}${color}${etc})', description: 'Draw a violin plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, 'strip': { name: 'Strip plot', - code: '${allocateTo} = px.strip(${data}${x}${y}${etc})', + code: '${allocateTo} = px.strip(${data}${x}${y}${color}${etc})', description: 'Draw a strip plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, 'ecdf': { name: 'Ecdf plot', - code: '${allocateTo} = px.ecdf(${data}${x}${y}${etc})', + code: '${allocateTo} = px.ecdf(${data}${x}${y}${color}${etc})', description: 'Draw a ecdf plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, @@ -229,13 +245,14 @@ define([ }, 'density_contour': { name: 'Density contour', - code: '${allocateTo} = px.density_contour(${data}${x}${y}${z}${etc})', + code: '${allocateTo} = px.density_contour(${data}${x}${y}${z}${color}${etc})', description: 'Draw a density contour plot.', options: [ { name: 'data', component: ['var_select'], var_type: ['DataFrame', 'Series', 'list'] }, { name: 'x', component: ['col_select'], usePair: true }, { name: 'y', component: ['col_select'], usePair: true }, { name: 'z', component: ['col_select'], usePair: true }, + { name: 'color', component: ['col_select'], usePair: true }, { name: 'allocateTo', label: 'Allocate To', component: ['input'], usePair: true } ] }, diff --git a/html/m_apps/bind.html b/html/m_apps/bind.html index 8f5fb45d..38a75bd9 100644 --- a/html/m_apps/bind.html +++ b/html/m_apps/bind.html @@ -11,7 +11,7 @@
- +
diff --git a/html/m_apps/groupby.html b/html/m_apps/groupby.html index 4095dfbc..85cc3cfb 100644 --- a/html/m_apps/groupby.html +++ b/html/m_apps/groupby.html @@ -9,7 +9,7 @@
- +
+ +
+
+
+ + +
+
+ + + +
diff --git a/html/m_visualize/wordCloud.html b/html/m_visualize/wordCloud.html index c7f70c93..845d0e89 100644 --- a/html/m_visualize/wordCloud.html +++ b/html/m_visualize/wordCloud.html @@ -12,9 +12,9 @@
- +
- +
diff --git a/js/com/com_Config.js b/js/com/com_Config.js index aceeaa70..67acd1c7 100644 --- a/js/com/com_Config.js +++ b/js/com/com_Config.js @@ -14,8 +14,9 @@ define([ './com_Const', './com_util', - './com_interface' -], function(com_Const, com_util, com_interface) { + './com_interface', + 'text!vp_base/python/userCommand.py', +], function(com_Const, com_util, com_interface, userCommandFile) { 'use strict'; //======================================================================== // Define Inner Variable @@ -115,7 +116,56 @@ define([ this.defaultConfig = {}; this.metadataSettings = {}; + this.moduleDict = { + 'np': { + code: 'import numpy as np', + type: 'package' + }, + 'pd': { + code: 'import pandas as pd', + type: 'package' + }, + 'plt': { + code: 'import matplotlib.pyplot as plt\n%matplotlib inline', + type: 'package' + }, + 'sns': { + code: 'import seaborn as sns', + type: 'package' + }, + 'metrics': { + code: 'from sklearn import metrics', + type: 'package' + }, + 'ProfileReport': { + code: 'from pandas_profiling import ProfileReport', + type: 'package' + }, + 'px': { + code: 'import plotly.express as px', + type: 'package' + }, + 'WordCloud': { + code: 'from wordcloud import WordCloud', + type: 'package' + }, + 'fitz': { + code: 'import fitz', + type: 'package' + }, + 'nltk': { + code: "import nltk\nnltk.download('punkt')", + type: 'package' + }, + 'Counter': { + code: 'from collections import Counter', + type: 'package' + } + } + this._readDefaultConfig(); + this._readUserCommandList(); + } /** @@ -150,6 +200,46 @@ define([ $.extend(true, this.defaultConfig, this.metadataSettings); } + _readUserCommandList() { + let divider = '#'.repeat(6); + // get list of codes (ignore first 2 items) + let tmpList = userCommandFile.split(divider).slice(2); + + // match key-codes-description + // { 'func_name': { code: '', description: '' } } + let funcDict = {}; + let reg = /^def (.+)\(/; + let name = ''; + let code = ''; + let desc = ''; + let packageAlias = { + '_vp_np': 'np', + '_vp_pd': 'pd', + '_vp_plt': 'plt' + } + + for (let i = 0; i < tmpList.length; i += 2) { + desc = tmpList[i].trim(); + code = tmpList[i + 1].trim(); + let regResult = reg.exec(code); + if (regResult !== null) { + name = regResult[1]; + // convert code's package alias + Object.keys(packageAlias).forEach(key => { + let desAlias = packageAlias[key]; + code = code.replaceAll(key + '.', desAlias + '.'); + }); + // list up + funcDict[name] = { code: code, type: 'function', description: desc }; + } + } + + this.moduleDict = { + ...this.moduleDict, + ...funcDict + } + } + /** * Read kernel functions for using visualpython * - manually click restart menu (MenuFrame.js) @@ -161,7 +251,7 @@ define([ 'fileNaviCommand.py', 'pandasCommand.py', 'variableCommand.py', - 'userCommand.py' + // 'userCommand.py' ]; let promiseList = []; libraryList.forEach(libName => { @@ -464,6 +554,17 @@ define([ return Object.keys(Config.ML_DATA_DICT); } + getModuleCode(modName='') { + if (modName == '') { + return this.moduleDict; + } + try { + return this.moduleDict[modName]; + } catch { + return null; + } + } + } //======================================================================== diff --git a/js/com/com_Kernel.js b/js/com/com_Kernel.js index a4f62dfa..674b65e4 100644 --- a/js/com/com_Kernel.js +++ b/js/com/com_Kernel.js @@ -96,6 +96,23 @@ define([ }); } + /** + * Check if module/function is loaded already + * @param {*} moduleList + */ + checkModule(moduleList) { + var that = this; + return new Promise(function(resolve, reject) { + that.execute(com_util.formatString('_vp_print(_vp_check_module_loaded({0}))', JSON.stringify(moduleList))).then(function(resultObj) { + // resolve + resolve(resultObj); + }).catch(function(err) { + // reject + reject(err); + }) + }); + } + getDataList(dataTypeList=[], excludeList=[]) { // use function command to get variable list of selected data types var cmdSB = '_vp_print(_vp_get_variables_list(None))'; diff --git a/js/com/com_generator.js b/js/com/com_generator.js index 939bff4d..2b4a71b1 100644 --- a/js/com/com_generator.js +++ b/js/com/com_generator.js @@ -189,6 +189,9 @@ define([ } else { suggestInput.setPlaceholder('Type or Select value'); } + if (required === true) { + suggestInput.addAttribute('required', true); + } suggestInput.setSelectEvent(function(selectedValue) { // trigger change $(pageThis.wrapSelector('#' + obj.name)).val(selectedValue); @@ -202,9 +205,10 @@ define([ $(tag).attr({ 'type': 'text', 'id': obj.name, - 'class': 'vp-input vp-state' + 'class': 'vp-input vp-state', + 'required': required === true }); - vp_generateVarSuggestInput(divTag, obj); + vp_generateVarSuggestInput(divTag, obj, required); tblInput.appendChild(tag); break; case 'var_multi': @@ -237,11 +241,30 @@ define([ } $(tblInput).append(textarea); break; + case 'input_number': + var input = com_makeDom.renderInput({ + 'type': 'number', + 'class': 'vp-input vp-state', + 'id': obj.name, + 'placeholder': (obj.placeholder==undefined?'':obj.placeholder), + 'value': (obj.default==undefined?'':obj.default), + 'title': (obj.help==undefined?'':obj.help), + 'required': required === true + }); + // cell metadata test + if (getValue && obj.value != undefined) { + // set as saved value + input.attr({ + 'value': obj.value + }); + } + $(tblInput).append(input); + break; case 'table': // break; case 'file': // break; - // default : input_single + // default : input_single default: // FIXME: use makedom var input = com_makeDom.renderInput({ @@ -250,7 +273,8 @@ define([ 'id':obj.name, 'placeholder':(obj.placeholder==undefined?'':obj.placeholder), 'value':(obj.default==undefined?'':obj.default), - 'title':(obj.help==undefined?'':obj.help) + 'title':(obj.help==undefined?'':obj.help), + 'required': required === true }); // cell metadata test if (getValue && obj.value != undefined) { @@ -271,7 +295,7 @@ define([ * Generate suggest input * @param {object} obj */ - var vp_generateVarSuggestInput = function(divTag, obj) { + var vp_generateVarSuggestInput = function(divTag, obj, required=false) { var types = obj.var_type; var defaultValue = obj.value; @@ -302,6 +326,9 @@ define([ if (obj.placeholder != undefined) { suggestInput.setPlaceholder(obj.placeholder); } + if (required === true) { + suggestInput.addAttribute('required', true); + } suggestInput.setSelectEvent(function(selectedValue) { // trigger change $(divTag + ' #' + obj.name).val(selectedValue); @@ -422,6 +449,7 @@ define([ case 'table': case 'file': case 'option_suggest': + case 'input_number': default: var input = $(vp_wrapSelector(pageId, '#'+obj.name)).val(); // same as default diff --git a/js/com/com_generatorV2.js b/js/com/com_generatorV2.js index 56b5e8e5..41f60320 100644 --- a/js/com/com_generatorV2.js +++ b/js/com/com_generatorV2.js @@ -341,6 +341,9 @@ define([ if (obj.placeholder != undefined) { suggestInput.setPlaceholder(obj.placeholder); } + if (obj.required === true) { + suggestInput.addAttribute('required', true); + } suggestInput.setSelectEvent(function(selectedValue) { // trigger change $(pageThis.wrapSelector('#' + obj.name)).val(selectedValue); @@ -354,16 +357,18 @@ define([ id: obj.name, allowDataType: obj.var_type, placeholder: obj.placeholder || 'Select data', - value: value + value: value, + required: obj.required === true }); content = $(dataSelector.toTagString()); break; case 'var_select': // suggest input tag var tag = $('').attr({ - 'type': 'text', - 'id': obj.name, - 'class': 'vp-input vp-state' + type: 'text', + id: obj.name, + class: 'vp-input vp-state', + required: obj.required === true }); vp_generateVarSuggestInput(pageThis.wrapSelector(), obj); content = tag; @@ -398,12 +403,13 @@ define([ break; case 'input_number': var input = $('').attr({ - 'type':'number', - 'class':'vp-input vp-state', - 'id':obj.name, - 'placeholder':(obj.placeholder==undefined?'Input Number':obj.placeholder), - 'value':(obj.default==undefined?'':obj.default), - 'title':(obj.help==undefined?'':obj.help) + type: 'number', + class: 'vp-input vp-state', + id: obj.name, + placeholder: (obj.placeholder==undefined?'Input Number':obj.placeholder), + value: (obj.default==undefined?'':obj.default), + title: (obj.help==undefined?'':obj.help), + required: obj.required === true }); if (obj.step != undefined) { $(input).attr({ 'step': obj.step }); @@ -430,12 +436,13 @@ define([ // default : input_single default: var input = $('').attr({ - 'type':'text', - 'class':'vp-input input-single vp-state', - 'id':obj.name, - 'placeholder':(obj.placeholder==undefined?'Input Data':obj.placeholder), - 'value':(obj.default==undefined?'':obj.default), - 'title':(obj.help==undefined?'':obj.help) + type: 'text', + class: 'vp-input input-single vp-state', + id: obj.name, + placeholder: (obj.placeholder==undefined?'Input Data':obj.placeholder), + value: (obj.default==undefined?'':obj.default), + title: (obj.help==undefined?'':obj.help), + required: obj.required == true }); // cell metadata test if (value != undefined) { @@ -490,6 +497,9 @@ define([ suggestInput.setSuggestList(function() { return varList; }); suggestInput.setNormalFilter(false); suggestInput.setValue(defaultValue); + if (obj.required === true) { + suggestInput.addAttribute('required', true); + } if (obj.placeholder != undefined) { suggestInput.setPlaceholder(obj.placeholder); } diff --git a/js/com/component/DataSelector.js b/js/com/component/DataSelector.js index 01583205..0543b481 100644 --- a/js/com/component/DataSelector.js +++ b/js/com/component/DataSelector.js @@ -23,6 +23,8 @@ define([ // type: 'data', // pageThis: this, // id: 'targetId', + // classes: '', + // placeholder: '', // select: function(value, dtype) { // ; // } @@ -55,7 +57,8 @@ define([ allowDataType: ['DataFrame', 'Series', 'ndarray', 'list', 'dict'], // default allow data types // additional options classes: '', - placeholder: '', + placeholder: 'Select variable', + required: false, ...this.prop } @@ -338,7 +341,9 @@ define([ } return `
- +
`; diff --git a/js/com/component/FileNavigation.js b/js/com/component/FileNavigation.js index 0da7ef48..141842a2 100644 --- a/js/com/component/FileNavigation.js +++ b/js/com/component/FileNavigation.js @@ -84,6 +84,7 @@ define([ this.currentFileList = []; this.pathState = { + parentPath: '', currentPath: '', baseFolder: '', // root folder name relativeDir: '', // root folder name @@ -257,9 +258,24 @@ define([ let fileList = this.currentFileList; // clear body $(this.wrapSelector('.fileNavigationPage-body')).html(''); + // render file items let dirArr = []; let fileArr = []; + + // render upper folder + if (that.pathState.parentPath != '') { + let upperFolderTag = that.renderFileItem({ + name: '..', + type: 'dir', + path: that.pathState.parentPath, + size: '', + atime: '', + mtime: '' + }); + dirArr.push(upperFolderTag); + } + fileList && fileList.forEach((file, idx) => { if (idx == 0) return; let fileTag = that.renderFileItem(file); @@ -400,7 +416,7 @@ define([ let selectedExt = $(that.wrapSelector('#vp_fileNavigationExt')).val(); let fileExtIdx = fileName.lastIndexOf('.'); // if no extension, add it - if (fileExtIdx < 0 || fileName.substring(fileExtIdx + 1) != selectedExt) { + if (selectedExt != '' && (fileExtIdx < 0 || fileName.substring(fileExtIdx + 1) != selectedExt)) { fileName += '.' + selectedExt; } // no path, set it @@ -408,7 +424,7 @@ define([ filePath = './' + fileName; } fileExtIdx = filePath.lastIndexOf('.'); - if (fileExtIdx < 0 || filePath.substring(fileExtIdx + 1) != selectedExt) { + if (selectedExt != '' && (fileExtIdx < 0 || filePath.substring(fileExtIdx + 1) != selectedExt)) { filePath += '.' + selectedExt; } @@ -569,8 +585,15 @@ define([ }); } + vpLog.display(VP_LOG_TYPE.DEVELOP, 'FileNavigation - getFileList: ', filtered_varList); var { currentDirStr, currentRelativePathStr } = that.splitPathStrAndSetStack(dirObj, filtered_varList); + if (filtered_varList[0].current === filtered_varList[0].parent) { + // no parent + that.pathState.parentPath = ''; + } else { + that.pathState.parentPath = filtered_varList[0].parent; // parent path + } that.pathState.relativeDir = currentRelativePathStr; that.pathState.currentPath = currentDirStr; that.currentFileList = filtered_varList; diff --git a/js/com/component/InnerFuncViewer.js b/js/com/component/InnerFuncViewer.js index 3e20a94c..2bb97366 100644 --- a/js/com/component/InnerFuncViewer.js +++ b/js/com/component/InnerFuncViewer.js @@ -15,12 +15,11 @@ define([ 'text!vp_base/html/component/innerFuncViewer.html!strip', 'css!vp_base/css/component/innerFuncViewer.css', - 'text!vp_base/python/userCommand.py', 'vp_base/js/com/com_util', 'vp_base/js/com/com_String', 'vp_base/js/com/component/PopupComponent', 'vp_base/js/com/component/FileNavigation' -], function(ifHtml, ifCss, userCommandFile, com_util, com_String, PopupComponent, FileNavigation) { +], function(ifHtml, ifCss, com_util, com_String, PopupComponent, FileNavigation) { /** * InnerFuncViewer @@ -46,12 +45,6 @@ define([ // double click setter this.clicked = 0; - - this.packageAlias = { - '_vp_np': 'np', - '_vp_pd': 'pd', - '_vp_plt': 'plt' - } } _bindEvent() { @@ -181,33 +174,8 @@ define([ loadUserCommandList() { var that = this; - let divider = '#'.repeat(6); - // get list of codes (ignore first 2 items) - let tmpList = userCommandFile.split(divider).slice(2); - - // match key-codes-description - // { 'func_name': { code: '', description: '' } } - let funcDict = {}; - let reg = /^def (.+)\(/; - let name = ''; - let code = ''; - let desc = ''; - - for (let i = 0; i < tmpList.length; i += 2) { - desc = tmpList[i].trim(); - code = tmpList[i + 1].trim(); - let regResult = reg.exec(code); - if (regResult !== null) { - name = regResult[1]; - // convert code's package alias - Object.keys(that.packageAlias).forEach(key => { - let desAlias = that.packageAlias[key]; - code = code.replaceAll(key + '.', desAlias + '.'); - }); - // list up - funcDict[name] = { code: code, description: desc }; - } - } + let funcDict = vpConfig.getModuleCode(); + funcDict = Object.fromEntries(Object.entries(funcDict).filter(([key]) => funcDict[key].type == 'function')); // clear table except head $(this.wrapSelector('.vp-if-table')).html(''); diff --git a/js/com/component/InstanceEditor.js b/js/com/component/InstanceEditor.js index 95453488..f8081045 100644 --- a/js/com/component/InstanceEditor.js +++ b/js/com/component/InstanceEditor.js @@ -431,8 +431,10 @@ define([ } }).catch(function(resultObj) { let { result } = resultObj; - // show alert - com_util.renderAlertModal(result.ename + ': ' + result.evalue); + // show alert if this is visible + if (that.pageThis.isHidden() == false) { + com_util.renderAlertModal(result.ename + ': ' + result.evalue); + } // callback if (callback) { callback(''); diff --git a/js/com/component/LibraryComponent.js b/js/com/component/LibraryComponent.js index fce95286..75b364dc 100644 --- a/js/com/component/LibraryComponent.js +++ b/js/com/component/LibraryComponent.js @@ -44,6 +44,7 @@ define([ vpLog.display(VP_LOG_TYPE.ERROR, 'Cannot find package id from library: ' + this.packageId); return; } + this.config.checkModules = ['pd']; vpLog.display(VP_LOG_TYPE.DEVELOP, 'loading state', this.state); } diff --git a/js/com/component/NumpyComponent.js b/js/com/component/NumpyComponent.js index 09d50026..224e74fb 100644 --- a/js/com/component/NumpyComponent.js +++ b/js/com/component/NumpyComponent.js @@ -51,6 +51,7 @@ define([ vpLog.display(VP_LOG_TYPE.ERROR, 'Cannot find package id from library: ' + this.packageId); return; } + this.config.checkModules = ['np']; vpLog.display(VP_LOG_TYPE.DEVELOP, 'loading state', this.state); } diff --git a/js/com/component/PopupComponent.js b/js/com/component/PopupComponent.js index 58472afc..35e02fcc 100644 --- a/js/com/component/PopupComponent.js +++ b/js/com/component/PopupComponent.js @@ -66,7 +66,8 @@ define([ footer: true, position: { right: 10, top: 120 }, size: { width: 400, height: 550 }, - saveOnly: false + saveOnly: false, + checkModules: [] // module aliases or function names }; // check BoardFrame width and set initial position of popup @@ -302,7 +303,7 @@ define([ // add install codes var codes = that.generateInstallCode(); codes && codes.forEach(code => { - com_interface.insertCell('code', code); + com_interface.insertCell('code', code, true, that.getSigText()); }); }); @@ -376,8 +377,10 @@ define([ } break; case 'run': - that.save(); - that.run(); + let result = that.run(); + if (result) { + that.save(); + } break; case 'show-detail': $(that.wrapSelector('.vp-popup-run-detailbox')).show(); @@ -396,8 +399,10 @@ define([ that.save(); break; case 'add': - that.save(); - that.run(false); + let result = that.run(false); + if (result) { + that.save(); + } break; } }); @@ -679,11 +684,8 @@ define([ } } - run(execute=true, addcell=true) { - let code = this.generateCode(); - let mode = this.config.executeMode; + getSigText() { let sigText = ''; - // check if it's block if (this.getTaskType() == 'block') { let block = this.taskItem; sigText = block.sigText; @@ -698,15 +700,85 @@ define([ } } catch {} } - if (addcell) { - if (Array.isArray(code)) { - // insert cells if it's array of codes - com_interface.insertCells(mode, code, execute, sigText); + return sigText; + } + + /** + * Check if required option is filled + * @returns true if it's ok / false if there is empty required option + */ + checkRequiredOption() { + let requiredFilled = true; + let requiredTags = $(this.wrapSelector('input[required=true],input[required=required]')); + + if (requiredTags) { + for (let i = 0; i < requiredTags.length; i++) { + let thisTag = $(requiredTags[i]); + // if it's visible and empty, focus on it + if (thisTag.is(':visible') && thisTag.val() == '') { + $(requiredTags[i]).focus(); + requiredFilled = false; + break; + } + } + } + + return requiredFilled; + } + + checkAndRunModules(execute=true, background=false) { + let sigText = this.getSigText(); + + let checkModules = this.config.checkModules; + return new Promise(function(resolve, reject) { + if (checkModules.length > 0) { + vpKernel.checkModule(checkModules).then(function(resultObj) { + let { result } = resultObj; + let checkedList = JSON.parse(result); + let executeList = []; + checkedList && checkedList.forEach((mod, idx) => { + if (mod == false) { + let modInfo = vpConfig.getModuleCode(checkModules[idx]); + if (modInfo) { + executeList.push(modInfo.code); + } + } + }); + if (executeList && executeList.length > 0) { + com_interface.insertCell('code', executeList.join('\n'), execute, sigText); + } + resolve(executeList); + }); } else { - com_interface.insertCell(mode, code, execute, sigText); + resolve([]); } + }); + } + + run(execute=true, addcell=true) { + // check required + if (this.checkRequiredOption() === false) { + return false; } - return code; + + let mode = this.config.executeMode; + let sigText = this.getSigText(); + let code = this.generateCode(); + + vpLog.display(VP_LOG_TYPE.DEVELOP, sigText, mode, code); + + // check modules + this.checkAndRunModules(execute).then(function(executeList) { + if (addcell) { + if (Array.isArray(code)) { + // insert cells if it's array of codes + com_interface.insertCells(mode, code, execute, sigText); + } else { + com_interface.insertCell(mode, code, execute, sigText); + } + } + }); + return true; } /** diff --git a/js/com/component/SuggestInput.js b/js/com/component/SuggestInput.js index f09c474c..4cd434e4 100644 --- a/js/com/component/SuggestInput.js +++ b/js/com/component/SuggestInput.js @@ -14,7 +14,7 @@ define([ _init() { this._value = ""; - this._placeholder = ""; + this._placeholder = "Select variable"; this._compID = ""; this._additionalClass = ""; this._normalFilter = true; diff --git a/js/com/component/VarSelector2.js b/js/com/component/VarSelector2.js index e3f09fac..7789f9ee 100644 --- a/js/com/component/VarSelector2.js +++ b/js/com/component/VarSelector2.js @@ -14,7 +14,7 @@ define([ _init() { this._value = ""; - this._placeholder = ""; + this._placeholder = "Select variable"; this._compID = ""; this._additionalClass = ""; this._normalFilter = true; diff --git a/js/m_apps/Bind.js b/js/m_apps/Bind.js index 73050a5a..1ffc27f4 100644 --- a/js/m_apps/Bind.js +++ b/js/m_apps/Bind.js @@ -30,6 +30,7 @@ define([ super._init(); /** Write codes executed before rendering */ this.config.sizeLevel = 2; + this.config.checkModules = ['pd']; this.howList = [ { label: 'Inner', value: 'inner', desc: 'Inner join' }, @@ -407,6 +408,7 @@ define([ variableInput.setSuggestList(function () { return mappedList; }); variableInput.setNormalFilter(true); variableInput.setValue(defaultValue); + variableInput.addAttribute('required', true); $(this.wrapSelector('#' + id)).replaceWith(function() { return variableInput.toTagString(); }); diff --git a/js/m_apps/File.js b/js/m_apps/File.js index e4c7ec14..3ae75c07 100644 --- a/js/m_apps/File.js +++ b/js/m_apps/File.js @@ -35,6 +35,7 @@ define([ /** Write codes executed before rendering */ this.config.dataview = false; this.config.sizeLevel = 1; + this.config.checkModules = ['pd']; this.fileExtensions = { 'csv': 'csv', @@ -310,19 +311,19 @@ define([ ); } else if (selectedType == 'pickle') { $(prefix + '#path').parent().html( - com_util.formatString('
' + com_util.formatString('
' , 'vp-file-browser-button') ); } else { $(this.fileState[pageType]['fileResultState']['pathInputId']).parent().html( - com_util.formatString('
' + com_util.formatString('
' , 'i1' , 'vp-file-browser-button') ); } } else { $(this.fileState[pageType]['fileResultState']['pathInputId']).parent().html( - com_util.formatString('
' + com_util.formatString('
' , 'i0' , 'vp-file-browser-button') ); diff --git a/js/m_apps/Frame.js b/js/m_apps/Frame.js index d8ed8469..c60f97e9 100644 --- a/js/m_apps/Frame.js +++ b/js/m_apps/Frame.js @@ -30,6 +30,7 @@ define([ _init() { super._init(); this.config.sizeLevel = 3; + this.config.checkModules = ['pd']; // state this.state = { @@ -107,12 +108,13 @@ define([ that.state.originObj = origin; that.state.tempObj = '_vp'; that.state.returnObj = that.state.tempObj; - that.state.inplace = false; + if (that.state.inplace === true) { + that.state.returnObj = origin; + } that.initState(); // reset return obj - $(that.wrapSelector('#vp_feReturn')).val(that.state.tempObj); - $(that.wrapSelector('#inplace')).prop('checked', false); + $(that.wrapSelector('#vp_feReturn')).val(that.state.returnObj); // reset table $(that.wrapSelector('.' + VP_FE_TABLE)).replaceWith(function() { @@ -368,6 +370,12 @@ define([ case FRAME_EDIT_TYPE.AS_TYPE: that.openInputPopup(editType); break; + case FRAME_EDIT_TYPE.DROP_OUT: + that.config.checkModules = ['pd', 'np', 'vp_drop_outlier']; + that.checkAndRunModules(true).then(function() { + that.loadCode(that.getTypeCode(editType)); + }); + break; default: that.loadCode(that.getTypeCode(editType)); break; @@ -514,6 +522,7 @@ define([ super.render(); var { + originObj, returnObj, inplace, steps @@ -521,7 +530,7 @@ define([ this.loadVariableList(); - $(this.wrapSelector('#vp_feVariable')).val(this.state.originObj); + $(this.wrapSelector('#vp_feVariable')).val(originObj); $(this.wrapSelector('#vp_feReturn')).val(returnObj); @@ -543,6 +552,7 @@ define([ variableInput.addClass('vp-state'); variableInput.setPlaceholder('Select variable'); variableInput.setSuggestList(function () { return mappedList; }); + variableInput.addAttribute('required', true); variableInput.setSelectEvent(function (value) { $(this.wrapSelector()).val(value); $(this.wrapSelector()).trigger('change'); @@ -1218,6 +1228,7 @@ define([ switch (type) { case FRAME_EDIT_TYPE.INIT: code.appendFormat('{0} = {1}.copy()', tempObj, orgObj); + this.config.checkModules = ['pd']; break; case FRAME_EDIT_TYPE.DROP: code.appendFormat("{0}.drop([{1}], axis={2}, inplace=True)", tempObj, selectedName, axis); @@ -1485,7 +1496,11 @@ define([ that.loadInfo(); // add to stack if (codeStr !== '') { - that.state.steps.push(codeStr); + let newSteps = codeStr.split('\n'); + that.state.steps = [ + ...that.state.steps, + ...newSteps + ] var replacedCode = codeStr.replaceAll(that.state.tempObj, that.state.returnObj); that.setPreview(replacedCode); } @@ -1509,7 +1524,10 @@ define([ }).catch(function(resultObj) { let { result, type, msg } = resultObj; vpLog.display(VP_LOG_TYPE.ERROR, result.ename + ': ' + result.evalue, msg, code.toString()); - com_util.renderAlertModal(result.ename + ': ' + result.evalue); + if (that.isHidden() == false) { + // show alert modal only if this popup is visible + com_util.renderAlertModal(result.ename + ': ' + result.evalue); + } that.loading = false; }); diff --git a/js/m_apps/Groupby.js b/js/m_apps/Groupby.js index a6ed682b..8d786397 100644 --- a/js/m_apps/Groupby.js +++ b/js/m_apps/Groupby.js @@ -30,6 +30,7 @@ define([ super._init(); /** Write codes executed before rendering */ this.config.size = { width: 700, height: 550 }; + this.config.checkModules = ['pd']; this.periodList = [ { label: 'business day', value: 'B'}, @@ -395,6 +396,7 @@ define([ variableInput.setPlaceholder('Select variable'); variableInput.setSuggestList(function () { return mappedList; }); variableInput.setNormalFilter(true); + variableInput.addAttribute('required', true); variableInput.setValue(defaultValue); return variableInput.toTagString(); diff --git a/js/m_apps/Import.js b/js/m_apps/Import.js index e82e97e2..e023b307 100644 --- a/js/m_apps/Import.js +++ b/js/m_apps/Import.js @@ -46,6 +46,7 @@ define([ /** Write codes executed before rendering */ this.config.dataview = false; this.config.sizeLevel = 1; + this.config.checkModules = ['pd']; let savedData = vpConfig.getDataSimple('', 'vpimport'); // Reset abnormal data diff --git a/js/m_apps/Instance.js b/js/m_apps/Instance.js index 2cc8d65d..6c856282 100644 --- a/js/m_apps/Instance.js +++ b/js/m_apps/Instance.js @@ -32,6 +32,7 @@ define([ /** Write codes executed before rendering */ this.config.dataview = false; this.config.sizeLevel = 1; + this.config.checkModules = ['pd']; this.state = { vp_instanceVariable: '', diff --git a/js/m_apps/Markdown.js b/js/m_apps/Markdown.js index 405f64c7..b1344ed4 100644 --- a/js/m_apps/Markdown.js +++ b/js/m_apps/Markdown.js @@ -42,6 +42,7 @@ define([ this.config.codeview = false; this.config.dataview = false; this.config.sizeLevel = 1; + this.config.checkModules = ['pd']; this.state = { editor: '', @@ -126,7 +127,7 @@ define([ var page = new com_String(); page.appendLine('
'); page.appendLine(this.templateForToolbar()); - page.appendFormatLine('', 'vp_markdownEditor', this.state.editor); + page.appendFormatLine('', 'vp_markdownEditor', this.state.editor); page.appendFormatLine('
', "vp_attachEncodedDataArea"); page.appendLine('
'); page.appendFormatLine('
{1}
', 'vp_markdownPreview', this.state.preview); @@ -148,10 +149,6 @@ define([
`; } - templateForDataView() { - return ``; - } - render() { super.render(); @@ -213,14 +210,9 @@ define([ preview = ''; } that.state.preview = preview; - document.getElementById("vp_markdownPreview").innerHTML = preview; + $(that.wrapSelector("#vp_markdownPreview")).html(preview); MathJax.Hub.Queue(["Typeset", MathJax.Hub, "vp_markdownPreview"]); - - // re render block - if (block) { - block.render(); - } }); } @@ -306,6 +298,7 @@ define([ // repace selection cm.replaceSelection(adjustText); // TODO: set block code + cm.focus(); } /** diff --git a/js/m_apps/PDF.js b/js/m_apps/PDF.js index cac45e19..d5168aa0 100644 --- a/js/m_apps/PDF.js +++ b/js/m_apps/PDF.js @@ -73,6 +73,7 @@ nltk.download('punkt')`; this.config.importButton = true; this.config.dataview = false; this.config.size = { width: 500, height: 400 }; + this.config.checkModules = ['pd', 'fitz', 'nltk', 'vp_pdf_get_sentence']; this.state = { vp_pdfFile: '', diff --git a/js/m_apps/Profiling.js b/js/m_apps/Profiling.js index 76d26033..2052a433 100644 --- a/js/m_apps/Profiling.js +++ b/js/m_apps/Profiling.js @@ -46,6 +46,7 @@ define([ this.config.dataview = false; this.config.runButton = false; this.config.size = { width: 500, height: 500 }; + this.config.checkModules = ['ProfileReport']; this.selectedReport = ''; } @@ -62,6 +63,11 @@ define([ // click menu $(this.wrapSelector('.vp-pf-menu-item')).on('click', function() { + // check required filled + if (that.checkRequiredOption() === false) { + return ; + } + var type = $(this).data('type'); var df = $(that.wrapSelector('#vp_pfVariable')).val(); var saveas = $(that.wrapSelector('#vp_pfReturn')).val(); @@ -76,7 +82,7 @@ define([ code.append(saveas); break; } - com_interface.insertCell('code', code.toString(), 'Data Analysis > Profiling'); + com_interface.insertCell('code', code.toString(), true, 'Data Analysis > Profiling'); that.loadReportList(); }); } @@ -113,7 +119,7 @@ define([ } var code = new com_String(); code.appendFormat("{0}.to_file('{1}')", varName, path); - com_interface.insertCell('code', code.toString(), 'Data Analysis > Profiling'); + com_interface.insertCell('code', code.toString(), true, 'Data Analysis > Profiling'); that.selectedReport = ''; }); @@ -124,7 +130,7 @@ define([ default: return; } - com_interface.insertCell('code', code.toString(), 'Data Analysis > Profiling'); + com_interface.insertCell('code', code.toString(), true, 'Data Analysis > Profiling'); that.loadReportList(); }); } @@ -143,7 +149,8 @@ define([ generateInstallCode() { return [ - '!pip install pandas-profiling' + '!pip install pandas-profiling', + '!pip install ipywidgets' ]; } @@ -203,6 +210,7 @@ define([ variableInput.setPlaceholder('Select variable'); variableInput.setSuggestList(function () { return mappedList; }); variableInput.setNormalFilter(true); + variableInput.addAttribute('required', true); variableInput.setValue(beforeValue); return variableInput.toTagString(); diff --git a/js/m_apps/Reshape.js b/js/m_apps/Reshape.js index 882abeb4..803c1dd4 100644 --- a/js/m_apps/Reshape.js +++ b/js/m_apps/Reshape.js @@ -30,6 +30,7 @@ define([ super._init(); /** Write codes executed before rendering */ this.config.sizeLevel = 2; + this.config.checkModules = ['pd']; this.state = { variable: '', @@ -353,6 +354,7 @@ define([ variableInput.setSuggestList(function () { return mappedList; }); variableInput.setNormalFilter(true); variableInput.setValue(defaultValue); + variableInput.addAttribute('required', true); $(this.wrapSelector('#' + id)).replaceWith(function() { return variableInput.toTagString(); }); diff --git a/js/m_apps/Subset.js b/js/m_apps/Subset.js index 3a501590..263ea9a8 100644 --- a/js/m_apps/Subset.js +++ b/js/m_apps/Subset.js @@ -30,6 +30,7 @@ define([ _init() { super._init(); this.config.sizeLevel = 3; + this.config.checkModules = ['pd']; // use Run/Add cell this.useCell = true; @@ -756,6 +757,7 @@ define([ }); variableInput.setNormalFilter(true); variableInput.setValue(prevValue); + variableInput.addAttribute('required', true); $(that.wrapSelector('.' + VP_DS_PANDAS_OBJECT)).replaceWith(function() { return variableInput.toTagString(); }); diff --git a/js/m_library/m_pandas/PandasPlot.js b/js/m_library/m_pandas/PandasPlot.js new file mode 100644 index 00000000..9d246f29 --- /dev/null +++ b/js/m_library/m_pandas/PandasPlot.js @@ -0,0 +1,80 @@ +/* + * Project Name : Visual Python + * Description : GUI-based Python code generator + * File Name : PandasPlot.js + * Author : Black Logic + * Note : Library Component + * License : GNU GPLv3 with Visual Python special exception + * Date : 2022. 06. 22 + * Change Date : + */ + +//============================================================================ +// [CLASS] PandasPlot +//============================================================================ +define([ + 'vp_base/js/com/component/LibraryComponent', + 'vp_base/js/com/component/DataSelector', + 'vp_base/js/com/com_util' +], function(LibraryComponent, DataSelector, com_util) { + /** + * PandasPlot + */ + class PandasPlot extends LibraryComponent { + _init() { + super._init(); + + this.state = { + i0: '', + o0: '', + figWidth: '', + figHeight: '', + ...this.state + } + } + + _bindEventAfterRender() { + let that = this; + + $(this.wrapSelector('#figWidth')).on('blur', function() { + let width = $(this).val(); + let height = $(that.wrapSelector('#figHeight')).val(); + + if (width !== '' || height !== '') { + $(that.wrapSelector('#figsize')).val(com_util.formatString('({0},{1})', width, height)); + } else { + $(that.wrapSelector('#figsize')).val(''); + } + }); + $(this.wrapSelector('#figHeight')).on('blur', function() { + let width = $(that.wrapSelector('#figWidth')).val(); + let height = $(this).val(); + + if (width !== '' || height !== '') { + $(that.wrapSelector('#figsize')).val(com_util.formatString('({0},{1})', width, height)); + } else { + $(that.wrapSelector('#figsize')).val(''); + } + }); + } + + render() { + super.render(); + + // add data selector + let dataSelector = new DataSelector({ pageThis: this, id: 'i0', value: this.state.i0, required: true }); + $(this.wrapSelector('#i0')).replaceWith(dataSelector.toTagString()); + + // divide figure size option to width / height + let figSizeTemplate = ` + + ` + $(this.wrapSelector('#figsize')).hide(); + $(this.wrapSelector('#figsize')).parent().append(figSizeTemplate); + + this._bindEventAfterRender(); + } + } + + return PandasPlot; +}); \ No newline at end of file diff --git a/js/m_ml/AutoML.js b/js/m_ml/AutoML.js index 88d2b4d7..d0ae9795 100644 --- a/js/m_ml/AutoML.js +++ b/js/m_ml/AutoML.js @@ -88,7 +88,7 @@ define([ let config = that.modelConfig[that.state.modelType]; if (config && config.install != undefined) { // insert install code - com_interface.insertCell('code', config.install); + com_interface.insertCell('code', config.install, true, 'Machine Learning > AutoML'); } }); diff --git a/js/m_ml/Classification.js b/js/m_ml/Classification.js index b53d5257..36c8154c 100644 --- a/js/m_ml/Classification.js +++ b/js/m_ml/Classification.js @@ -88,7 +88,7 @@ define([ let config = that.modelConfig[that.state.modelType]; if (config && config.install != undefined) { // insert install code - com_interface.insertCell('code', config.install); + com_interface.insertCell('code', config.install, true, 'Machine Learning > Classification'); } }); diff --git a/js/m_ml/Clustering.js b/js/m_ml/Clustering.js index a636bfc5..90d5a437 100644 --- a/js/m_ml/Clustering.js +++ b/js/m_ml/Clustering.js @@ -91,7 +91,7 @@ define([ let config = that.modelConfig[that.state.modelType]; if (config && config.install != undefined) { // insert install code - com_interface.insertCell('code', config.install); + com_interface.insertCell('code', config.install, true, 'Machine Learning > Clustering'); } }); diff --git a/js/m_ml/DataPrep.js b/js/m_ml/DataPrep.js index 0cc70917..ce7faa12 100644 --- a/js/m_ml/DataPrep.js +++ b/js/m_ml/DataPrep.js @@ -106,7 +106,7 @@ define([ let config = that.modelConfig[that.state.modelType]; if (config && config.install != undefined) { // insert install code - com_interface.insertCell('code', config.install); + com_interface.insertCell('code', config.install, true, 'Machine Learning > DataPrep'); } }); diff --git a/js/m_ml/DimensionReduction.js b/js/m_ml/DimensionReduction.js index f11a82b9..0efbcdc3 100644 --- a/js/m_ml/DimensionReduction.js +++ b/js/m_ml/DimensionReduction.js @@ -87,7 +87,7 @@ define([ let config = that.modelConfig[that.state.modelType]; if (config && config.install != undefined) { // insert install code - com_interface.insertCell('code', config.install); + com_interface.insertCell('code', config.install, true, 'Machine Learning > DimensionReduction'); } }); diff --git a/js/m_ml/ModelInfo.js b/js/m_ml/ModelInfo.js index de115ae5..43482fbd 100644 --- a/js/m_ml/ModelInfo.js +++ b/js/m_ml/ModelInfo.js @@ -255,6 +255,14 @@ define([ $(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'); + + if (name == 'feature_importances') { + that.config.checkModules = ['pd', 'vp_create_feature_importances']; + } else if (name == 'plot_feature_importances') { + that.config.checkModules = ['pd', 'plt', 'vp_create_feature_importances', 'vp_plot_feature_importances']; + } else { + that.config.checkModules = ['pd']; + } that.renderOptionPage(type, name); @@ -425,11 +433,12 @@ define([ 'plot_feature_importances': { name: 'plot_feature_importances', label: 'Plot feature importances', - code: "vp_plot_feature_importances(${model}, ${fi_featureData}${sort})", + code: "vp_plot_feature_importances(${model}, ${fi_featureData}${sort}${top_count})", description: 'Draw feature_importances_', options: [ { name: 'fi_featureData', label: 'Feature Data', component: ['data_select'], var_type: ['DataFrame', 'Series', 'ndarray', 'list', 'dict'], value: 'X_train' }, - { name: 'sort', label: 'Sort data', component: ['bool_checkbox'], value: true, usePair: true } + { name: 'sort', label: 'Sort data', component: ['bool_checkbox'], value: true, usePair: true }, + { name: 'top_count', label: 'Top count', component: ['input_number'], min: 0, max: 5, usePair: true }, ] } } diff --git a/js/m_ml/Regression.js b/js/m_ml/Regression.js index 4b46ccd4..68cec34b 100644 --- a/js/m_ml/Regression.js +++ b/js/m_ml/Regression.js @@ -88,7 +88,7 @@ define([ let config = that.modelConfig[that.state.modelType]; if (config && config.install != undefined) { // insert install code - com_interface.insertCell('code', config.install); + com_interface.insertCell('code', config.install, true, 'Machine Learning > Regression'); } }); diff --git a/js/m_ml/dataSplit.js b/js/m_ml/dataSplit.js index 2051234c..06b8f50e 100644 --- a/js/m_ml/dataSplit.js +++ b/js/m_ml/dataSplit.js @@ -104,12 +104,12 @@ define([ $(page).find('#testSize').html(sizeOptions); let featureSelector = new DataSelector({ - pageThis: this, id: 'featureData', placeholder: 'Select feature data' + pageThis: this, id: 'featureData', placeholder: 'Select feature data', required: true }); $(page).find('#featureData').replaceWith(featureSelector.toTagString()); let targetSelector = new DataSelector({ - pageThis: this, id: 'targetData', placeholder: 'Select target data' + pageThis: this, id: 'targetData', placeholder: 'Select target data', required: true }); $(page).find('#targetData').replaceWith(targetSelector.toTagString()); diff --git a/js/m_ml/evaluation.js b/js/m_ml/evaluation.js index 80b9fc14..038a1933 100644 --- a/js/m_ml/evaluation.js +++ b/js/m_ml/evaluation.js @@ -30,6 +30,7 @@ define([ super._init(); this.config.importButton = true; this.config.dataview = false; + this.config.checkModules = ['metrics']; this.state = { modelType: 'rgs', @@ -56,7 +57,7 @@ define([ // import library $(this.wrapSelector('#vp_importLibrary')).on('click', function() { - com_interface.insertCell('code', 'from sklearn import metrics'); + com_interface.insertCell('code', 'from sklearn import metrics', true, 'Machine Learning > Evaluation'); }); // model type change @@ -130,28 +131,28 @@ define([ // data selector let predDataSelector = new DataSelector({ - pageThis: this, id: 'predictData', value: this.state.predictData + pageThis: this, id: 'predictData', value: this.state.predictData, required: true }); $(page).find('#predictData').replaceWith(predDataSelector.toTagString()); let targetDataSelector = new DataSelector({ - pageThis: this, id: 'targetData', value: this.state.targetData + pageThis: this, id: 'targetData', value: this.state.targetData, required: true }); $(page).find('#targetData').replaceWith(targetDataSelector.toTagString()); // Clustering - data selection let clusteredIdxSelector = new DataSelector({ - pageThis: this, id: 'clusteredIndex', value: this.state.clusteredIndex + pageThis: this, id: 'clusteredIndex', value: this.state.clusteredIndex, required: true }); $(page).find('#clusteredIndex').replaceWith(clusteredIdxSelector.toTagString()); let featureData2Selector = new DataSelector({ - pageThis: this, id: 'featureData2', value: this.state.featureData2, classes: 'vp-ev-model silhouette' + pageThis: this, id: 'featureData2', value: this.state.featureData2, classes: 'vp-ev-model silhouette', required: true }); $(page).find('#featureData2').replaceWith(featureData2Selector.toTagString()); let targetData2Selector = new DataSelector({ - pageThis: this, id: 'targetData2', value: this.state.targetData2, classes: 'vp-ev-model ari-nmi' + pageThis: this, id: 'targetData2', value: this.state.targetData2, classes: 'vp-ev-model ari-nmi', required: true }); $(page).find('#targetData2').replaceWith(targetData2Selector.toTagString()); diff --git a/js/m_visualize/Chart.js b/js/m_visualize/Chart.js index a8014bfc..0922e276 100644 --- a/js/m_visualize/Chart.js +++ b/js/m_visualize/Chart.js @@ -34,6 +34,7 @@ define([ /** Write codes executed before rendering */ this.config.dataview = false; this.config.sizeLevel = 2; + this.config.checkModules = ['plt']; this.setDefaultVariables(); this.state = { @@ -78,6 +79,9 @@ define([ if (obj.required != false) { // label = "* " + obj.label; $(that.wrapSelector('#' + obj.name)).closest('tr').find('th').addClass('vp-orange-text'); + $(that.wrapSelector('#' + obj.name)).attr({'required': true}); + } else { + $(that.wrapSelector('#' + obj.name)).attr({'required': false}); } // $(that.wrapSelector("label[for='" + obj.name + "']")).text(label); $(that.wrapSelector('#' + obj.name)).closest('tr').find('th').text(label); @@ -159,7 +163,7 @@ define([ } templateForBody() { - return chartHTml + return chartHTml; } loadState() { @@ -338,6 +342,9 @@ define([ if (obj.required != false) { // label = "* " + obj.label; $(this.wrapSelector('#' + obj.name)).closest('tr').find('th').addClass('vp-orange-text'); + $(this.wrapSelector('#' + obj.name)).attr({'required': true}); + } else { + $(this.wrapSelector('#' + obj.name)).attr({'required': false}); } // $(this.wrapSelector("label[for='" + obj.name + "']")).text(label); $(this.wrapSelector('#' + obj.name)).closest('tr').find('th').text(label); @@ -407,17 +414,17 @@ define([ var that = this; let xSelector = new DataSelector({ - pageThis: this, id: 'x', placeholder: 'Select data' + pageThis: this, id: 'x' }); $(this.wrapSelector('#x')).replaceWith(xSelector.toTagString()); let ySelector = new DataSelector({ - pageThis: this, id: 'y', placeholder: 'Select data' + pageThis: this, id: 'y', required: true }); $(this.wrapSelector('#y')).replaceWith(ySelector.toTagString()); let zSelector = new DataSelector({ - pageThis: this, id: 'z', placeholder: 'Select data' + pageThis: this, id: 'z' }); $(this.wrapSelector('#z')).replaceWith(zSelector.toTagString()); } @@ -639,8 +646,10 @@ define([ sbCode.append('plt.show()'); } catch (exmsg) { - // 에러 표시 - com_util.renderAlertModal(exmsg); + // show error on alert modal + if (this.isHidden() == false) { + com_util.renderAlertModal(exmsg); + } } return sbCode.toString(); diff --git a/js/m_visualize/ChartSetting.js b/js/m_visualize/ChartSetting.js index 29cea980..612bd2e8 100644 --- a/js/m_visualize/ChartSetting.js +++ b/js/m_visualize/ChartSetting.js @@ -75,7 +75,6 @@ define([ var code = new com_String(); // FIXME: convert it to kernelApi code.appendLine('import matplotlib.pyplot as plt'); - code.appendLine('%matplotlib inline'); code.appendLine('import json'); code.append(`print(json.dumps([{ 'label': s, 'value': s } for s in plt.style.available]))`); vpKernel.execute(code.toString()).then(function(resultObj) { @@ -125,8 +124,8 @@ define([ generateImportCode() { var code = new com_String(); code.appendLine('import matplotlib.pyplot as plt'); - code.appendLine('import seaborn as sns'); code.append('%matplotlib inline'); + code.appendLine('import seaborn as sns'); return [code.toString()]; } @@ -143,6 +142,7 @@ define([ let { figureWidth, figureHeight, styleSheet, fontName, fontSize } = this.state; code.appendLine('import matplotlib.pyplot as plt'); + code.appendLine('%matplotlib inline'); code.appendLine('import seaborn as sns'); code.appendFormatLine("plt.rc('figure', figsize=({0}, {1}))", figureWidth, figureHeight); if (styleSheet && styleSheet.length > 0) { diff --git a/js/m_visualize/Plotly.js b/js/m_visualize/Plotly.js index b272ca92..511e4ae0 100644 --- a/js/m_visualize/Plotly.js +++ b/js/m_visualize/Plotly.js @@ -25,23 +25,6 @@ define([ 'vp_base/data/m_visualize/plotlyLibrary', ], function(ptHTML, ptCss, com_String, com_generator, com_util, PopupComponent, SuggestInput, FileNavigation, DataSelector, PLOTLY_LIBRARIES) { - /** - * TODO: libraries.json add menu - * { - "id" : "visualize_plotly", - "type" : "function", - "level": 1, - "name" : "Plotly", - "tag" : "PLOTLY,VISUALIZATION,VISUALIZE", - "path" : "visualpython - visualization - plotly", - "desc" : "Plotly express", - "file" : "m_visualize/Plotly", - "apps" : { - "color": 5, - "icon": "apps/apps_visualize.svg" - } - }, - */ class Plotly extends PopupComponent { _init() { super._init(); @@ -50,12 +33,20 @@ define([ this.config.installButton = true; this.config.importButton = true; this.config.dataview = false; + this.config.checkModules = ['px']; this.state = { chartType: 'scatter', data: '', + x: '', y: '', z: '', + x_start: '', x_end: '', + values: '', names: '', parents: '', + color: '', + sort: '', userOption: '', title: '', + x_label: '', + y_label: '', userCode: '', autoRefresh: true, ...this.state @@ -118,28 +109,36 @@ define([ $(that.wrapSelector('#x')).closest('.pt-option').show(); $(that.wrapSelector('#y')).closest('.pt-option').show(); $(that.wrapSelector('#z')).closest('.pt-option').show(); + if (chartType === 'density_contour') { + $(that.wrapSelector('#color')).closest('.pt-option').show(); + } } else if (chartType === 'timeline') { $(that.wrapSelector('#x_start')).closest('.pt-option').show(); $(that.wrapSelector('#x_end')).closest('.pt-option').show(); $(that.wrapSelector('#y')).closest('.pt-option').show(); + $(that.wrapSelector('#color')).closest('.pt-option').show(); } else if (chartType === 'pie' || chartType === 'funnel_area') { // show values, names $(that.wrapSelector('#values')).closest('.pt-option').show(); $(that.wrapSelector('#names')).closest('.pt-option').show(); + $(that.wrapSelector('#color')).closest('.pt-option').show(); } else if (chartType === 'sunburst' || chartType === 'treemap' || chartType === 'icicle') { // show values, names, parents $(that.wrapSelector('#values')).closest('.pt-option').show(); $(that.wrapSelector('#names')).closest('.pt-option').show(); + $(that.wrapSelector('#color')).closest('.pt-option').show(); $(that.wrapSelector('#parents')).closest('.pt-option').show(); } else { // show x, y $(that.wrapSelector('#x')).closest('.pt-option').show(); $(that.wrapSelector('#y')).closest('.pt-option').show(); + $(that.wrapSelector('#color')).closest('.pt-option').show(); } + $(that.wrapSelector('#sort')).closest('.pt-option').show(); }); // use data or not @@ -157,6 +156,7 @@ define([ $(that.wrapSelector('#values')).closest('.vp-ds-box').replaceWith(''); $(that.wrapSelector('#names')).closest('.vp-ds-box').replaceWith(''); $(that.wrapSelector('#parents')).closest('.vp-ds-box').replaceWith(''); + $(that.wrapSelector('#color')).closest('.vp-ds-box').replaceWith(''); } else { // set X Y indivisually // disable data selection @@ -171,6 +171,7 @@ define([ that.state.values = ''; that.state.names = ''; that.state.parents = ''; + that.state.color = ''; let dataSelectorX = new DataSelector({ pageThis: that, id: 'x' }); $(that.wrapSelector('#x')).replaceWith(dataSelectorX.toTagString()); @@ -195,6 +196,9 @@ define([ let dataSelectorParents = new DataSelector({ pageThis: that, id: 'parents' }); $(that.wrapSelector('#parents')).replaceWith(dataSelectorParents.toTagString()); + + let dataSelectorColor = new DataSelector({ pageThis: that, id: 'color' }); + $(that.wrapSelector('#color')).replaceWith(dataSelectorColor.toTagString()); } }); @@ -255,10 +259,11 @@ define([ $(that.wrapSelector('#values')).prop('disabled', false); $(that.wrapSelector('#names')).prop('disabled', false); $(that.wrapSelector('#parents')).prop('disabled', false); + $(that.wrapSelector('#color')).prop('disabled', false); // bind column source using selected dataframe com_generator.vp_bindColumnSource(that, 'data', [ - 'x', 'x_start', 'x_end', 'y', 'z', 'values', 'names', 'parents' + 'x', 'x_start', 'x_end', 'y', 'z', 'values', 'names', 'parents', 'color' ], 'select', true, true); } else { $(that.wrapSelector('#x')).prop('disabled', true); @@ -269,6 +274,7 @@ define([ $(that.wrapSelector('#values')).prop('disabled', true); $(that.wrapSelector('#names')).prop('disabled', true); $(that.wrapSelector('#parents')).prop('disabled', true); + $(that.wrapSelector('#color')).prop('disabled', true); } }, @@ -284,10 +290,11 @@ define([ $(that.wrapSelector('#values')).prop('disabled', false); $(that.wrapSelector('#names')).prop('disabled', false); $(that.wrapSelector('#parents')).prop('disabled', false); + $(that.wrapSelector('#color')).prop('disabled', false); // bind column source using selected dataframe com_generator.vp_bindColumnSource(that, 'data', [ - 'x', 'x_start', 'x_end', 'y', 'z', 'values', 'names', 'parents' + 'x', 'x_start', 'x_end', 'y', 'z', 'values', 'names', 'parents', 'color' ], 'select', true, true); } else { $(that.wrapSelector('#x')).prop('disabled', true); @@ -298,6 +305,7 @@ define([ $(that.wrapSelector('#values')).prop('disabled', true); $(that.wrapSelector('#names')).prop('disabled', true); $(that.wrapSelector('#parents')).prop('disabled', true); + $(that.wrapSelector('#color')).prop('disabled', true); } } }); @@ -310,18 +318,23 @@ define([ $(page).find('#x').closest('.pt-option').show(); $(page).find('#y').closest('.pt-option').show(); $(page).find('#z').closest('.pt-option').show(); + if (this.state.chartType === 'density_contour') { + $(page).find('#color').closest('.pt-option').show(); + } } else if (this.state.chartType === 'timeline') { // show x_start, x_end, y $(page).find('#x_start').closest('.pt-option').show(); $(page).find('#x_end').closest('.pt-option').show(); $(page).find('#y').closest('.pt-option').show(); + $(page).find('#color').closest('.pt-option').show(); } else if (this.state.chartType === 'pie' || this.state.chartType === 'funnel_area') { // show values, names $(page).find('#values').closest('.pt-option').show(); $(page).find('#names').closest('.pt-option').show(); + $(page).find('#color').closest('.pt-option').show(); } else if (this.state.chartType === 'sunburst' || this.state.chartType === 'treemap' @@ -329,13 +342,16 @@ define([ // show values, names, parents $(page).find('#values').closest('.pt-option').show(); $(page).find('#names').closest('.pt-option').show(); + $(page).find('#color').closest('.pt-option').show(); $(page).find('#parents').closest('.pt-option').show(); } else { // show x, y $(page).find('#x').closest('.pt-option').show(); $(page).find('#y').closest('.pt-option').show(); + $(page).find('#color').closest('.pt-option').show(); } + $(page).find('#sort').closest('.pt-option').show(); //================================================================ // Load state @@ -391,7 +407,7 @@ define([ key: userCodeKey, selector: userCodeTarget, events: [{ - key: 'change', + key: 'blur', callback: function(instance, evt) { // save its state instance.save(); @@ -409,44 +425,46 @@ define([ let that = this; let code = this.generateCode(true); - // show variable information on clicking variable - vpKernel.execute(code).then(function(resultObj) { - let { result, type, msg } = resultObj; - if (msg.content.data) { - var textResult = msg.content.data["text/plain"]; - var htmlResult = msg.content.data["text/html"]; - var imgResult = msg.content.data["image/png"]; - - $(that.wrapSelector('#vp_ptPreview')).html(''); - if (htmlResult != undefined) { - // 1. HTML tag - $(that.wrapSelector('#vp_ptPreview')).append(htmlResult); - } else if (imgResult != undefined) { - // 2. Image data (base64) - var imgTag = ''; - $(that.wrapSelector('#vp_ptPreview')).append(imgTag); - } else if (textResult != undefined) { - // 3. Text data - var preTag = document.createElement('pre'); - $(preTag).text(textResult); - $(that.wrapSelector('#vp_ptPreview')).html(preTag); + that.checkAndRunModules(true).then(function() { + // show variable information on clicking variable + vpKernel.execute(code).then(function(resultObj) { + let { result, type, msg } = resultObj; + if (msg.content.data) { + var textResult = msg.content.data["text/plain"]; + var htmlResult = msg.content.data["text/html"]; + var imgResult = msg.content.data["image/png"]; + + $(that.wrapSelector('#vp_ptPreview')).html(''); + if (htmlResult != undefined) { + // 1. HTML tag + $(that.wrapSelector('#vp_ptPreview')).append(htmlResult); + } else if (imgResult != undefined) { + // 2. Image data (base64) + var imgTag = ''; + $(that.wrapSelector('#vp_ptPreview')).append(imgTag); + } else if (textResult != undefined) { + // 3. Text data + var preTag = document.createElement('pre'); + $(preTag).text(textResult); + $(that.wrapSelector('#vp_ptPreview')).html(preTag); + } + } else { + var errorContent = ''; + if (msg.content.ename) { + errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue); + } + $(that.wrapSelector('#vp_ptPreview')).html(errorContent); + vpLog.display(VP_LOG_TYPE.ERROR, msg.content.ename, msg.content.evalue, msg.content); } - } else { + }).catch(function(resultObj) { + let { msg } = resultObj; var errorContent = ''; if (msg.content.ename) { errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue); } $(that.wrapSelector('#vp_ptPreview')).html(errorContent); vpLog.display(VP_LOG_TYPE.ERROR, msg.content.ename, msg.content.evalue, msg.content); - } - }).catch(function(resultObj) { - let { msg } = resultObj; - var errorContent = ''; - if (msg.content.ename) { - errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue); - } - $(that.wrapSelector('#vp_ptPreview')).html(errorContent); - vpLog.display(VP_LOG_TYPE.ERROR, msg.content.ename, msg.content.evalue, msg.content); + }); }); } @@ -470,9 +488,9 @@ define([ */ let { chartType, - data, x, y, setXY, + data, x, y, color, setXY, sort, userOption, userCode, - title + title, x_label, y_label } = this.state; let code = new com_String(); let config = this.chartConfig[chartType]; @@ -482,6 +500,42 @@ define([ if (title != '') { etcOptionCode.push(com_util.formatString("title='{0}'", title)); } + let labelOptions = []; + // add x_label + if (x_label != '') { + if (setXY === true) { + // replace 'x' to x_label + labelOptions.push(com_util.formatString("'x': '{0}'", x_label)); + } else { + // if x is selected + if (x != '') { + // replace x column name to x_label + labelOptions.push(com_util.formatString("{0}: '{1}'", x, x_label)); + } else { + // replace 'index' to x_label + labelOptions.push(com_util.formatString("'index': '{0}'", x_label)); + } + } + } + // add y_label + if (y_label != '') { + if (setXY === true) { + // replace 'y' to y_label + labelOptions.push(com_util.formatString("'y': '{0}'", y_label)); + } else { + // if y is selected + if (y != '') { + // replace y column name to y_label + labelOptions.push(com_util.formatString("{0}: '{1}'", y, y_label)); + } else { + // replace 'index' to y_label + labelOptions.push(com_util.formatString("'index': '{0}'", y_label)); + } + } + } + if (labelOptions.length > 0) { + etcOptionCode.push(com_util.formatString("labels={ {0} }", labelOptions.join(', '))); + } // add user option if (userOption != '') { etcOptionCode.push(userOption); @@ -499,12 +553,17 @@ define([ let generatedCode = com_generator.vp_codeGenerator(this, config, this.state , etcOptionCode.length > 0? ', ' + etcOptionCode.join(', '): ''); - code.append(generatedCode); + code.appendFormatLine("fig = {0}", generatedCode); + + // sort code + if (sort && sort != '') { + code.appendFormatLine("fig.update_xaxes(categoryorder='{0}')", sort); + } if (userCode && userCode != '') { - code.appendLine(); - code.append(userCode); + code.appendLine(userCode); } + code.append('fig.show()'); return code.toString(); } diff --git a/js/m_visualize/Seaborn.js b/js/m_visualize/Seaborn.js index e7ccc2e4..eb92cf84 100644 --- a/js/m_visualize/Seaborn.js +++ b/js/m_visualize/Seaborn.js @@ -31,6 +31,7 @@ define([ this.config.dataview = false; this.config.size = { width: 1064, height: 550 }; + this.config.checkModules = ['plt', 'sns']; this.state = { chartType: 'scatterplot', @@ -49,6 +50,11 @@ define([ kde: '', stat: '', showValues: false, + showValuesPrecision: '', + sortBy: 'y', + sortType: '', + sortHue: '', + sortHueText: false, // axes options x_limit_from: '', x_limit_to: '', @@ -193,8 +199,10 @@ define([ $(that.wrapSelector('#stat')).closest('.sb-option').show(); } else if (chartType == 'barplot') { $(that.wrapSelector('#showValues')).closest('.sb-option').show(); + $(that.wrapSelector('#sortBy')).closest('.sb-option').show(); } else if (chartType == 'countplot') { $(that.wrapSelector('#showValues')).closest('.sb-option').show(); + $(that.wrapSelector('#sortBy')).closest('.sb-option').show(); } }); @@ -208,6 +216,11 @@ define([ $(that.wrapSelector('#x')).closest('.vp-ds-box').replaceWith(''); $(that.wrapSelector('#y')).closest('.vp-ds-box').replaceWith(''); $(that.wrapSelector('#hue')).closest('.vp-ds-box').replaceWith(''); + + // FIXME: hide sort values for barplot/countplot (as temporary) + if (that.state.chartType == 'barplot' || that.state.chartType == 'countplot') { + $(that.wrapSelector('#sortBy')).closest('.sb-option').show(); + } } else { // set X Y indivisually // disable data selection @@ -226,10 +239,55 @@ define([ let dataSelectorHue = new DataSelector({ pageThis: that, id: 'hue' }); $(that.wrapSelector('#hue')).replaceWith(dataSelectorHue.toTagString()); + + // FIXME: hide sort values for barplot/countplot (as temporary) + if (that.state.chartType == 'barplot' || that.state.chartType == 'countplot') { + $(that.wrapSelector('#sortBy')).closest('.sb-option').hide(); + } } }); + // change hue + $(document).off('change', this.wrapSelector('.vp-state')); + $(document).on('change', this.wrapSelector('#hue'), function() { + let { chartType, data } = that.state; + let hue = $(this).val(); + if (chartType == 'barplot' || chartType == 'countplot') { + let colDtype = $(that.wrapSelector('#hue')).find('option:selected').data('type'); + console.log(data, hue); + // get result and load column list + vpKernel.getColumnCategory(data, hue).then(function (resultObj) { + let { result } = resultObj; + try { + var category = JSON.parse(result); + if (category && category.length > 0 && colDtype == 'object') { + // if it's categorical column and its dtype is object, check 'Text' as default + $(that.wrapSelector('#sortHueText')).prop('checked', true); + } else { + $(that.wrapSelector('#sortHueText')).prop('checked', false); + } + $(that.wrapSelector('#sortHue')).replaceWith(that.templateForHueCondition(category, colDtype)); + } catch { + $(that.wrapSelector('#sortHueText')).prop('checked', false); + $(that.wrapSelector('#sortHue')).replaceWith(that.templateForHueCondition([], colDtype)); + } + }); + } + }); + + // show values or not + $(this.wrapSelector('#showValues')).on('change', function() { + let checked = $(this).prop('checked'); + if (checked === true) { + that.config.checkModules = ['plt', 'sns', 'np', 'vp_seaborn_show_values']; + $(that.wrapSelector('#showValuesPrecision')).attr('disabled', false); + } else { + that.config.checkModules = ['plt', 'sns']; + $(that.wrapSelector('#showValuesPrecision')).attr('disabled', true); + } + }); + // use color or not $(this.wrapSelector('#useColor')).on('change', function() { that.state.useColor = $(this).prop('checked'); @@ -526,7 +584,7 @@ define([ key: userCodeKey, selector: userCodeTarget, events: [{ - key: 'change', + key: 'blur', callback: function(instance, evt) { // save its state instance.save(); @@ -540,6 +598,25 @@ define([ this.loadPreview(); } + templateForHueCondition(category, dtype='object') { + var vpCondSuggest = new SuggestInput(); + vpCondSuggest.setComponentID('sortHue'); + vpCondSuggest.addClass('vp-input vp-state'); + vpCondSuggest.setPlaceholder('Type hue condition'); + if (category && category.length > 0) { + vpCondSuggest.setPlaceholder((dtype=='object'?'Categorical':dtype) + " dtype"); + vpCondSuggest.setSuggestList(function () { return category; }); + vpCondSuggest.setSelectEvent(function (value) { + $(this.wrapSelector()).val(value); + $(this.wrapSelector()).trigger('change'); + }); + vpCondSuggest.setNormalFilter(false); + } else { + vpCondSuggest.setPlaceholder(dtype==''?'Value':(dtype + " dtype")); + } + return vpCondSuggest.toTagString(); + } + bindSettingBox() { //==================================================================== // Stylesheet suggestinput @@ -625,44 +702,46 @@ define([ let that = this; let code = this.generateCode(true); - // show variable information on clicking variable - vpKernel.execute(code).then(function(resultObj) { - let { result, type, msg } = resultObj; - if (msg.content.data) { - var textResult = msg.content.data["text/plain"]; - var htmlResult = msg.content.data["text/html"]; - var imgResult = msg.content.data["image/png"]; - - $(that.wrapSelector('#chartPreview')).html(''); - if (htmlResult != undefined) { - // 1. HTML tag - $(that.wrapSelector('#chartPreview')).append(htmlResult); - } else if (imgResult != undefined) { - // 2. Image data (base64) - var imgTag = ''; - $(that.wrapSelector('#chartPreview')).append(imgTag); - } else if (textResult != undefined) { - // 3. Text data - var preTag = document.createElement('pre'); - $(preTag).text(textResult); - $(that.wrapSelector('#chartPreview')).html(preTag); + that.checkAndRunModules(true).then(function() { + // show variable information on clicking variable + vpKernel.execute(code).then(function(resultObj) { + let { result, type, msg } = resultObj; + if (msg.content.data) { + var textResult = msg.content.data["text/plain"]; + var htmlResult = msg.content.data["text/html"]; + var imgResult = msg.content.data["image/png"]; + + $(that.wrapSelector('#chartPreview')).html(''); + if (htmlResult != undefined) { + // 1. HTML tag + $(that.wrapSelector('#chartPreview')).append(htmlResult); + } else if (imgResult != undefined) { + // 2. Image data (base64) + var imgTag = ''; + $(that.wrapSelector('#chartPreview')).append(imgTag); + } else if (textResult != undefined) { + // 3. Text data + var preTag = document.createElement('pre'); + $(preTag).text(textResult); + $(that.wrapSelector('#chartPreview')).html(preTag); + } + } else { + var errorContent = ''; + if (msg.content.ename) { + errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue); + } + $(that.wrapSelector('#chartPreview')).html(errorContent); + vpLog.display(VP_LOG_TYPE.ERROR, msg.content.ename, msg.content.evalue, msg.content); } - } else { + }).catch(function(resultObj) { + let { msg } = resultObj; var errorContent = ''; if (msg.content.ename) { errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue); } $(that.wrapSelector('#chartPreview')).html(errorContent); vpLog.display(VP_LOG_TYPE.ERROR, msg.content.ename, msg.content.evalue, msg.content); - } - }).catch(function(resultObj) { - let { msg } = resultObj; - var errorContent = ''; - if (msg.content.ename) { - errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue); - } - $(that.wrapSelector('#chartPreview')).html(errorContent); - vpLog.display(VP_LOG_TYPE.ERROR, msg.content.ename, msg.content.evalue, msg.content); + }); }); } @@ -705,7 +784,10 @@ define([ generateCode(preview=false) { let { - chartType, data, x, y, setXY, hue, kde, stat, showValues, userOption='', + chartType, data, x, y, setXY, hue, kde, stat, + showValues, showValuesPrecision, + sortType, sortBy, sortHue, sortHueText, + userOption='', x_limit_from, x_limit_to, y_limit_from, y_limit_to, xticks, xticks_label, xticks_rotate, removeXticks, yticks, yticks_label, yticks_rotate, removeYticks, @@ -720,25 +802,6 @@ define([ let config = this.chartConfig[chartType]; let state = JSON.parse(JSON.stringify(this.state)); - let chartCode = new com_String(); - - let etcOptionCode = [] - if (useColor == true && color != '') { - etcOptionCode.push(com_util.formatString("color='{0}'", color)); - } - if (markerStyle != '') { - // TODO: marker to seaborn argument (ex. marker='+' / markers={'Lunch':'s', 'Dinner':'X'}) - etcOptionCode.push(com_util.formatString("marker='{0}'", markerStyle)); - } - if (showValues === true && chartType === 'barplot') { - etcOptionCode.push('ci=None'); - } - - // add user option - if (userOption != '') { - etcOptionCode.push(userOption); - } - if (preview && useSampling) { // data sampling code for preview // convertedData = data + '.sample(n=' + sampleCount + ', random_state=0)'; @@ -760,7 +823,71 @@ define([ state.data = com_util.formatString('_vp_sample({0}, {1})', data, sampleCount); } } - } + } + + let chartCode = new com_String(); + + let etcOptionCode = [] + if (useColor == true && color != '') { + etcOptionCode.push(com_util.formatString("color='{0}'", color)); + } + if (markerStyle != '') { + // TODO: marker to seaborn argument (ex. marker='+' / markers={'Lunch':'s', 'Dinner':'X'}) + etcOptionCode.push(com_util.formatString("marker='{0}'", markerStyle)); + } + if (showValues === true && chartType === 'barplot') { + etcOptionCode.push('ci=None'); + } + if (sortType != '') { + let sortCode = ''; + let sortTypeStr = (sortType === 'descending'? 'ascending=False': 'ascending=True'); + let sortX = state.x; + let sortY = state.y; + if (sortBy === 'x') { + sortX = state.y; + sortY = state.x; + } + if (chartType === 'barplot') { + if (setXY === true) { + // TODO: sort on setXY + // if (hue !== '' && sortHue !== '') { + // sortCode = com_util.formatString("{}.groupby({})[{}].mean().sort_values({}).index") + // } else { + // sortCode = com_util.formatString("pd.concat([{0},{1}], axis=1).groupby({2})[{3}].mean().sort_values({4}).index" + // , sortX, sortY, sortX) + // } + } else { + if (hue !== '' && sortHue !== '') { + sortCode = com_util.formatString("{0}[{1}[{2}]=={3}].groupby({4})[{5}].mean().sort_values({6}).index" + , state.data, state.data, state.hue, com_util.convertToStr(sortHue, sortHueText), sortX, sortY, sortTypeStr); + } else { + sortCode = com_util.formatString("{0}.groupby({1})[{2}].mean().sort_values({3}).index", state.data, sortX, sortY, sortTypeStr); + } + } + } else if (chartType === 'countplot') { + let countVar = sortX === ''? sortY: sortX; + if (setXY === true) { + // TODO: sort on setXY + ; + } else { + if (hue !== '' && sortHue !== '') { + sortCode = com_util.formatString("{0}[{1}[{2}]=={3}][{4}].value_counts({5}).index" + , state.data, state.data, state.hue, com_util.convertToStr(sortHue, sortHueText), countVar, sortTypeStr); + } else { + sortCode = com_util.formatString("{0}[{1}].value_counts({2}).index", state.data, countVar, sortTypeStr); + } + } + } + + if (sortCode != '') { + etcOptionCode.push('order=' + sortCode); + } + } + + // add user option + if (userOption != '') { + etcOptionCode.push(userOption); + } let generatedCode = com_generator.vp_codeGenerator(this, config, state , etcOptionCode.length > 0? ', ' + etcOptionCode.join(', '): ''); @@ -851,7 +978,11 @@ define([ if (showValues && showValues === true) { code.appendLine('ax = ' + generatedCode); - code.appendLine("vp_seaborn_show_values(ax)"); + code.append("vp_seaborn_show_values(ax"); + if (showValuesPrecision !== '') { + code.appendFormat(", precision={0}", showValuesPrecision); + } + code.appendLine(")"); } else { code.appendLine(generatedCode); } diff --git a/js/m_visualize/WordCloud.js b/js/m_visualize/WordCloud.js index 564b0759..fad25ba2 100644 --- a/js/m_visualize/WordCloud.js +++ b/js/m_visualize/WordCloud.js @@ -31,6 +31,7 @@ define([ this.config.installButton = true; this.config.importButton = true; this.config.dataview = false; + this.config.checkModules = ['Counter', 'plt', 'Wordcloud']; this.state = { data: '', @@ -179,6 +180,7 @@ define([ type: 'data', pageThis: this, id: 'data', + required: true, select: function() { that.state.useFile = false; $(that.wrapSelector('#useFile')).prop('checked', false); @@ -237,44 +239,46 @@ define([ let that = this; let code = this.generateCode(true); - // show variable information on clicking variable - vpKernel.execute(code).then(function(resultObj) { - let { result, type, msg } = resultObj; - if (msg.content.data) { - var textResult = msg.content.data["text/plain"]; - var htmlResult = msg.content.data["text/html"]; - var imgResult = msg.content.data["image/png"]; - - $(that.wrapSelector('#vp_wcPreview')).html(''); - if (htmlResult != undefined) { - // 1. HTML tag - $(that.wrapSelector('#vp_wcPreview')).append(htmlResult); - } else if (imgResult != undefined) { - // 2. Image data (base64) - var imgTag = ''; - $(that.wrapSelector('#vp_wcPreview')).append(imgTag); - } else if (textResult != undefined) { - // 3. Text data - var preTag = document.createElement('pre'); - $(preTag).text(textResult); - $(that.wrapSelector('#vp_wcPreview')).html(preTag); + that.checkAndRunModules(true).then(function() { + // show variable information on clicking variable + vpKernel.execute(code).then(function(resultObj) { + let { result, type, msg } = resultObj; + if (msg.content.data) { + var textResult = msg.content.data["text/plain"]; + var htmlResult = msg.content.data["text/html"]; + var imgResult = msg.content.data["image/png"]; + + $(that.wrapSelector('#vp_wcPreview')).html(''); + if (htmlResult != undefined) { + // 1. HTML tag + $(that.wrapSelector('#vp_wcPreview')).append(htmlResult); + } else if (imgResult != undefined) { + // 2. Image data (base64) + var imgTag = ''; + $(that.wrapSelector('#vp_wcPreview')).append(imgTag); + } else if (textResult != undefined) { + // 3. Text data + var preTag = document.createElement('pre'); + $(preTag).text(textResult); + $(that.wrapSelector('#vp_wcPreview')).html(preTag); + } + } else { + var errorContent = ''; + if (msg.content.ename) { + errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue); + } + $(that.wrapSelector('#vp_wcPreview')).html(errorContent); + vpLog.display(VP_LOG_TYPE.ERROR, msg.content.ename, msg.content.evalue, msg.content); } - } else { + }).catch(function(resultObj) { + let { msg } = resultObj; var errorContent = ''; if (msg.content.ename) { errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue); } $(that.wrapSelector('#vp_wcPreview')).html(errorContent); vpLog.display(VP_LOG_TYPE.ERROR, msg.content.ename, msg.content.evalue, msg.content); - } - }).catch(function(resultObj) { - let { msg } = resultObj; - var errorContent = ''; - if (msg.content.ename) { - errorContent = com_util.templateForErrorBox(msg.content.ename, msg.content.evalue); - } - $(that.wrapSelector('#vp_wcPreview')).html(errorContent); - vpLog.display(VP_LOG_TYPE.ERROR, msg.content.ename, msg.content.evalue, msg.content); + }); }); } diff --git a/python/userCommand.py b/python/userCommand.py index 0d48ee72..96307fc6 100644 --- a/python/userCommand.py +++ b/python/userCommand.py @@ -81,11 +81,16 @@ def vp_create_feature_importances(model, X_train=None, sort=False): ###### # Visual Python: Machine Learning > Model Info ###### -def vp_plot_feature_importances(model, X_train=None, sort=False): +def vp_plot_feature_importances(model, X_train=None, sort=False, top_count=0): df_i = vp_create_feature_importances(model, X_train, sort) - if sort: df_i['Percentage'].sort_values().plot(kind='barh') - else: df_i['Percentage'].plot(kind='barh') + if sort: + if top_count > 0: + df_i['Percentage'].sort_values().tail(top_count).plot(kind='barh') + else: + df_i['Percentage'].sort_values().plot(kind='barh') + else: + df_i['Percentage'].plot(kind='barh') _vp_plt.xlabel('Feature importance Percentage') _vp_plt.ylabel('Features') diff --git a/python/variableCommand.py b/python/variableCommand.py index 22975e77..26d5f906 100644 --- a/python/variableCommand.py +++ b/python/variableCommand.py @@ -80,15 +80,30 @@ def _vp_get_profiling_list(): import numpy as _vp_np import random as _vp_rd def _vp_sample(data, sample_cnt): + """ + Sampling data + """ dataType = type(data).__name__ sample_cnt = len(data) if len(data) < sample_cnt else sample_cnt if dataType == 'DataFrame': - return data.sample(sample_cnt) + return data.sample(sample_cnt, random_state=0) elif dataType == 'Series': - return data.sample(sample_cnt) + return data.sample(sample_cnt, random_state=0) elif dataType == 'ndarray': return data[_vp_np.random.choice(data.shape[0], sample_cnt, replace=False)] elif dataType == 'list': return _vp_rd.choices(data, k=sample_cnt) - return data \ No newline at end of file + return data + +def _vp_check_module_loaded(fname_list): + """ + Check if this module is loaded + """ + result = [] + for fname in fname_list: + if fname in globals(): + result.append(True) + else: + result.append(False) + return result \ No newline at end of file