Skip to content

Commit 3d6b0e2

Browse files
author
minjk-bl
committed
ML > Data Prep - make column transformer added
1 parent 874b594 commit 3d6b0e2

File tree

3 files changed

+224
-16
lines changed

3 files changed

+224
-16
lines changed

data/m_ml/mlLibrary.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ define([
250250
]
251251
},
252252
'make-column-transformer': {
253-
name: 'Make Column Transformer',
253+
name: 'MakeColumnTransformer',
254254
import: 'from sklearn.compose import make_column_transformer',
255255
code: 'make_column_transformer(${mct_code})',
256256
options: [

js/com/component/ModelEditor.js

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,22 @@ define([
141141
'transform': {
142142
...defaultActions['transform'],
143143
description: 'Transform labels to normalized encoding.'
144-
},
145-
'inverse_transform': {
146-
name: 'inverse_transform',
147-
label: 'Inverse transform',
148-
code: '${inverse_allocate} = ${model}.inverse_transform(${inverse_featureData})',
149-
description: 'Transform binary labels back to multi-class labels.',
150-
options: [
151-
{ name: 'inverse_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], default: 'X' },
152-
{ name: 'inverse_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable' }
153-
]
144+
}
145+
}
146+
147+
if (modelType != 'ColumnTransformer') {
148+
actions = {
149+
...actions,
150+
'inverse_transform': {
151+
name: 'inverse_transform',
152+
label: 'Inverse transform',
153+
code: '${inverse_allocate} = ${model}.inverse_transform(${inverse_featureData})',
154+
description: 'Transform binary labels back to multi-class labels.',
155+
options: [
156+
{ name: 'inverse_featureData', label: 'Feature Data', component: ['var_select'], var_type: ['DataFrame', 'Series'], default: 'X' },
157+
{ name: 'inverse_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable' }
158+
]
159+
}
154160
}
155161
}
156162
break;
@@ -373,6 +379,28 @@ define([
373379
}
374380
}
375381
}
382+
if (modelType == 'ColumnTransformer') {
383+
infos = {
384+
'transformers_': {
385+
name: 'transformers_',
386+
label: 'Transformers_',
387+
code: '${transformers_allocate} = ${model}.transformers_',
388+
description: 'The collection of fitted transformers as tuples of (name, fitted_transformer, column).',
389+
options: [
390+
{ name: 'transformers_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', default: 'classes' }
391+
]
392+
},
393+
'get_feature_names_out': {
394+
name: 'get_feature_names_out',
395+
label: 'Get feature names',
396+
code: '${feature_names_allocate} = ${model}.get_feature_names_out()',
397+
description: 'Get output feature names.',
398+
options: [
399+
{ name: 'feature_names_allocate', label: 'Allocate to', component: ['input'], placeholder: 'New variable', default: 'features' }
400+
]
401+
}
402+
}
403+
}
376404
infos = {
377405
...infos,
378406
'get_params': defaultInfos['get_params']

js/m_ml/DataPrep.js

Lines changed: 185 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ define([
2121
'vp_base/data/m_ml/mlLibrary',
2222
'vp_base/js/com/component/PopupComponent',
2323
'vp_base/js/com/component/VarSelector2',
24-
'vp_base/js/com/component/ModelEditor'
25-
], function(msHtml, com_util, com_interface, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, ModelEditor) {
24+
'vp_base/js/com/component/ModelEditor',
25+
'vp_base/js/com/component/MultiSelector'
26+
], function(msHtml, com_util, com_interface, com_String, com_generator, ML_LIBRARIES, PopupComponent, VarSelector2, ModelEditor, MultiSelector) {
2627

2728
/**
2829
* DataPrep
@@ -47,6 +48,12 @@ define([
4748
...this.state
4849
}
4950

51+
this.popup = {
52+
type: '',
53+
targetSelector: '',
54+
colSelector: undefined // multi column selector
55+
}
56+
5057
this.modelConfig = ML_LIBRARIES;
5158

5259
this.modelTypeList = {
@@ -55,6 +62,10 @@ define([
5562
'ETC': ['make-column-transformer']
5663
}
5764

65+
this.mctEstimator = {
66+
'Encoding': ['prep-onehot', 'prep-label', 'prep-ordinal', 'prep-target', 'prep-smote'],
67+
'Scaling': ['prep-standard', 'prep-robust', 'prep-minmax', 'prep-normalizer', 'prep-func-trsfrm-log', 'prep-func-trsfrm-exp', 'prep-poly-feat', 'prep-kbins-discretizer'],
68+
}
5869

5970
}
6071

@@ -82,6 +93,12 @@ define([
8293
} else {
8394
$(that.wrapSelector('#vp_installLibrary')).hide();
8495
}
96+
97+
if (modelType == 'make-column-transformer') {
98+
// load mct-targetData
99+
that.loadVariableList();
100+
that.bindMCT();
101+
}
85102
});
86103

87104
// install library
@@ -99,6 +116,47 @@ define([
99116
});
100117
}
101118

119+
bindMCT() {
120+
let that = this;
121+
// mct : click column selector 1
122+
$(this.wrapSelector('#mct_columns1btn')).on('click', function() {
123+
that.openColumnSelector($(that.wrapSelector('#mct_columns1')), 'Select columns to transform');
124+
});
125+
126+
// mct : click column selector 2
127+
$(this.wrapSelector('#mct_columns2btn')).on('click', function() {
128+
that.openColumnSelector($(that.wrapSelector('#mct_columns2')), 'Select columns to transform');
129+
});
130+
}
131+
132+
/**
133+
* Open Inner popup page for column selection
134+
* @param {Object} targetSelector
135+
* @param {string} title
136+
* @param {Array<string>} includeList
137+
*/
138+
openColumnSelector(targetSelector, title='Select columns', includeList=[]) {
139+
this.popup.type = 'column';
140+
this.popup.targetSelector = targetSelector;
141+
var previousList = targetSelector.data('list');
142+
if (previousList) {
143+
previousList = previousList.map(col => col.code)
144+
}
145+
this.renderMultiSelector(previousList, includeList);
146+
this.openInnerPopup(title);
147+
}
148+
149+
/**
150+
* Render column selector using MultiSelector module
151+
* @param {Array<string>} previousList previous selected columns
152+
* @param {Array<string>} includeList columns to include
153+
*/
154+
renderMultiSelector(previousList, includeList) {
155+
this.popup.colSelector = new MultiSelector(this.wrapSelector('.vp-inner-popup-body'),
156+
{ mode: 'columns', parent: [this.state.mct_targetData], selectedList: previousList, includeList: includeList }
157+
);
158+
}
159+
102160
templateForBody() {
103161
let page = $(msHtml);
104162

@@ -215,8 +273,63 @@ define([
215273
let state = this.state;
216274
let optBox = new com_String();
217275
if (modelType == 'make-column-transformer') {
276+
let that = this;
218277
// render tag
219-
optBox.append('Test'); // TODO:
278+
// DataFrame selection
279+
optBox.appendLine('<label for="mct_targetData" class="vp-orange-text">DataFrame</label>');
280+
optBox.appendLine('<select id="mct_targetData" class="vp-state vp-select"></select>');
281+
// Estimator 1 selection
282+
optBox.appendLine('<label for="">Estimator 1</label>');
283+
optBox.appendLine('<select id="mct_estimator1" class="vp-state vp-select">');
284+
optBox.appendFormatLine('<option value="{0}">{1}</option>', '', 'None');
285+
Object.keys(this.mctEstimator).forEach(modelCategory => {
286+
let modelOptionTag = new com_String();
287+
that.mctEstimator[modelCategory].forEach(opt => {
288+
let optConfig = that.modelConfig[opt];
289+
let selectedFlag = '';
290+
if (opt == that.state.mct_estimator1) {
291+
selectedFlag = 'selected';
292+
}
293+
modelOptionTag.appendFormatLine('<option value="{0}" {1}>{2}</option>',
294+
opt, selectedFlag, optConfig.name);
295+
})
296+
optBox.appendFormatLine('<optgroup label="{0}">{1}</optgroup>',
297+
modelCategory, modelOptionTag.toString());
298+
});
299+
optBox.appendLine('</select>');
300+
// Estimator 1 column selection
301+
optBox.appendLine('<label for="mct_columns1">Columns 1</label>');
302+
optBox.appendLine('<div>');
303+
optBox.appendLine('<input type="text" id="mct_columns1" class="vp-input vp-state" placeholder="Estimator 1 columns" disabled="">');
304+
optBox.appendLine('<button id="mct_columns1btn" class="vp-button w50">Edit</button>');
305+
optBox.appendLine('</div>');
306+
307+
// Estimator 2 selection
308+
optBox.appendLine('<label for="">Estimator 2</label>');
309+
optBox.appendLine('<select id="mct_estimator2" class="vp-state vp-select">');
310+
optBox.appendFormatLine('<option value="{0}">{1}</option>', '', 'None');
311+
Object.keys(this.mctEstimator).forEach(modelCategory => {
312+
let modelOptionTag = new com_String();
313+
that.mctEstimator[modelCategory].forEach(opt => {
314+
let optConfig = that.modelConfig[opt];
315+
let selectedFlag = '';
316+
if (opt == that.state.mct_estimator2) {
317+
selectedFlag = 'selected';
318+
}
319+
modelOptionTag.appendFormatLine('<option value="{0}" {1}>{2}</option>',
320+
opt, selectedFlag, optConfig.name);
321+
})
322+
optBox.appendFormatLine('<optgroup label="{0}">{1}</optgroup>',
323+
modelCategory, modelOptionTag.toString());
324+
});
325+
optBox.appendLine('</select>');
326+
// Estimator 2 column selection
327+
optBox.appendLine('<label for="mct_columns1">Columns 2</label>');
328+
optBox.appendLine('<div>');
329+
optBox.appendLine('<input type="text" id="mct_columns2" class="vp-input vp-state" placeholder="Estimator 2 columns" disabled="">');
330+
optBox.appendLine('<button id="mct_columns2btn" class="vp-button w50">Edit</button>');
331+
optBox.appendLine('</div>');
332+
220333
} else {
221334
// render tag
222335
config.options.forEach(opt => {
@@ -236,9 +349,58 @@ define([
236349
return optBox.toString();
237350
}
238351

352+
/**
353+
* Load variable list (dataframe)
354+
*/
355+
loadVariableList() {
356+
var that = this;
357+
// load using kernel
358+
var dataTypes = ['DataFrame'];
359+
vpKernel.getDataList(dataTypes).then(function(resultObj) {
360+
let { result } = resultObj;
361+
try {
362+
var varList = JSON.parse(result);
363+
// render variable list
364+
// get prevvalue
365+
var prevValue = that.state.mct_targetData;
366+
// replace
367+
$(that.wrapSelector('#mct_targetData')).replaceWith(function() {
368+
var tag = new com_String();
369+
tag.appendFormatLine('<select id="{0}" class="vp-select vp-state">', 'mct_targetData');
370+
varList.forEach(vObj => {
371+
// varName, varType
372+
var label = vObj.varName;
373+
tag.appendFormatLine('<option value="{0}" data-type="{1}" {2}>{3}</option>'
374+
, vObj.varName, vObj.varType
375+
, prevValue == vObj.varName?'selected':''
376+
, label);
377+
});
378+
tag.appendLine('</select>'); // VP_VS_VARIABLES
379+
return tag.toString();
380+
});
381+
$(that.wrapSelector('#mct_targetData')).trigger('change');
382+
} catch (ex) {
383+
vpLog.display(VP_LOG_TYPE.ERROR, 'DataPrep:', result);
384+
}
385+
});
386+
}
387+
388+
handleInnerOk() {
389+
// ok input popup
390+
var dataList = this.popup.colSelector.getDataList();
391+
392+
$(this.popup.targetSelector).val(dataList.map(col => { return col.code }).join(','));
393+
$(this.popup.targetSelector).data('list', dataList);
394+
$(this.popup.targetSelector).trigger({ type: 'change', dataList: dataList });
395+
this.closeInnerPopup();
396+
}
397+
239398
render() {
240399
super.render();
241400

401+
this.loadVariableList();
402+
this.bindMCT();
403+
242404
// Instance Editor
243405
this.modelEditor = new ModelEditor(this, "model", "instanceEditor");
244406
}
@@ -255,11 +417,29 @@ define([
255417
*/
256418
let config = this.modelConfig[modelType];
257419
code.appendLine(config.import);
258-
code.appendLine();
259-
420+
260421
// model code
261422
let modelCode = config.code;
262423
modelCode = com_generator.vp_codeGenerator(this, config, this.state, (userOption != ''? ', ' + userOption : ''));
424+
425+
// generate mct code
426+
if (modelType == 'make-column-transformer') {
427+
let mctCodes = [];
428+
let { mct_estimator1, mct_columns1, mct_estimator2, mct_columns2 } = this.state;
429+
if (mct_estimator1 != undefined && mct_estimator1 != '') {
430+
code.appendLine(this.modelConfig[mct_estimator1].import);
431+
let estimator1code = com_generator.vp_codeGenerator(this, this.modelConfig[mct_estimator1], this.state, (userOption != ''? ', ' + userOption : ''));
432+
mctCodes.push(com_util.formatString('({0}, [{1}])', estimator1code, mct_columns1));
433+
}
434+
if (mct_estimator2 != undefined && mct_estimator2 != '') {
435+
code.appendLine(this.modelConfig[mct_estimator2].import);
436+
let estimator2code = com_generator.vp_codeGenerator(this, this.modelConfig[mct_estimator2], this.state, (userOption != ''? ', ' + userOption : ''));
437+
mctCodes.push(com_util.formatString('({0}, [{1}])', estimator2code, mct_columns2));
438+
}
439+
modelCode = modelCode.replace('${mct_code}', mctCodes.join(', '));
440+
}
441+
442+
code.appendLine();
263443
code.appendFormat('{0} = {1}', allocateToCreation, modelCode);
264444
} else {
265445
/**

0 commit comments

Comments
 (0)