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 @@
+
+
+
+
+
+
+
+
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/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/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;
}
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/com/com_generatorV2.js b/js/com/com_generatorV2.js
index 4881a316..a8256500 100644
--- a/js/com/com_generatorV2.js
+++ b/js/com/com_generatorV2.js
@@ -656,12 +656,13 @@ define([
* @param {object} target
* @param {array} columnInputIdList
* @param {string} tagType input / select (tag type)
+ * @param {array/boolean} columnWithEmpty boolean array or value to decide whether select tag has empty space
* Usage :
* $(document).on('change', this.wrapSelector('#dataframe_tag_id'), function() {
- * pdGen.vp_bindColumnSource(that.wrapSelector(), this, ['column_input_id']);
+ * pdGen.vp_bindColumnSource(that.wrapSelector(), this, ['column_input_id'], 'select', [true, true, true]);
* });
*/
- var vp_bindColumnSource = function(selector, target, columnInputIdList, tagType="input") {
+ var vp_bindColumnSource = function(selector, target, columnInputIdList, tagType="input", columnWithEmpty=false) {
var varName = '';
if ($(target).length > 0) {
varName = $(target).val();
@@ -701,8 +702,22 @@ define([
let { result, type, msg } = resultObj;
var varResult = JSON.parse(result);
+ // check if it needs to add empty option
+ let addEmpty = false;
+ if (Array.isArray(columnWithEmpty)) {
+ addEmpty = columnWithEmpty[idx];
+ } else {
+ addEmpty = columnWithEmpty;
+ }
+ if (addEmpty == true) {
+ varResult = [
+ {value: '', label: ''},
+ ...varResult
+ ]
+ }
+
// columns using suggestInput
- columnInputIdList && columnInputIdList.forEach(columnInputId => {
+ columnInputIdList && columnInputIdList.forEach((columnInputId, idx) => {
let defaultValue = $(selector + ' #' + columnInputId).val();
if (defaultValue == null || defaultValue == undefined) {
defaultValue = '';
@@ -724,6 +739,7 @@ define([
'id': columnInputId,
'class': 'vp-select vp-state'
});
+ // make tag
varResult.forEach(listVar => {
var option = document.createElement('option');
$(option).attr({
diff --git a/js/com/component/ModelEditor.js b/js/com/component/ModelEditor.js
index 9b6fa738..2eee09cf 100644
--- a/js/com/component/ModelEditor.js
+++ b/js/com/component/ModelEditor.js
@@ -883,8 +883,13 @@ define([
});
}
+ /**
+ * Show Model Editor
+ * @param {*} showType all / action / info
+ */
show() {
$(this.wrapSelector()).show();
+
this.reload();
}
diff --git a/js/com/component/PopupComponent.js b/js/com/component/PopupComponent.js
index 4c5dbac7..548adffe 100644
--- a/js/com/component/PopupComponent.js
+++ b/js/com/component/PopupComponent.js
@@ -251,12 +251,44 @@ 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();
+ // save size
+ that.config.size = {
+ width: $(that.wrapSelector()).width(),
+ height: $(that.wrapSelector()).height()
+ }
+ // 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() {
@@ -297,45 +329,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 +417,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) {
});
}
@@ -458,8 +452,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();
+ }
});
}
@@ -618,46 +618,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_ml/FitPredict.js b/js/m_ml/FitPredict.js
new file mode 100644
index 00000000..1366c913
--- /dev/null
+++ b/js/m_ml/FitPredict.js
@@ -0,0 +1,685 @@
+/*
+ * 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',
+ 'css!vp_base/css/m_ml/fitPredict.css',
+ 'vp_base/js/com/com_util',
+ '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/SuggestInput'
+], function(msHtml, msCss, com_util, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, SuggestInput) {
+
+ /**
+ * FitPredict
+ */
+ class FitPredict extends PopupComponent {
+ _init() {
+ super._init();
+ this.config.sizeLevel = 2;
+ this.config.dataview = false;
+
+ this.state = {
+ // model selection
+ category: 'All',
+ model: '',
+ modelType: '',
+ method: '',
+ action: {},
+ 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;
+
+ // click category
+ $(this.wrapSelector('.vp-ins-select-list.category .vp-ins-select-item')).on('click', function() {
+ let category = $(this).data('var-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);
+ });
+ }
+
+ templateForBody() {
+ let page = $(msHtml);
+
+ let that = this;
+
+ //================================================================
+ // Model selection
+ //================================================================
+ // set model category list
+ let modelCategoryTag = new com_String();
+ this.modelCategories.forEach(category => {
+ let selected = '';
+ if (category == that.state.category) {
+ selected = 'selected';
+ }
+ modelCategoryTag.appendFormatLine('
{5} ',
+ 'vp-ins-select-item', selected, category, 'category', category, category);
+ });
+ $(page).find('.vp-ins-select-list.category').html(modelCategoryTag.toString());
+
+ //================================================================
+ // 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 optionConfig = this.modelConfig[modelType];
+ let state = this.state;
+
+ let optBox = new com_String();
+ // render tag
+ optionConfig.options.forEach(opt => {
+ optBox.appendFormatLine('
{2} '
+ , 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('
{1} ', 'userOption', 'User option');
+ optBox.appendFormatLine('
',
+ 'userOption', 'key=value, ...', this.state.userOption);
+ return optBox.toString();
+ }
+
+ 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 model = this.state.model;
+ let modelType = this.state.modelType;
+
+ let actions = this.getAction(modelType);
+ this.state.action = { ...actions };
+
+ var actListTag = 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);
+ });
+
+ $(this.wrapSelector('.vp-ins-select-list.action')).html(actListTag.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();
+ });
+
+ // bind event
+ // 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.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 optionConfig = this.state[type][name];
+ let optBox = new com_String();
+ // render tag
+ optionConfig && optionConfig.options && optionConfig.options.forEach(opt => {
+ let label = opt.name;
+ if (opt.label != undefined) {
+ label = opt.label;
+ }
+ // fix label
+ label = com_util.optionToLabel(label);
+ optBox.appendFormatLine('
{2} '
+ , 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.optionConfig = optionConfig;
+
+ // add selection
+ 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);
+ $(this.wrapSelector('#modelEditorName')).val(name);
+ this.state.modelEditorType = type;
+ this.state.modelEditorName = name;
+ }
+ }
+
+ generateCode() {
+ let { model } = this.state;
+ let code = new com_String();
+ let replaceDict = {'${model}': model};
+
+ if (this.state.optionConfig.import != undefined) {
+ code.appendLine(this.state.optionConfig.import);
+ code.appendLine();
+ }
+ 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();
+ }
+
+ 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;
+});
\ 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..2d945da3
--- /dev/null
+++ b/js/m_ml/ModelInfo.js
@@ -0,0 +1,654 @@
+/*
+ * 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] ModelInfo
+//============================================================================
+define([
+ '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_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/SuggestInput'
+], function(msHtml, msCss, com_util, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, SuggestInput) {
+
+ /**
+ * ModelInfo
+ */
+ class ModelInfo extends PopupComponent {
+ _init() {
+ super._init();
+ this.config.sizeLevel = 2;
+ this.config.dataview = false;
+
+ this.state = {
+ // model selection
+ category: 'All',
+ model: '',
+ modelType: '',
+ method: '',
+ info: {},
+ 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;
+
+ // click category
+ $(this.wrapSelector('.vp-ins-select-list.category .vp-ins-select-item')).on('click', function() {
+ let category = $(this).data('var-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);
+ });
+ }
+
+ templateForBody() {
+ let page = $(msHtml);
+
+ let that = this;
+
+ //================================================================
+ // Model selection
+ //================================================================
+ // set model category list
+ let modelCategoryTag = new com_String();
+ this.modelCategories.forEach(category => {
+ let selected = '';
+ if (category == that.state.category) {
+ selected = 'selected';
+ }
+ modelCategoryTag.appendFormatLine('
{5} ',
+ 'vp-ins-select-item', selected, category, 'category', category, category);
+ });
+ $(page).find('.vp-ins-select-list.category').html(modelCategoryTag.toString());
+
+ //================================================================
+ // 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 optionConfig = this.modelConfig[modelType];
+ let state = this.state;
+
+ let optBox = new com_String();
+ // render tag
+ optionConfig.options.forEach(opt => {
+ optBox.appendFormatLine('
{2} '
+ , 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('
{1} ', 'userOption', 'User option');
+ optBox.appendFormatLine('
',
+ 'userOption', 'key=value, ...', this.state.userOption);
+ return optBox.toString();
+ }
+
+ 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 model = this.state.model;
+ let modelType = this.state.modelType;
+
+ 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.info')).html(infoListTag.toString());
+
+ let that = this;
+ // info search suggest
+ let suggestInput = new SuggestInput();
+ suggestInput.addClass('vp-input');
+ suggestInput.addClass('vp-ins-search');
+ suggestInput.setPlaceholder("Search information");
+ 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
+ // 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.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 optionConfig = this.state[type][name];
+ let optBox = new com_String();
+ // render tag
+ optionConfig && optionConfig.options && optionConfig.options.forEach(opt => {
+ let label = opt.name;
+ if (opt.label != undefined) {
+ label = opt.label;
+ }
+ // fix label
+ label = com_util.optionToLabel(label);
+ optBox.appendFormatLine('
{2} '
+ , 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.optionConfig = optionConfig;
+
+ // add selection
+ 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);
+ $(this.wrapSelector('#modelEditorName')).val(name);
+ this.state.modelEditorType = type;
+ this.state.modelEditorName = name;
+ }
+ }
+
+ generateCode() {
+ let { model } = this.state;
+ let code = new com_String();
+ let replaceDict = {'${model}': model};
+
+ if (this.state.optionConfig.import != undefined) {
+ code.appendLine(this.state.optionConfig.import);
+ code.appendLine();
+ }
+ 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();
+ }
+
+ 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 = {
+ 'get_params': {
+ ...defaultInfos['get_params']
+ }
+ }
+
+ if (modelType == 'KMeans') {
+ infos = {
+ ...infos,
+ 'score': {
+ ...defaultInfos['score'],
+ description: 'Return the mean accuracy on the given test data and labels.'
+ },
+ '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'] }
+ ]
+ }
+ }
+ }
+
+ if (modelType == 'GaussianMixture') {
+ infos = {
+ ...infos,
+ 'score': {
+ ...defaultInfos['score'],
+ description: 'Compute the per-sample average log-likelihood of the given data X.'
+ }
+ }
+ }
+ 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;
+});
\ No newline at end of file
diff --git a/js/m_ml/evaluation.js b/js/m_ml/evaluation.js
index 41ab3be5..26ea1eed 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,13 +66,33 @@ 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();
+ 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);
}
}
});
@@ -76,12 +100,13 @@ define([
// 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);
}
}
});
@@ -91,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;
}
@@ -118,6 +143,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 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 vp-ev-model ari-nmi');
+ varSelector.setValue(this.state.targetData2);
+ $(page).find('#targetData2').replaceWith(varSelector.toTagString());
+
// model
// set model list
let modelOptionTag = new com_String();
@@ -169,14 +213,25 @@ define([
}
});
- 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();
+ $(page).find('.vp-upper-box').hide();
+ $(page).find('.vp-upper-box.' + this.state.modelType).show();
+
+ if (this.state.modelType == 'rgs') {
+ // Regression
+
+ } else if (this.state.modelType == 'clf') {
+ // 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;
@@ -197,7 +252,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 +373,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());
}
}
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',
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 8135a90c..4e509b1d 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: 1064, height: 550 };
this.state = {
chartType: 'scatterplot',
@@ -39,10 +39,27 @@ define([
figColumn: 0,
shareX: false,
shareY: false,
+ setXY: false,
data: '',
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,
@@ -52,17 +69,50 @@ define([
this.chartConfig = CHART_LIBRARIES;
this.chartTypeList = {
'Relational': [ 'scatterplot', 'lineplot' ],
- 'Distributions': [ 'histplot', 'kdeplot', 'ecdfplot', 'rugplot' ], // FIXME: ecdf : no module
+ 'Distributions': [ 'histplot', 'kdeplot', 'rugplot' ],
'Categorical': [ 'stripplot', 'swarmplot', 'boxplot', 'violinplot', 'pointplot', 'barplot' ],
- 'ETC': [ ]
+ // 'ETC': [ ]
}
+
+ this.legendPosList = [
+ 'best', 'upper right', 'upper left', 'lower left', 'lower right',
+ 'center left', 'center right', 'lower center', 'upper center', 'center'
+ ];
+
+ this.markerList = [
+ // 'custom': { label: 'Custom', value: 'marker' },
+ { label: ' ', value: ' ', title: 'select marker style'},
+ { label: '.', value: '.', title: 'point' },
+ { label: ',', value: ',', title: 'pixel' },
+ { label: 'o', value: 'o', title: 'circle' },
+ { label: '▼', value: 'v', title: 'triangle_down' },
+ { label: '▲', value: '^', title: 'triangle_up' },
+ { label: '◀', value: '<', title: 'triangle_left' },
+ { label: '▶', value: '>', title: 'triangle_right' },
+ { label: '┬', value: '1', title: 'tri_down' },
+ { label: '┵', value: '2', title: 'tri_up' },
+ { label: '┤', value: '3', title: 'tri_left' },
+ { label: '├', value: '4', title: 'tri_right' },
+ { label: 'octagon', value: '8', title: 'octagon' },
+ { label: 'square', value: 's', title: 'square' },
+ { label: 'pentagon', value: 'p', title: 'pentagon' },
+ { label: 'filled plus', value: 'P', title: 'plus (filled)' },
+ { label: 'star', value: '*', title: 'star' },
+ { label: 'hexagon1', value: 'h', title: 'hexagon1' },
+ { label: 'hexagon2', value: 'H', title: 'hexagon2' },
+ { label: 'plus', value: '+', title: 'plus' },
+ { label: 'x', value: 'x', title: 'x' },
+ { label: 'filled x', value: 'X', title: 'x (filled)' },
+ { label: 'diamond', value: 'D', title: 'diamond' },
+ { label: 'thin diamond', value: 'd', title: 'thin_diamond' }
+ ]
}
_bindEvent() {
- super._bindEvent();
-
let that = this;
+ super._bindEvent();
+
// setting popup
$(this.wrapSelector('#chartSetting')).on('click', function() {
// show popup box
@@ -88,30 +138,74 @@ 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');
- $(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();
});
-
- // bind column by dataframe
- $(document).on('change', this.wrapSelector('#data'), function() {
- com_generator.vp_bindColumnSource(that.wrapSelector(), this, ['x', 'y', 'hue'], 'select');
+
+ // use data or not
+ $(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 {
+ // set X Y indivisually
+ // 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());
+ }
});
// preview refresh
$(this.wrapSelector('#previewRefresh')).on('click', function() {
that.loadPreview();
});
- $(this.wrapSelector('.vp-state')).on('change', function() {
- if (that.state.autoRefresh && that.state.data != '') {
+ // 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() {
@@ -141,9 +235,50 @@ define([
let varSelector = new VarSelector2(this.wrapSelector(), ['DataFrame', 'Series', 'list']);
varSelector.setComponentID('data');
varSelector.addClass('vp-state vp-input');
- varSelector.setValue(this.state.featureData);
+ varSelector.setValue(this.state.data);
+ varSelector.setSelectEvent(function (value, item) {
+ $(this.wrapSelector()).val(value);
+ that.state.dtype = item.dtype;
+
+ if (item.dtype == 'DataFrame') {
+ $(that.wrapSelector('#x')).prop('disabled', false);
+ $(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', true);
+ } 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 legendPosTag = new com_String();
+ this.legendPosList.forEach(pos => {
+ let selectedFlag = '';
+ if (pos == that.state.legendPos) {
+ selectedFlag = 'selected';
+ }
+ legendPosTag.appendFormatLine('
{2}{3} ',
+ pos, selectedFlag, pos, pos == 'best'?' (default)':'');
+ });
+ $(page).find('#legendPos').html(legendPosTag.toString());
+
+ // marker style
+ let markerTag = new com_String();
+ this.markerList.forEach(marker => {
+ let selectedFlag = '';
+ if (marker.value == that.state.markerStyle) {
+ selectedFlag = 'selected';
+ }
+ markerTag.appendFormatLine('
{3} ',
+ marker.value, marker.title, selectedFlag, marker.label);
+ });
+ $(page).find('#markerStyle').html(markerTag.toString());
+
// preview sample count
let sampleCountList = [30, 50, 100, 300, 500, 700, 1000];
let sampleCountTag = new com_String();
@@ -157,23 +292,60 @@ define([
});
$(page).find('#sampleCount').html(sampleCountTag.toString());
+ //================================================================
+ // 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;
}
templateForSettingBox() {
- return `
-
Figure size
-
`;
+
+ `;
}
render() {
@@ -191,10 +363,10 @@ define([
// set size
$(this.wrapSelector('.vp-inner-popup-box')).css({ width: 400, height: 260});
- this.renderImportOptions();
+ this.bindSettingBox();
}
- renderImportOptions() {
+ bindSettingBox() {
//====================================================================
// Stylesheet suggestinput
//====================================================================
@@ -213,6 +385,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();
@@ -243,6 +416,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,53 +479,128 @@ 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();
}
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 indent = '';
let code = new com_String();
let config = this.chartConfig[chartType];
+ let state = JSON.parse(JSON.stringify(this.state));
+
+ let chartCode = new com_String();
+
+ let etcOptionCode = []
+ if (useMarker == 'True') {
+ // TODO: marker to seaborn argument (ex. marker='+' / markers={'Lunch':'s', 'Dinner':'X'})
+ etcOptionCode.push(com_util.formatString("marker='{0}'", markerStyle));
+ }
- let chartCode = com_generator.vp_codeGenerator(this, config, this.state, (userOption != ''? ', ' + userOption : ''));
+ // add user option
+ if (userOption != '') {
+ etcOptionCode.push(userOption);
+ }
+
+ if (etcOptionCode.length > 0) {
+ etcOptionCode = [
+ '',
+ ...etcOptionCode
+ ]
+ }
+
+ let generatedCode = com_generator.vp_codeGenerator(this, config, state, etcOptionCode.join(', '));
+
+ // Info
+ if (title && title != '') {
+ chartCode.appendFormatLine("plt.title('{0}')", title);
+ }
+ if (x_label && x_label != '') {
+ chartCode.appendFormatLine("plt.xlabel('{0}')", x_label);
+ }
+ if (y_label && y_label != '') {
+ chartCode.appendFormatLine("plt.ylabel('{0}')", y_label);
+ }
+ if (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 != '') {
+ chartCode.appendFormatLine("plt.ylim(({0}, {1}))", y_limit_from, y_limit_to);
+ }
+ if (useLegend == 'True' && legendPos != '') {
+ chartCode.appendFormatLine("plt.legend(loc='{0}')", legendPos);
+ }
+ if (useGrid == 'True') {
+ chartCode.appendLine("plt.grid(True)");
+ // TODO: grid types
+ // plt.grid(True, axis='x', color='red', alpha=0.5, linestyle='--')
+ }
+ 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
- code.appendLine('plt.figure(figsize=(6, 4))');
+ 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=30, random_state=0)';
+ convertedData = data + '.sample(n=' + sampleCount + ', random_state=0)';
+ // replace pre-defined options
+ generatedCode = generatedCode.replace(data, convertedData);
}
- }
-
- // replace pre-defined options
- chartCode = chartCode.replace(data, convertedData);
- code.appendLine(chartCode);
- 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();
}