/*
* Project Name : Visual Python
* Description : GUI-based Python code generator
* File Name : DataPrep.js
* Author : Black Logic
* Note : Model selection and fitting
* License : GNU GPLv3 with Visual Python special exception
* Date : 2022. 02. 07
* Change Date :
*/
//============================================================================
// [CLASS] DataPrep
//============================================================================
define([
__VP_TEXT_LOADER__('vp_base/html/m_ml/model.html'), // INTEGRATION: unified version of text loader
'vp_base/js/com/com_util',
'vp_base/js/com/com_interface',
'vp_base/js/com/com_String',
'vp_base/js/com/com_generatorV2',
'vp_base/data/m_ml/mlLibrary',
'vp_base/js/com/component/PopupComponent',
'vp_base/js/com/component/VarSelector2',
'vp_base/js/com/component/ModelEditor',
'vp_base/js/com/component/MultiSelector'
], function(msHtml, com_util, com_interface, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, ModelEditor, MultiSelector) {
/**
* DataPrep
*/
class DataPrep extends PopupComponent {
_init() {
super._init();
this.config.sizeLevel = 2;
this.config.dataview = false;
this.state = {
// model creation
modelControlType: 'creation',
modelType: 'prep-onehot',
userOption: '',
featureData: 'X_train',
targetData: 'y_train',
allocateToCreation: 'model',
// model selection
model: '',
method: '',
...this.state
}
this.popup = {
type: '',
targetSelector: '',
colSelector: undefined // multi column selector
}
this.modelConfig = ML_LIBRARIES;
this.modelTypeList = {
'Encoding': ['prep-onehot', 'prep-label', 'prep-ordinal', 'prep-target'],
'Scaling': ['prep-standard', 'prep-robust', 'prep-minmax', 'prep-normalizer', 'prep-func-trsfrm-log', 'prep-func-trsfrm-exp', 'prep-poly-feat', 'prep-kbins-discretizer'],
'ETC': ['prep-simple-imputer', 'prep-smote', 'make-column-transformer']
}
this.mctEstimator = {
'Encoding': ['prep-onehot', 'prep-label', 'prep-ordinal', 'prep-target'],
'Scaling': ['prep-standard', 'prep-robust', 'prep-minmax', 'prep-normalizer', 'prep-func-trsfrm-log', 'prep-func-trsfrm-exp', 'prep-poly-feat', 'prep-kbins-discretizer'],
}
}
_bindEvent() {
super._bindEvent();
/** Implement binding events */
var that = this;
// select model control type
$(this.wrapSelector('#modelControlType')).on('change', function() {
let modelControlType = $(this).val();
// show/hide model box
$(that.wrapSelector('.vp-model-box')).hide();
$(that.wrapSelector(`.vp-model-box[data-type="${modelControlType}"]`)).show();
});
// select model type
$(this.wrapSelector('#modelType')).on('change', function() {
let modelType = $(this).val();
that.state.modelType = modelType;
$(that.wrapSelector('.vp-model-option-box')).html(that.templateForOption(modelType));
// show install button
if (that.modelConfig[modelType].install != undefined) {
$(that.wrapSelector('#vp_installLibrary')).show();
} else {
$(that.wrapSelector('#vp_installLibrary')).hide();
}
if (modelType === 'make-column-transformer') {
// load mct-targetData
that.loadVariableList();
that.bindMCT();
} else if (modelType === 'prep-simple-imputer') {
$(that.wrapSelector('#missing_values')).replaceWith(`
${$(that.wrapSelector('#missing_values'))[0].outerHTML}
`);
$(that.wrapSelector('#fill_value')).replaceWith(`
${$(that.wrapSelector('#fill_value'))[0].outerHTML}
`);
}
});
// install library
$(this.wrapSelector('#vp_installLibrary')).on('click', function() {
let config = that.modelConfig[that.state.modelType];
if (config && config.install != undefined) {
// insert install code
let installCode = config.install;
if (vpConfig.extensionType === 'lite') {
installCode = installCode.replace('!', '%');
}
com_interface.insertCell('code', installCode, true, 'Machine Learning > DataPrep');
}
});
// change model
$(this.wrapSelector('#model')).on('change', function() {
that.modelEditor.reload();
});
}
bindMCT() {
let that = this;
// mct : click column selector 1
$(this.wrapSelector('#mct_columns1btn')).on('click', function() {
that.openColumnSelector($(that.wrapSelector('#mct_columns1')), 'Select columns to transform');
});
// mct : click column selector 2
$(this.wrapSelector('#mct_columns2btn')).on('click', function() {
that.openColumnSelector($(that.wrapSelector('#mct_columns2')), 'Select columns to transform');
});
}
/**
* Open Inner popup page for column selection
* @param {Object} targetSelector
* @param {string} title
* @param {Array} includeList
*/
openColumnSelector(targetSelector, title='Select columns', includeList=[]) {
this.popup.type = 'column';
this.popup.targetSelector = targetSelector;
var previousList = targetSelector.data('list');
if (previousList) {
previousList = previousList.map(col => col.code)
}
this.renderMultiSelector(previousList, includeList);
this.openInnerPopup(title);
}
/**
* Render column selector using MultiSelector module
* @param {Array} previousList previous selected columns
* @param {Array} includeList columns to include
*/
renderMultiSelector(previousList, includeList) {
this.popup.colSelector = new MultiSelector(this.wrapSelector('.vp-inner-popup-body'),
{ mode: 'columns', parent: [this.state.mct_targetData], selectedList: previousList, includeList: includeList }
);
}
templateForBody() {
let page = $(msHtml);
let that = this;
// model control type
$(page).find('.vp-model-box').hide();
$(page).find(`.vp-model-box[data-type="${this.state.modelControlType}"]`).show();
//================================================================
// Model creation
//================================================================
// model types
let modelTypeTag = new com_String();
Object.keys(this.modelTypeList).forEach(modelCategory => {
let modelOptionTag = new com_String();
that.modelTypeList[modelCategory].forEach(opt => {
let optConfig = that.modelConfig[opt];
let selectedFlag = '';
if (opt == that.state.modelType) {
selectedFlag = 'selected';
}
modelOptionTag.appendFormatLine('',
opt, selectedFlag, optConfig.name);
})
modelTypeTag.appendFormatLine('',
modelCategory, modelOptionTag.toString());
});
$(page).find('#modelType').html(modelTypeTag.toString());
// show install button
if (this.modelConfig[this.state.modelType].install != undefined) {
$(page).find('#vp_installLibrary').show();
} else {
$(page).find('#vp_installLibrary').hide();
}
// render option page
$(page).find('.vp-model-option-box').html(this.templateForOption(this.state.modelType));
let varSelector = new VarSelector2(this.wrapSelector());
varSelector.setComponentID('featureData');
varSelector.addClass('vp-state vp-input');
varSelector.setValue(this.state.featureData);
$(page).find('#featureData').replaceWith(varSelector.toTagString());
varSelector = new VarSelector2(this.wrapSelector());
varSelector.setComponentID('targetData');
varSelector.addClass('vp-state vp-input');
varSelector.setValue(this.state.targetData);
$(page).find('#targetData').replaceWith(varSelector.toTagString());
//================================================================
// Model selection
//================================================================
// set model list
let modelOptionTag = new com_String();
vpKernel.getModelList('Data Preparation').then(function(resultObj) {
let { result } = resultObj;
var modelList = JSON.parse(result);
modelList && modelList.forEach(model => {
let selectFlag = '';
if (model.varName == that.state.model) {
selectFlag = 'selected';
}
modelOptionTag.appendFormatLine('',
model.varName, model.varType, selectFlag, model.varName, model.varType);
});
$(page).find('#model').html(modelOptionTag.toString());
$(that.wrapSelector('#model')).html(modelOptionTag.toString());
if (!that.state.model || that.state.model == '') {
that.state.model = $(that.wrapSelector('#model')).val();
}
that.modelEditor.show();
});
//================================================================
// Load state
//================================================================
Object.keys(this.state).forEach(key => {
let tag = $(page).find('#' + key);
let tagName = $(tag).prop('tagName'); // returns with UpperCase
let value = that.state[key];
if (value == undefined) {
return;
}
switch(tagName) {
case 'INPUT':
let inputType = $(tag).prop('type');
if (inputType == 'text' || inputType == 'number' || inputType == 'hidden') {
$(tag).val(value);
break;
}
if (inputType == 'checkbox') {
$(tag).prop('checked', value);
break;
}
break;
case 'TEXTAREA':
case 'SELECT':
default:
$(tag).val(value);
break;
}
});
return page;
}
templateForOption(modelType) {
let config = this.modelConfig[modelType];
let state = this.state;
let optBox = new com_String();
if (modelType == 'make-column-transformer') {
let that = this;
// render tag
// DataFrame selection
optBox.appendLine('');
optBox.appendLine('');
// Estimator 1 selection
optBox.appendLine('');
optBox.appendLine('');
// Estimator 1 column selection
optBox.appendLine('');
optBox.appendLine('');
optBox.appendLine('');
optBox.appendLine('');
optBox.appendLine('
');
// Estimator 2 selection
optBox.appendLine('');
optBox.appendLine('');
// Estimator 2 column selection
optBox.appendLine('');
optBox.appendLine('');
optBox.appendLine('');
optBox.appendLine('');
optBox.appendLine('
');
} else {
// render tag
config.options.forEach(opt => {
optBox.appendFormatLine(''
, opt.name, opt.name, com_util.optionToLabel(opt.name));
let content = com_generator.renderContent(this, opt.component[0], opt, state);
optBox.appendLine(content[0].outerHTML);
});
// show user option
if (config.code.includes('${etc}')) {
// render user option
optBox.appendFormatLine('', 'userOption', 'User option');
optBox.appendFormatLine('',
'userOption', 'key=value, ...', this.state.userOption);
}
}
return optBox.toString();
}
/**
* Load variable list (dataframe)
*/
loadVariableList() {
var that = this;
// load using kernel
var dataTypes = ['DataFrame'];
vpKernel.getDataList(dataTypes).then(function(resultObj) {
let { result } = resultObj;
try {
var varList = JSON.parse(result);
// render variable list
// get prevvalue
var prevValue = that.state.mct_targetData;
// replace
$(that.wrapSelector('#mct_targetData')).replaceWith(function() {
var tag = new com_String();
tag.appendFormatLine(''); // VP_VS_VARIABLES
return tag.toString();
});
$(that.wrapSelector('#mct_targetData')).trigger('change');
} catch (ex) {
vpLog.display(VP_LOG_TYPE.ERROR, 'DataPrep:', result);
}
});
}
handleInnerOk() {
// ok input popup
var dataList = this.popup.colSelector.getDataList();
$(this.popup.targetSelector).val(dataList.map(col => { return col.code }).join(','));
$(this.popup.targetSelector).data('list', dataList);
$(this.popup.targetSelector).trigger({ type: 'change', dataList: dataList });
this.closeInnerPopup();
}
render() {
super.render();
this.loadVariableList();
this.bindMCT();
// Instance Editor
this.modelEditor = new ModelEditor(this, "model", "instanceEditor");
}
generateCode() {
let { modelControlType, modelType, userOption, allocateToCreation, model } = this.state;
let state = JSON.parse(JSON.stringify(this.state));
let code = new com_String();
if (modelControlType == 'creation') {
/**
* Model Creation
* ---
* from module import model_function
* model = Model(key=value, ...)
*/
let config = this.modelConfig[modelType];
code.appendLine(config.import);
if (modelType === 'prep-simple-imputer') {
let checkList = ['missing_values', 'fill_value'];
checkList.forEach(checkKey => {
try {
state[checkKey] = com_util.convertToStr(state[checkKey], state[checkKey + '_istext']);
} catch(e) {
;
}
});
}
// model code
let modelCode = config.code;
modelCode = com_generator.vp_codeGenerator(this, config, state, (userOption != ''? ', ' + userOption : ''));
// generate mct code
if (modelType === 'make-column-transformer') {
let mctCodes = [];
let { mct_estimator1, mct_columns1='', mct_estimator2, mct_columns2='' } = state;
if (mct_estimator1 != undefined && mct_estimator1 != '') {
code.appendLine(this.modelConfig[mct_estimator1].import);
let estimator1code = com_generator.vp_codeGenerator(this, this.modelConfig[mct_estimator1], state, (userOption != ''? ', ' + userOption : ''));
mctCodes.push(com_util.formatString('({0}, [{1}])', estimator1code, mct_columns1));
}
if (mct_estimator2 != undefined && mct_estimator2 != '') {
code.appendLine(this.modelConfig[mct_estimator2].import);
let estimator2code = com_generator.vp_codeGenerator(this, this.modelConfig[mct_estimator2], state, (userOption != ''? ', ' + userOption : ''));
mctCodes.push(com_util.formatString('({0}, [{1}])', estimator2code, mct_columns2));
}
modelCode = modelCode.replace('${mct_code}', mctCodes.join(', '));
}
code.appendLine();
code.appendFormat('{0} = {1}', allocateToCreation, modelCode);
} else {
/**
* Model Selection
* ---
* ...
*/
code.append(this.modelEditor.getCode({'${model}': model}));
}
return code.toString();
}
}
return DataPrep;
});