/* * 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('{1}', 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; });