From c4f683787974eb0cd6483669fe011bd2dcc1bcb6 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Wed, 30 Aug 2023 13:01:44 +0900 Subject: [PATCH 01/26] Add prev, next and fit_predict, fit_transform to pipeline, Add returnType to mlLibrary --- visualpython/css/m_ml/pipeline.css | 56 ++++++++- visualpython/data/m_ml/mlLibrary.js | 52 +++++++- visualpython/html/m_ml/pipeline.html | 4 + visualpython/js/m_ml/Pipeline.js | 173 ++++++++++++++++++++++----- 4 files changed, 253 insertions(+), 32 deletions(-) diff --git a/visualpython/css/m_ml/pipeline.css b/visualpython/css/m_ml/pipeline.css index 0bbe46c7..7e670421 100644 --- a/visualpython/css/m_ml/pipeline.css +++ b/visualpython/css/m_ml/pipeline.css @@ -15,7 +15,7 @@ } .vp-pp-right-box { display: grid; - grid-template-rows: 35px 10px calc(100% - 45px); + grid-template-rows: 35px 10px calc(100% - 80px) 35px; padding-left: 5px; overflow: auto; } @@ -45,7 +45,13 @@ position: absolute; transition: all 0.5s ease-in-out; } -.vp-pp-item:not(:last-child):before { +.vp-pp-item:last-child:before, +.vp-pp-item:last-child:after, +.vp-pp-item.vp-last-visible:before, +.vp-pp-item.vp-last-visible:after { + display: none; +} +.vp-pp-item:before { border: 0.25px solid var(--vp-gray-color); border-width: 0 4px 4px 0; display: inline-block; @@ -55,7 +61,7 @@ top: 47px; left: 100px; } -.vp-pp-item:not(:last-child):after { +.vp-pp-item:after { border: 0.25px solid var(--vp-gray-color); border-width: 0 4px 0 0; height: 15px; @@ -129,4 +135,48 @@ } .vp-pp-step-content:empty::after { content: 'Select the template and Follow the pipeline to generate simple ML code.' +} +.vp-pp-step-footer { + border-top: 0.25px solid var(--vp-border-gray-color); + width: 100%; + height: 35px; +} +.vp-pp-step-move-btn { + width: 70px; + height: 30px; + background-color: var(--vp-background-color); + border: 0.25px solid var(--vp-border-gray-color); + box-sizing: border-box; + box-shadow: 0.5px 0.5px 0.5px rgb(0 0 0 / 10%); + border-radius: 3px; + line-height: 30px; + vertical-align: middle; + font-family: 'AppleSDGothicNeo'; + font-size: 13px; + text-align: center; + color: var(--vp-font-primary); + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + display: inline-block; + position: absolute; + bottom: 10px; +} +.vp-pp-step-move-btn:hover { + background-color: var(--vp-light-gray-color); +} +.vp-pp-step-move-btn.disabled { + background-color: var(--vp-gray-color); + cursor: not-allowed; +} +.vp-pp-step-prev { + left: 270px; +} +.vp-pp-step-next{ + right: 15px; } \ No newline at end of file diff --git a/visualpython/data/m_ml/mlLibrary.js b/visualpython/data/m_ml/mlLibrary.js index 78528fed..beeae0ec 100644 --- a/visualpython/data/m_ml/mlLibrary.js +++ b/visualpython/data/m_ml/mlLibrary.js @@ -129,6 +129,7 @@ define([ name: 'OneHotEncoder', import: 'from sklearn.preprocessing import OneHotEncoder', code: 'OneHotEncoder(${sparse}${handle_unknown}${etc})', + returnType: 'OneHotEncoder', options: [ { name: 'sparse', component: ['bool_select'], default: 'False', usePair: true }, { name: 'handle_unknown', component: ['option_suggest'], usePair: true, @@ -139,6 +140,7 @@ define([ name: 'LabelEncoder', import: 'from sklearn.preprocessing import LabelEncoder', code: 'LabelEncoder()', + returnType: 'LabelEncoder', options: [ ] @@ -147,6 +149,7 @@ define([ name: 'OrdinalEncoder', import: 'from sklearn.preprocessing import OrdinalEncoder', code: 'OrdinalEncoder(${handle_unknown}${unknown_values}${etc})', + returnType: 'OrdinalEncoder', options: [ { name: 'handle_unknown', component: ['option_suggest'], usePair: true, options: ['error', 'use_encoded_value'], default: 'error' }, @@ -158,6 +161,7 @@ define([ install: '!pip install category_encoders', import: 'from category_encoders.target_encoder import TargetEncoder', code: 'TargetEncoder(${cols}${handle_missing}${handle_unknown}${smoothing}${etc})', + returnType: 'TargetEncoder', options: [ { name: 'cols', component: ['var_suggest', '1darr'], usePair: true }, { name: 'handle_missing', component: ['option_suggest'], usePair: true, @@ -172,6 +176,7 @@ define([ install: '!pip install imblearn', import: 'from imblearn.over_sampling import SMOTE', code: 'SMOTE(${random_state}${k_neighbors}${etc})', + returnType: 'SMOTE', options: [ { name: 'random_state', component: ['input_number'], placeholder: '123', usePair: true }, { name: 'k_neighbors', component: ['input_number'], default: 5, usePair: true } @@ -182,6 +187,7 @@ define([ name: 'StandardScaler', import: 'from sklearn.preprocessing import StandardScaler', code: 'StandardScaler(${with_mean}${with_std}${etc})', + returnType: 'StandardScaler', options: [ { name: 'with_mean', component: ['bool_select'], default: 'True', usePair: true }, { name: 'with_std', component: ['bool_select'], default: 'True', usePair: true } @@ -191,6 +197,7 @@ define([ name: 'RobustScaler', import: 'from sklearn.preprocessing import RobustScaler', code: 'RobustScaler(${with_centering}${with_scaling}${etc})', + returnType: 'RobustScaler', options: [ { name: 'with_centering', component: ['bool_select'], default: 'True', usePair: true }, { name: 'with_scaling', component: ['bool_select'], default: 'True', usePair: true } @@ -200,6 +207,7 @@ define([ name: 'MinMaxScaler', import: 'from sklearn.preprocessing import MinMaxScaler', code: 'MinMaxScaler(${feature_range}${etc})', + returnType: 'MinMaxScaler', options: [ { name: 'feature_range', component: ['input'], placeholder: '(min, max)', default: '(0, 1)', usePair: true } ] @@ -208,6 +216,7 @@ define([ name: 'Normalizer', import: 'from sklearn.preprocessing import Normalizer', code: 'Normalizer(${norm}${etc})', + returnType: 'Normalizer', options: [ { name: 'norm', component: ['option_suggest'], usePair: true, options: ['l1', 'l2', 'max'], default: 'l2' }, @@ -217,6 +226,7 @@ define([ name: 'Log Scaling', import: 'from sklearn.preprocessing import FunctionTransformer', code: 'FunctionTransformer(np.log1p${etc})', + returnType: 'FunctionTransformer', options: [ ] @@ -225,6 +235,7 @@ define([ name: 'Exponential Scaling', import: 'from sklearn.preprocessing import FunctionTransformer', code: 'FunctionTransformer(np.expm1${etc})', + returnType: 'FunctionTransformer', options: [ ] @@ -233,6 +244,7 @@ define([ name: 'Polynomial Features', import: 'from sklearn.preprocessing import PolynomialFeatures', code: 'PolynomialFeatures(${etc})', + returnType: 'PolynomialFeatures', options: [ ] @@ -241,6 +253,7 @@ define([ name: 'KBins Discretizer', import: 'from sklearn.preprocessing import KBinsDiscretizer', code: 'KBinsDiscretizer(${n_bins}${strategy}${encode}${etc})', + returnType: 'KBinsDiscretizer', options: [ { name: 'n_bins', component: ['input_number'], default: 5, usePair: true }, { name: 'strategy', component: ['option_select'], type: 'text', default: 'quantile', usePair: true, @@ -253,6 +266,7 @@ define([ name: 'SimpleImputer', import: 'from sklearn.impute import SimpleImputer', code: 'SimpleImputer(${missing_values}${strategy}${fill_value}${copy}${add_indicator}${etc})', + returnType: 'SimpleImputer', options: [ { name: 'missing_values', component: ['input'], placeholder: 'np.nan', usePair: true }, { name: 'strategy', component: ['option_select'], type: 'text', default: 'quantile', usePair: true, @@ -266,6 +280,7 @@ define([ name: 'MakeColumnTransformer', import: 'from sklearn.compose import make_column_transformer', code: 'make_column_transformer(${mct_code})', + returnType: 'ColumnTransformer', options: [ ] @@ -275,6 +290,7 @@ define([ name: 'LinearRegression', import: 'from sklearn.linear_model import LinearRegression', code: 'LinearRegression(${fit_intercept}${etc})', + returnType: 'LinearRegression', options: [ { name: 'fit_intercept', component: ['bool_select'], default: 'True', usePair: true } ] @@ -283,6 +299,7 @@ define([ name: 'Ridge', import: 'from sklearn.linear_model import Ridge', code: 'Ridge(${alpha}${etc})', + returnType: 'Ridge', options: [ { name: 'alpha', component: ['input_number'], default: 1.0, usePair: true } ] @@ -291,6 +308,7 @@ define([ name: 'Lasso', import: 'from sklearn.linear_model import Lasso', code: 'Lasso(${alpha}${etc})', + returnType: 'Lasso', options: [ { name: 'alpha', component: ['input_number'], default: 1.0, usePair: true } ] @@ -299,6 +317,7 @@ define([ name: 'ElasticNet', import: 'from sklearn.linear_model import ElasticNet', code: 'ElasticNet(${alpha}${l1_ratio}${etc})', + returnType: 'ElasticNet', options: [ { name: 'alpha', component: ['input_number'], default: 1.0, usePair: true }, { name: 'l1_ratio', component: ['input_number'], default: 0.5, usePair: true } @@ -308,6 +327,7 @@ define([ name: 'SVR', import: 'from sklearn.svm import SVR', code: 'SVR(${C}${kernel}${degree}${gamma}${coef0}${random_state}${etc})', + returnType: 'SVR', options: [ { name: 'C', component: ['input_number'], placeholder: '1.0', usePair: true, step: 0.1, min: 0 }, { name: 'kernel', component: ['option_select'], type: 'text', usePair: true, @@ -323,6 +343,7 @@ define([ name: 'DecisionTreeRegressor', import: 'from sklearn.tree import DecisionTreeRegressor', code: 'DecisionTreeRegressor(${criterion}${max_depth}${min_samples_split}${random_state}${etc})', + returnType: 'DecisionTreeRegressor', options: [ { name: 'criterion', component: ['option_select'], type: 'text', default: 'squared_error', type:'text', options: ['squared_error', 'friedman_mse', 'absolute_error', 'poisson'] }, @@ -335,6 +356,7 @@ define([ name: 'RandomForestRegressor', import: 'from sklearn.ensemble import RandomForestRegressor', code: 'RandomForestRegressor(${n_estimators}${criterion}${max_depth}${min_samples_split}${n_jobs}${random_state}${etc})', + returnType: 'RandomForestRegressor', options: [ { name: 'n_estimators', component: ['input_number'], default: 100, usePair: true }, { name: 'criterion', component: ['option_select'], type: 'text', default: 'squared_error', type:'text', usePair: true, @@ -349,6 +371,7 @@ define([ name: 'GradientBoostingRegressor', import: 'from sklearn.ensemble import GradientBoostingRegressor', code: 'GradientBoostingRegressor(${loss}${learning_rate}${n_estimators}${criterion}${random_state}${etc})', + returnType: 'GradientBoostingRegressor', options: [ { name: 'loss', component: ['option_select'], type: 'text', default: 'squared_error', type:'text', usePair: true, options: ['squared_error', 'absolute_error', 'huber', 'quantile'] }, @@ -364,6 +387,7 @@ define([ install: '!pip install xgboost', import: 'from xgboost import XGBRegressor', code: 'XGBRegressor(${n_estimators}${max_depth}${learning_rate}${gamma}${random_state}${etc})', + returnType: 'XGBRegressor', options: [ { name: 'n_estimators', component: ['input_number'], default: 100, usePair: true }, { name: 'max_depth', component: ['input_number'], placeholder: 'None', usePair: true }, @@ -377,6 +401,7 @@ define([ install: '!pip install lightgbm', import: 'from lightgbm import LGBMRegressor', code: 'LGBMRegressor(${boosting_type}${max_depth}${learning_rate}${n_estimators}${random_state}${etc})', + returnType: 'LGBMRegressor', options: [ { name: 'boosting_type', component: ['option_select'], type: 'text', default: 'gbdt', type: 'text', usePair: true, options: ['gbdt', 'dart', 'goss', 'rf']}, @@ -391,6 +416,7 @@ define([ install: '!pip install catboost', import: 'from catboost import CatBoostRegressor', code: 'CatBoostRegressor(${learning_rate}${loss_function}${task_type}${max_depth}${n_estimators}${random_state}${etc})', + returnType: 'CatBoostRegressor', options: [ { name: 'learning_rate', component: ['input_number'], placeholder: 'None', usePair: true }, { name: 'loss_function', component: ['option_select'], type: 'text', default: 'RMSE', type:'text', usePair: true, @@ -407,6 +433,7 @@ define([ name: 'LogisticRegression', import: 'from sklearn.linear_model import LogisticRegression', code: 'LogisticRegression(${penalty}${C}${random_state}${etc})', + returnType: 'LogisticRegression', options: [ { name: 'penalty', component: ['option_select'], type: 'text', default: 'l2', usePair: true, options: ['l1', 'l2', 'elasticnet', 'none']}, { name: 'C', component: ['input_number'], placeholder: '1.0', usePair: true, step: 0.1, min: 0 }, @@ -417,6 +444,7 @@ define([ name: 'BernoulliNB', import: 'from sklearn.naive_bayes import BernoulliNB', code: 'BernoulliNB(${etc})', + returnType: 'BernoulliNB', options: [ //TODO: ] @@ -425,6 +453,7 @@ define([ name: 'MultinomialNB', import: 'from sklearn.naive_bayes import MultinomialNB', code: 'MultinomialNB(${etc})', + returnType: 'MultinomialNB', options: [ //TODO: ] @@ -433,6 +462,7 @@ define([ name: 'GaussianNB', import: 'from sklearn.naive_bayes import GaussianNB', code: 'GaussianNB(${etc})', + returnType: 'GaussianNB', options: [ //TODO: ] @@ -441,6 +471,7 @@ define([ name: 'SVC', import: 'from sklearn.svm import SVC', code: 'SVC(${C}${kernel}${degree}${gamma}${coef0}${random_state}${etc})', + returnType: 'SVC', options: [ { name: 'C', component: ['input_number'], placeholder: '1.0', usePair: true, step: 0.1, min: 0 }, { name: 'kernel', component: ['option_select'], type: 'text', usePair: true, @@ -456,6 +487,7 @@ define([ name: 'DecisionTreeClassifier', import: 'from sklearn.tree import DecisionTreeClassifier', code: 'DecisionTreeClassifier(${criterion}${max_depth}${min_samples_split}${random_state}${etc})', + returnType: 'DecisionTreeClassifier', options: [ { name: 'criterion', component: ['option_select'], type: 'text', default: 'squared_error', type:'text', options: ['squared_error', 'friedman_mse', 'absolute_error', 'poisson'], usePair: true }, @@ -468,6 +500,7 @@ define([ name: 'RandomForestClassifier', import: 'from sklearn.ensemble import RandomForestClassifier', code: 'RandomForestClassifier(${n_estimators}${criterion}${max_depth}${min_samples_split}${n_jobs}${random_state}${etc})', + returnType: 'RandomForestClassifier', options: [ { name: 'n_estimators', component: ['input_number'], default: 100, usePair: true }, { name: 'criterion', component: ['option_select'], type: 'text', default: 'gini', type:'text', usePair: true, @@ -482,6 +515,7 @@ define([ name: 'GradientBoostingClassifier', import: 'from sklearn.ensemble import GradientBoostingClassifier', code: 'GradientBoostingClassifier(${loss}${learning_rate}${n_estimators}${criterion}${random_state}${etc})', + returnType: 'GradientBoostingClassifier', options: [ { name: 'loss', component: ['option_select'], type: 'text', default: 'deviance', type: 'text', usePair: true, options: ['deviance', 'exponential'] }, @@ -497,6 +531,7 @@ define([ install: '!pip install xgboost', import: 'from xgboost import XGBClassifier', code: 'XGBClassifier(${n_estimators}${max_depth}${learning_rate}${gamma}${random_state}${etc})', + returnType: 'XGBClassifier', options: [ { name: 'n_estimators', component: ['input_number'], default: 100, usePair: true }, { name: 'max_depth', component: ['input_number'], placeholder: 'None', usePair: true }, @@ -510,6 +545,7 @@ define([ install: '!pip install lightgbm', import: 'from lightgbm import LGBMClassifier', code: 'LGBMClassifier(${boosting_type}${max_depth}${learning_rate}${n_estimators}${random_state}${etc})', + returnType: 'LGBMClassifier', options: [ { name: 'boosting_type', component: ['option_select'], type: 'text', default: 'gbdt', type: 'text', usePair: true, options: ['gbdt', 'dart', 'goss', 'rf']}, @@ -524,6 +560,7 @@ define([ install: '!pip install catboost', import: 'from catboost import CatBoostClassifier', code: 'CatBoostClassifier(${learning_rate}${loss_function}${task_type}${max_depth}${n_estimators}${random_state}${etc})', + returnType: 'CatBoostClassifier', options: [ { name: 'learning_rate', component: ['input_number'], placeholder: 'None', usePair: true }, { name: 'loss_function', component: ['option_select'], type: 'text', default: 'RMSE', type:'text', usePair: true, @@ -542,6 +579,7 @@ define([ import: 'from autosklearn.regression import AutoSklearnRegressor', link: 'https://automl.github.io/auto-sklearn/master/api.html#regression', code: 'AutoSklearnRegressor(${etc})', + returnType: 'AutoSklearnRegressor', options: [ ] @@ -551,6 +589,7 @@ define([ install: '!pip install tpot', import: 'from tpot import TPOTRegressor', code: 'TPOTRegressor(${generation}${population_size}${cv}${random_state}${etc})', + returnType: 'TPOTRegressor', options: [ { name: 'generation', component: ['input_number'], default: 100, usePair: true }, { name: 'population_size', component: ['input_number'], default: 100, usePair: true }, @@ -564,6 +603,7 @@ define([ import: 'from autosklearn.classification import AutoSklearnClassifier', link: 'https://automl.github.io/auto-sklearn/master/api.html#classification', code: 'AutoSklearnClassifier(${etc})', + returnType: 'AutoSklearnClassifier', options: [ ] @@ -573,6 +613,7 @@ define([ install: '!pip install tpot', import: 'from tpot import TPOTClassifier', code: 'TPOTClassifier(${generation}${population_size}${cv}${random_state}${etc})', + returnType: 'TPOTClassifier', options: [ { name: 'generation', component: ['input_number'], default: 100, usePair: true }, { name: 'population_size', component: ['input_number'], default: 100, usePair: true }, @@ -585,6 +626,7 @@ define([ name: 'KMeans', import: 'from sklearn.cluster import KMeans', code: 'KMeans(${n_clusters}${random_state}${etc})', + returnType: 'KMeans', options: [ { name: 'n_clusters', component: ['input_number'], default: 8, usePair: true }, { name: 'random_state', component: ['input_number'], placeholder: '123', usePair: true } @@ -594,6 +636,7 @@ define([ name: 'AgglomerativeClustering', import: 'from sklearn.cluster import AgglomerativeClustering', code: 'AgglomerativeClustering(${n_clusters}${random_state}${etc})', + returnType: 'AgglomerativeClustering', options: [ { name: 'n_clusters', component: ['input_number'], default: 2, usePair: true }, { name: 'random_state', component: ['input_number'], placeholder: '123', usePair: true } @@ -603,6 +646,7 @@ define([ name: 'GaussianMixture', import: 'from sklearn.mixture import GaussianMixture', code: 'GaussianMixture(${n_components}${random_state}${etc})', + returnType: 'GaussianMixture', options: [ { name: 'n_components', component: ['input_number'], default: 1, usePair: true }, { name: 'random_state', component: ['input_number'], placeholder: '123', usePair: true } @@ -612,6 +656,7 @@ define([ name: 'DBSCAN', import: 'from sklearn.cluster import DBSCAN', code: 'DBSCAN(${eps}${min_samples}${etc})', + returnType: 'DBSCAN', options: [ { name: 'eps', component: ['input_number'], default: 0.5, usePair: true }, { name: 'min_samples', component: ['input_number'], default: 5, usePair: true } @@ -622,6 +667,7 @@ define([ name: 'PCA(Principal Component Analysis)', import: 'from sklearn.decomposition import PCA', code: 'PCA(${n_components}${random_state}${etc})', + returnType: 'PCA', options: [ { name: 'n_components', component: ['input_number'], placeholder: 'None', usePair: true }, { name: 'random_state', component: ['input_number'], placeholder: '123', usePair: true } @@ -631,6 +677,7 @@ define([ name: 'LDA(Linear Discriminant Analysis)', import: 'from sklearn.discriminant_analysis import LinearDiscriminantAnalysis', code: 'LinearDiscriminantAnalysis(${n_components}${etc})', + returnType: 'LinearDiscriminantAnalysis', options: [ { name: 'n_components', component: ['input_number'], placeholder: 'None', usePair: true } ] @@ -639,6 +686,7 @@ define([ name: 'Truncated SVD', import: 'from sklearn.decomposition import TruncatedSVD', code: 'TruncatedSVD(${n_components}${random_state}${etc})', + returnType: 'TruncatedSVD', options: [ { name: 'n_components', component: ['input_number'], default: 2, usePair: true }, { name: 'random_state', component: ['input_number'], placeholder: '123', usePair: true } @@ -648,6 +696,7 @@ define([ name: 'NMF(Non-Negative Matrix Factorization)', import: 'from sklearn.decomposition import NMF', code: 'NMF(${n_components}${random_state}${etc})', + returnType: 'NMF', options: [ { name: 'n_components', component: ['input_number'], placeholder: 'None', usePair: true }, { name: 'random_state', component: ['input_number'], placeholder: '123', usePair: true } @@ -657,6 +706,7 @@ define([ name: 'TSNE(T-distributed Stochastic Neighbor Embedding)', import: 'from sklearn.manifold import TSNE', code: 'TSNE(${n_components}${learning_rate}${random_state}${etc})', + returnType: 'TSNE', options: [ { name: 'n_components', component: ['input_number'], placeholder: 'None', usePair: true }, { name: 'learning_rate', component: ['input_number'], default: 200.0, usePair: true }, @@ -667,7 +717,7 @@ define([ 'grid-search': { name: 'GridSearch', import: 'from sklearn.model_selection import GridSearchCV', - code: 'GridSearchCV(${estimator}, ${param_grid}${n_jobs}${cv}${verbose}${etc})', + returnType: 'GridSearchCV', options: [ { name: 'estimator', component: ['data_select'], placeholder: 'Select model'}, { name: 'param_grid', component: ['input'], placeholder: 'Enter parameters'}, diff --git a/visualpython/html/m_ml/pipeline.html b/visualpython/html/m_ml/pipeline.html index 464f27fa..f9d5570e 100644 --- a/visualpython/html/m_ml/pipeline.html +++ b/visualpython/html/m_ml/pipeline.html @@ -20,6 +20,10 @@

+ \ No newline at end of file diff --git a/visualpython/js/m_ml/Pipeline.js b/visualpython/js/m_ml/Pipeline.js index da0f7181..49e75265 100644 --- a/visualpython/js/m_ml/Pipeline.js +++ b/visualpython/js/m_ml/Pipeline.js @@ -107,6 +107,7 @@ define([ { name: 'ml_clustering', label: 'Clustering', useApp: true, child: ['pp_fit', 'pp_predict', 'pp_transform'] }, { name: 'pp_fit', label: 'Fit' }, { name: 'pp_predict', label: 'Predict' }, + { name: 'pp_fit_predict', label: 'Fit and Predict' }, { name: 'pp_transform', label: 'Transform' }, { name: 'ml_evaluation', label: 'Evaluation', useApp: true, state: { modelType: 'cls' } }, ] @@ -117,7 +118,8 @@ define([ step: [ { name: 'ml_dimensionReduction', label: 'Dimension Reduction', useApp: true, child: ['pp_fit', 'pp_transform'] }, { name: 'pp_fit', label: 'Fit' }, - { name: 'pp_transform', label: 'Transform' } + { name: 'pp_transform', label: 'Transform' }, + { name: 'pp_fit_transform', label: 'Fit and Transform' }, ] }, 'gridSearch': { @@ -184,6 +186,8 @@ define([ $(that.wrapSelector('.vp-pp-step-content')).html(''); $(that.wrapSelector('.vp-pp-step-title')).text(''); that.state.pipeline = []; + $(that.wrapSelector('.vp-pp-step-prev')).addClass('disabled'); + $(that.wrapSelector('.vp-pp-step-next')).addClass('disabled'); that.handleChangeTemplate(type); }); @@ -192,7 +196,7 @@ define([ $(document).on('click', this.wrapSelector('.vp-pp-item[data-flag="enabled"]'), function() { if (!$(this).hasClass('selected')) { let title = $(this).data('label'); - let stepSeq = parseInt($(this).data('seq')); // 0 ~ n + let stepSeq = parseInt($(this).data('step')); // 0 ~ n let name = $(this).data('name'); let ppObj = that.state.pipeline[stepSeq]; // set title @@ -210,6 +214,20 @@ define([ // check selected $(that.wrapSelector('.vp-pp-item')).removeClass('selected'); $(this).addClass('selected'); + + // check prev/next + let prevTagList = $(this).prevAll('.vp-pp-item[data-flag="enabled"]:visible'); + let nextTagList = $(this).nextAll('.vp-pp-item[data-flag="enabled"]:visible'); + if (prevTagList.length == 0) { + $(that.wrapSelector('.vp-pp-step-prev')).addClass('disabled'); + } else { + $(that.wrapSelector('.vp-pp-step-prev')).removeClass('disabled'); + } + if (nextTagList.length == 0) { + $(that.wrapSelector('.vp-pp-step-next')).addClass('disabled'); + } else { + $(that.wrapSelector('.vp-pp-step-next')).removeClass('disabled'); + } } }); @@ -232,6 +250,25 @@ define([ $(that.wrapSelector('.vp-pp-step-title')).text(''); $(that.wrapSelector(`.vp-pp-step-page[data-name="${name}"]`)).hide(); $(itemTag).removeClass('selected'); + + // disable prev/next + $(that.wrapSelector('.vp-pp-step-prev')).addClass('disabled'); + $(that.wrapSelector('.vp-pp-step-next')).addClass('disabled'); + } else { + // check prev/next + let selectedTag = $(that.wrapSelector('.vp-pp-item.selected')); + let prevTagList = $(selectedTag).prevAll('.vp-pp-item[data-flag="enabled"]:visible'); + let nextTagList = $(selectedTag).nextAll('.vp-pp-item[data-flag="enabled"]:visible'); + if (prevTagList.length == 0) { + $(that.wrapSelector('.vp-pp-step-prev')).addClass('disabled'); + } else { + $(that.wrapSelector('.vp-pp-step-prev')).removeClass('disabled'); + } + if (nextTagList.length == 0) { + $(that.wrapSelector('.vp-pp-step-next')).addClass('disabled'); + } else { + $(that.wrapSelector('.vp-pp-step-next')).removeClass('disabled'); + } } }); @@ -239,26 +276,75 @@ define([ // model type change event $(document).on('change', this.wrapSelector(`#modelType`), function() { let name = $(this).closest('.vp-pp-step-page').data('name'); - - let modelType = $(this).val(); - let modelObj = that.modelConfig[modelType]; - let modelTypeName = modelObj.code.split('(')[0]; - - that.state.modelType = modelType; - that.state.modelTypeName = modelTypeName; - - // show fit / predict / transform depends on model selection - let defaultActions = ['fit', 'predict', 'transform']; - let actions = that.modelEditor.getAction(modelTypeName); - defaultActions.forEach(actKey => { - if (actions[actKey] === undefined) { - // if undefined, hide step - $(that.wrapSelector(`.vp-pp-item[data-name="pp_${actKey}"]`)).hide(); - } else { - $(that.wrapSelector(`.vp-pp-item[data-name="pp_${actKey}"]`)).show(); + let modelCreatorList = ['ml_dataPrep', 'ml_regression', 'ml_classification', 'ml_clustering', 'ml_dimensionReduction', 'ml_gridSearch']; + if (modelCreatorList.includes(name)) { + let modelType = $(this).val(); + let modelObj = that.modelConfig[modelType]; + let modelTypeName = modelObj.returnType; + + that.state.modelType = modelType; + that.state.modelTypeName = modelTypeName; + + // show fit / predict / transform depends on model selection + let defaultActions = ['fit', 'predict', 'transform', 'fit_predict', 'fit_transform']; + let actions = that.modelEditor.getAction(modelTypeName); + defaultActions.forEach(actKey => { + if (actions[actKey] === undefined) { + // if undefined, hide step + $(that.wrapSelector(`.vp-pp-item[data-name="pp_${actKey}"]`)).hide(); + } else { + $(that.wrapSelector(`.vp-pp-item[data-name="pp_${actKey}"]`)).show(); + if (actKey === 'fit_predict') { + if (actions['fit'] === undefined || actions['predict'] === undefined) { + $(that.wrapSelector(`.vp-pp-item[data-name="pp_fit"]`)).hide(); + $(that.wrapSelector(`.vp-pp-item[data-name="pp_predict"]`)).hide(); + } else { + $(that.wrapSelector(`.vp-pp-item[data-name="pp_${actKey}"]`)).hide(); + } + } else if (actKey === 'fit_transform') { + if (actions['fit'] === undefined || actions['transform'] === undefined) { + $(that.wrapSelector(`.vp-pp-item[data-name="pp_fit"]`)).hide(); + $(that.wrapSelector(`.vp-pp-item[data-name="pp_transform"]`)).hide(); + } else { + $(that.wrapSelector(`.vp-pp-item[data-name="pp_${actKey}"]`)).hide(); + } + } + } + $(that.wrapSelector('.vp-pp-item')).removeClass('vp-last-visible'); + $(that.wrapSelector('.vp-pp-item:visible:last')).addClass('vp-last-visible'); + }); + + // change evaluation page's default selection + if (name === 'ml_gridSearch') { + let modelCategory = that.modelEditor.getModelCategory(modelTypeName); // Regression / Classification / Clustering + let evalModelTypeValue = 'rgs'; + switch (modelCategory) { + case 'Regression': + evalModelTypeValue = 'rgs'; + break; + case 'Classification': + evalModelTypeValue = 'clf'; + break; + case 'Clustering': + evalModelTypeValue = 'cls'; + break; + } + let evalPageTag = $(that.wrapSelector('.vp-pp-step-page[data-name="ml_evaluation"]')); + if (evalPageTag.is(':empty') === true) { + // evaluation page is not opened yet + // set state for evaluation page + let stepSeq = $(evalPageTag).data('step'); + let ppObj = that.state.pipeline[stepSeq]; + if (ppObj.app) { + ppObj.app.state.modelType = evalModelTypeValue; + } + } else { + let evalModelTypeTag = $(evalPageTag).find('#modelType'); + $(evalModelTypeTag).val(evalModelTypeValue); + $(evalModelTypeTag).trigger('change'); + } } - }); - + } }); // model allocation variable change @@ -267,6 +353,20 @@ define([ let modelAllocation = $(this).val(); that.state.model = modelAllocation; }); + + // click prev button + $(this.wrapSelector('.vp-pp-step-prev:not(.disabled)')).on('click', function() { + let selectedTag = $(that.wrapSelector('.vp-pp-item.selected')); + let prevTagList = $(selectedTag).prevAll('.vp-pp-item[data-flag="enabled"]:visible'); + $(prevTagList[0]).trigger('click'); + }); + + // click next button + $(this.wrapSelector('.vp-pp-step-next:not(.disabled)')).on('click', function() { + let selectedTag = $(that.wrapSelector('.vp-pp-item.selected')); + let nextTagList = $(selectedTag).nextAll('.vp-pp-item[data-flag="enabled"]:visible') + $(nextTagList[0]).trigger('click'); + }); } /** @@ -284,7 +384,7 @@ define([ // load pipeline items tplObj.step.forEach((stepObj, idx) => { let { name, label, useApp=false, child=[], state={} } = stepObj; - ppTag.appendFormatLine(`
+ ppTag.appendFormatLine(`
{3}
@@ -354,7 +454,8 @@ define([ that.handleAppView(name, mlComponent); // select first step - $(that.wrapSelector('.vp-pp-item[data-seq="0"]')).click(); + $(that.wrapSelector('.vp-pp-item[data-step="0"]')).click(); + $(that.wrapSelector('#modelType')).trigger('change'); // end of DUP AREA: pp-1 } }); @@ -390,7 +491,8 @@ define([ that.handleAppView(name, mlComponent); // select first step - $(that.wrapSelector('.vp-pp-item[data-seq="0"]')).click(); + $(that.wrapSelector('.vp-pp-item[data-step="0"]')).click(); + $(that.wrapSelector('#modelType')).trigger('change'); // end of DUP AREA: pp-1 } }) @@ -436,6 +538,9 @@ define([ // load state if (this.state.templateType !== '') { $(this.wrapSelector('#templateType')).trigger('change'); + } else { + $(this.wrapSelector('.vp-pp-step-prev')).addClass('disabled'); + $(this.wrapSelector('.vp-pp-step-next')).addClass('disabled'); } } @@ -469,6 +574,12 @@ define([ case 'pp_transform': tag = this.templateForOptionPage(actions['transform']); break; + case 'pp_fit_predict': + tag = this.templateForOptionPage(actions['fit_predict']); + break; + case 'pp_fit_transform': + tag = this.templateForOptionPage(actions['fit_transform']); + break; } $(this.wrapSelector(`.vp-pp-step-page[data-name="${appId}"]`)).html(`
${tag}
@@ -505,8 +616,8 @@ define([ var { name, label, useApp, app } = ppObj; let requiredList = []; result = true; - let isVisible = $(that.wrapSelector(`.vp-pp-item[data-seq="${idx}"]`)).is(':visible') === true; - let isEnabled = $(that.wrapSelector(`.vp-pp-item[data-seq="${idx}"]`)).attr('data-flag') === 'enabled'; + let isVisible = $(that.wrapSelector(`.vp-pp-item[data-step="${idx}"]`)).is(':visible') === true; + let isEnabled = $(that.wrapSelector(`.vp-pp-item[data-step="${idx}"]`)).attr('data-flag') === 'enabled'; if (isVisible && isEnabled) { switch (name) { case 'ml_dataSplit': @@ -563,6 +674,12 @@ define([ case 'pp_transform': actObj = actions['transform']; break; + case 'pp_fit_predict': + actObj = actions['fit_predict']; + break; + case 'pp_fit_transform': + actObj = actions['fit_transform']; + break; } let code = new com_String(); @@ -597,8 +714,8 @@ define([ let { name, label, useApp, app } = ppObj; // check disabled - let isVisible = $(that.wrapSelector(`.vp-pp-item[data-seq="${idx}"]`)).is(':visible') === true; - let isEnabled = $(that.wrapSelector(`.vp-pp-item[data-seq="${idx}"]`)).attr('data-flag') === 'enabled'; + let isVisible = $(that.wrapSelector(`.vp-pp-item[data-step="${idx}"]`)).is(':visible') === true; + let isEnabled = $(that.wrapSelector(`.vp-pp-item[data-step="${idx}"]`)).attr('data-flag') === 'enabled'; if (isVisible && isEnabled) { if (code.toString() !== '') { code.appendLine(); From 1aa5cf8eb20f84e23ee9cd4b3af4f84ff2b8e0ae Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Wed, 30 Aug 2023 13:02:37 +0900 Subject: [PATCH 02/26] Add scoring option to GridSearch --- visualpython/data/m_ml/mlLibrary.js | 2 + visualpython/html/m_ml/gridSearch.html | 2 +- visualpython/js/m_ml/GridSearch.js | 62 +++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/visualpython/data/m_ml/mlLibrary.js b/visualpython/data/m_ml/mlLibrary.js index beeae0ec..5268cdb4 100644 --- a/visualpython/data/m_ml/mlLibrary.js +++ b/visualpython/data/m_ml/mlLibrary.js @@ -717,10 +717,12 @@ define([ 'grid-search': { name: 'GridSearch', import: 'from sklearn.model_selection import GridSearchCV', + code: 'GridSearchCV(${estimator}, ${param_grid}${scoring}${n_jobs}${cv}${verbose}${etc})', returnType: 'GridSearchCV', options: [ { name: 'estimator', component: ['data_select'], placeholder: 'Select model'}, { name: 'param_grid', component: ['input'], placeholder: 'Enter parameters'}, + { name: 'scoring', component: ['input'], placeholder: 'None', usePair: true }, // https://scikit-learn.org/stable/modules/model_evaluation.html#scoring-parameter { name: 'n_jobs', component: ['input'], placeholder: 'None', usePair: true }, { name: 'cv', component: ['input'], placeholder: 'None', usePair: true }, { name: 'verbose', component: ['input_number'], placeholder: 'Input number', usePair: true } diff --git a/visualpython/html/m_ml/gridSearch.html b/visualpython/html/m_ml/gridSearch.html index 976cd69c..ee276db9 100644 --- a/visualpython/html/m_ml/gridSearch.html +++ b/visualpython/html/m_ml/gridSearch.html @@ -22,7 +22,6 @@
-
@@ -36,6 +35,7 @@
+
\ No newline at end of file diff --git a/visualpython/js/m_ml/GridSearch.js b/visualpython/js/m_ml/GridSearch.js index dd1a1eb5..32dbc967 100644 --- a/visualpython/js/m_ml/GridSearch.js +++ b/visualpython/js/m_ml/GridSearch.js @@ -81,6 +81,8 @@ define([ that.hideInstallButton(); } + that.handleScoringOptions(modelType); + // reset model param set $(that.wrapSelector('.vp-param-grid-box')).html(''); $(that.wrapSelector('.vp-param-grid-box')).html(that.templateForParamSet()); @@ -140,6 +142,10 @@ define([ let parentTag = $(thisTag).parent(); let paramIsText = $(parentTag).find('.vp-param-val').data('type') === 'text'; // text / var let paramVal = $(parentTag).find('.vp-param-val').val(); + let reservedKeywordList = ['None', 'True', 'False', 'np.nan', 'np.NaN']; + if (reservedKeywordList.includes(paramVal)) { + paramIsText = false; + } // check , and split it let paramSplit = paramVal.split(','); paramSplit && paramSplit.forEach(val => { @@ -195,6 +201,58 @@ define([ }); } + handleScoringOptions(modelType) { + let options = { + 'Classification': [ + "'accuracy'", + "'balanced_accuracy'", + "'top_k_accuracy'", + "'average_precision'", + "'neg_brier_score'", + "'f1'", + "'f1_micro'", + "'f1_macro'", + "'f1_weighted'", + "'f1_samples'", + "'neg_log_loss'", + "'precision' etc.", + "'recall' etc.", + "'jaccard' etc.", + "'roc_auc'", + "'roc_auc_ovr'", + "'roc_auc_ovo'", + "'roc_auc_ovr_weighted'", + "'roc_auc_ovo_weighted'", + ], + 'Regression': [ + "'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'", + "'d2_absolute_error_score'", + "'d2_pinball_score'", + "'d2_tweedie_score'" + ] + } + let modelCategory = this.modelTypeList['Regression'].includes(modelType)?'Regression':'Classification'; + + // Set suggestInput on scoring option + var suggestInput = new SuggestInput(); + suggestInput.setComponentID('scoring'); + suggestInput.setPlaceholder('Select option'); + suggestInput.addClass('vp-input vp-state'); + suggestInput.setSuggestList(function() { return options[modelCategory]; }); + suggestInput.setNormalFilter(true); + $(this.wrapSelector('#scoring')).replaceWith(suggestInput.toTagString()); + } + templateForParamSet() { let paramSetNo = 1; // set param set number @@ -378,6 +436,7 @@ define([ let thisTag = $(that.wrapSelector('.' + suggestInput.uuid)); that.handleAddParamValue($(thisTag)); $(thisTag).val(''); + return false; }); paramSet.appendLine(suggestInput.toTagString()); } @@ -394,6 +453,8 @@ define([ // Model Editor this.modelEditor = new ModelEditor(this, "model", "instanceEditor"); + + this.handleScoringOptions(this.state.modelType); } generateInstallCode() { @@ -432,7 +493,6 @@ define([ state['estimator'] = estimator; state['param_grid'] = '{}'; - let reservedKeywordList = ['None', 'True', 'False', 'np.nan', 'np.NaN']; let paramGrid = []; // generate param_grid $(this.wrapSelector('.vp-param-set-box')).each((i, tag) => { From d3434e9b0b2a98b62829e698313e8b70bc854bf4 Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Wed, 30 Aug 2023 13:03:28 +0900 Subject: [PATCH 03/26] Edit SaveLoad to show model types for target and Fixed labels --- visualpython/data/m_ml/mlLibrary.js | 12 ++++++------ visualpython/js/m_ml/SaveLoad.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/visualpython/data/m_ml/mlLibrary.js b/visualpython/data/m_ml/mlLibrary.js index 5268cdb4..e5bf2205 100644 --- a/visualpython/data/m_ml/mlLibrary.js +++ b/visualpython/data/m_ml/mlLibrary.js @@ -732,19 +732,19 @@ define([ 'model_save': { name: 'Save model', import: 'import joblib', - code: "joblib.dump(${target}, ${savePath}${etc})", + code: "joblib.dump(${target}, ${save_path}${etc})", options: [ - { name: 'target', component: ['data_select'], placeholder: 'Select model'}, - { name: 'savePath', component: ['file-save'], placeholder: 'Select path'} + { name: 'target', component: ['data_select'], comp_type: 'ml', placeholder: 'Select model'}, + { name: 'save_path', component: ['file-save'], placeholder: 'Select path'} ] }, 'model_load': { name: 'Load model', import: 'import joblib', - code: "${allocateTo} = joblib.load(${loadPath}${etc})", + code: "${allocate_to} = joblib.load(${load_path}${etc})", options: [ - { name: 'loadPath', component: ['file-open'], placeholder: 'Select path'}, - { name: 'allocateTo', component: ['data_select'], placeholder: 'New variable to load'} + { name: 'load_path', component: ['file-open'], placeholder: 'Select path'}, + { name: 'allocate_to', component: ['input'], placeholder: 'New variable to load'} ] } } diff --git a/visualpython/js/m_ml/SaveLoad.js b/visualpython/js/m_ml/SaveLoad.js index 0940b028..454f468c 100644 --- a/visualpython/js/m_ml/SaveLoad.js +++ b/visualpython/js/m_ml/SaveLoad.js @@ -86,7 +86,7 @@ define([ // render tag config.options.forEach(opt => { optBox.appendFormatLine('' - , opt.name, opt.name, opt.name); + , 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); }); From d4e9c346f51c01f4ddcc09e71e216a81cdfe0eea Mon Sep 17 00:00:00 2001 From: minjk-bl Date: Wed, 30 Aug 2023 13:05:43 +0900 Subject: [PATCH 04/26] Edit Codeview, DataView not to save its content after hide --- .../html/component/popupComponent.html | 4 +- .../js/com/component/PopupComponent.js | 48 +++++++++++++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/visualpython/html/component/popupComponent.html b/visualpython/html/component/popupComponent.html index 825a51c9..4c1ddec2 100644 --- a/visualpython/html/component/popupComponent.html +++ b/visualpython/html/component/popupComponent.html @@ -91,11 +91,11 @@