diff --git a/apps/dash-svm/.gitignore b/apps/dash-svm/.gitignore new file mode 100644 index 000000000..1f65cd953 --- /dev/null +++ b/apps/dash-svm/.gitignore @@ -0,0 +1,192 @@ +# .gitignore specifies the files that shouldn't be included +# in version control and therefore shouldn't be included when +# deploying an application to Dash Enterprise +# This is a very exhaustive list! +# This list was based off of https://github.com/github/gitignore + +# Ignore data that is generated during the runtime of an application +# This folder is used by the "Large Data" sample applications +runtime_data/ +data/ + +# Omit SQLite databases that may be produced by dash-snapshots in development +*.db + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + + +# Jupyter Notebook + +.ipynb_checkpoints +*/.ipynb_checkpoints/* + +# IPython +profile_default/ +ipython_config.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +secrets.sh + +# Spyder project settings +.spyderproject +.spyproject + +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + + +# macOS General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# History files +.Rhistory +.Rapp.history + +# Session Data files +.RData + +# User-specific files +.Ruserdata + +# Example code in package build process +*-Ex.R + +# Output files from R CMD check +/*.Rcheck/ + +# RStudio files +.Rproj.user/ + +# produced vignettes +vignettes/*.html +vignettes/*.pdf + +# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 +.httr-oauth + +# knitr and R markdown default cache directories +*_cache/ +/cache/ + +# Temporary files created by R markdown +*.utf8.md +*.knit.md + +# R Environment Variables +.Renviron + +# Linux +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# SublineText +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings \ No newline at end of file diff --git a/apps/dash-svm/README.md b/apps/dash-svm/README.md index abd26f12d..57e73c28a 100644 --- a/apps/dash-svm/README.md +++ b/apps/dash-svm/README.md @@ -64,6 +64,7 @@ An SVM is a popular Machine Learning model used in many different fields. You ca * **Matthew Chan** - *Code Review* - [@matthewchan15](https://github.com/matthewchan15) * **Yunke Xiao** - *Redesign* - [@YunkeXiao](https://github.com/YunkeXiao) * **celinehuang** - *Code Review* - [@celinehuang](https://github.com/celinehuang) +* **Daniel Anton S** - *2022 complete refactor* - [@danton267](https://github.com/danton267) ## Acknowledgments diff --git a/apps/dash-svm/app.py b/apps/dash-svm/app.py index b17a5b623..c4be4880b 100644 --- a/apps/dash-svm/app.py +++ b/apps/dash-svm/app.py @@ -1,324 +1,84 @@ -import time -import importlib - -import dash -import dash_core_components as dcc -import dash_html_components as html +from dash import Dash, dcc, Input, Output, State +import dash_bootstrap_components as dbc import numpy as np -from dash.dependencies import Input, Output, State -from sklearn.model_selection import train_test_split -from sklearn.preprocessing import StandardScaler -from sklearn import datasets -from sklearn.svm import SVC -import utils.dash_reusable_components as drc -import utils.figures as figs +from utils.components import Header, controls_first, controls_second, controls_third +from utils.model import svm_prediction + -app = dash.Dash( +app = Dash( __name__, - meta_tags=[ - {"name": "viewport", "content": "width=device-width, initial-scale=1.0"} - ], + title = "Support Vector Machine", + external_stylesheets=[dbc.themes.CYBORG], ) -app.title = "Support Vector Machine" server = app.server - -def generate_data(n_samples, dataset, noise): - if dataset == "moons": - return datasets.make_moons(n_samples=n_samples, noise=noise, random_state=0) - - elif dataset == "circles": - return datasets.make_circles( - n_samples=n_samples, noise=noise, factor=0.5, random_state=1 - ) - - elif dataset == "linear": - X, y = datasets.make_classification( - n_samples=n_samples, - n_features=2, - n_redundant=0, - n_informative=2, - random_state=2, - n_clusters_per_class=1, - ) - - rng = np.random.RandomState(2) - X += noise * rng.uniform(size=X.shape) - linearly_separable = (X, y) - - return linearly_separable - - else: - raise ValueError( - "Data type incorrectly specified. Please choose an existing dataset." - ) - - -app.layout = html.Div( +app.layout = dbc.Container( children=[ - # .container class is fixed, .container.scalable is scalable - html.Div( - className="banner", - children=[ - # Change App Name here - html.Div( - className="container scalable", - children=[ - # Change App Name here - html.H2( - id="banner-title", - children=[ - html.A( - "Support Vector Machine (SVM) Explorer", - href="https://github.com/plotly/dash-svm", - style={ - "text-decoration": "none", - "color": "inherit", - }, - ) - ], - ), - html.A( - id="banner-logo", - children=[ - html.Img(src=app.get_asset_url("dash-logo-new.png")) - ], - href="https://plot.ly/products/dash/", - ), - ], - ) - ], - ), - html.Div( - id="body", - className="container scalable", - children=[ - html.Div( - id="app-container", - # className="row", - children=[ - html.Div( - # className="three columns", - id="left-column", - children=[ - drc.Card( - id="first-card", - children=[ - drc.NamedDropdown( - name="Select Dataset", - id="dropdown-select-dataset", - options=[ - {"label": "Moons", "value": "moons"}, - { - "label": "Linearly Separable", - "value": "linear", - }, - { - "label": "Circles", - "value": "circles", - }, - ], - clearable=False, - searchable=False, - value="moons", - ), - drc.NamedSlider( - name="Sample Size", - id="slider-dataset-sample-size", - min=100, - max=500, - step=100, - marks={ - str(i): str(i) - for i in [100, 200, 300, 400, 500] - }, - value=300, - ), - drc.NamedSlider( - name="Noise Level", - id="slider-dataset-noise-level", - min=0, - max=1, - marks={ - i / 10: str(i / 10) - for i in range(0, 11, 2) - }, - step=0.1, - value=0.2, - ), - ], - ), - drc.Card( - id="button-card", - children=[ - drc.NamedSlider( - name="Threshold", - id="slider-threshold", - min=0, - max=1, - value=0.5, - step=0.01, - ), - html.Button( - "Reset Threshold", - id="button-zero-threshold", - ), - ], - ), - drc.Card( - id="last-card", - children=[ - drc.NamedDropdown( - name="Kernel", - id="dropdown-svm-parameter-kernel", - options=[ - { - "label": "Radial basis function (RBF)", - "value": "rbf", - }, - {"label": "Linear", "value": "linear"}, - { - "label": "Polynomial", - "value": "poly", - }, - { - "label": "Sigmoid", - "value": "sigmoid", - }, - ], - value="rbf", - clearable=False, - searchable=False, - ), - drc.NamedSlider( - name="Cost (C)", - id="slider-svm-parameter-C-power", - min=-2, - max=4, - value=0, - marks={ - i: "{}".format(10 ** i) - for i in range(-2, 5) - }, - ), - drc.FormattedSlider( - id="slider-svm-parameter-C-coef", - min=1, - max=9, - value=1, - ), - drc.NamedSlider( - name="Degree", - id="slider-svm-parameter-degree", - min=2, - max=10, - value=3, - step=1, - marks={ - str(i): str(i) for i in range(2, 11, 2) - }, - ), - drc.NamedSlider( - name="Gamma", - id="slider-svm-parameter-gamma-power", - min=-5, - max=0, - value=-1, - marks={ - i: "{}".format(10 ** i) - for i in range(-5, 1) - }, - ), - drc.FormattedSlider( - id="slider-svm-parameter-gamma-coef", - min=1, - max=9, - value=5, - ), - html.Div( - id="shrinking-container", - children=[ - html.P(children="Shrinking"), - dcc.RadioItems( - id="radio-svm-parameter-shrinking", - labelStyle={ - "margin-right": "7px", - "display": "inline-block", - }, - options=[ - { - "label": " Enabled", - "value": "True", - }, - { - "label": " Disabled", - "value": "False", - }, - ], - value="True", - ), - ], - ), - ], - ), - ], - ), - html.Div( - id="div-graphs", - children=dcc.Graph( - id="graph-sklearn-svm", - figure=dict( - layout=dict( - plot_bgcolor="#282b38", paper_bgcolor="#282b38" - ) - ), - ), - ), - ], - ) - ], - ), - ] + Header(app, "Support Vector Machine (SVM) Explorer"), + dbc.Row([ + dbc.Col([ + dbc.Card(controls_first), + dbc.Card(controls_second), + dbc.Card(controls_third) + ], xl=2, lg=3, sm=4, xs=12, className="control-pannel"), + dbc.Col( + dcc.Loading(dcc.Graph(id="graph-sklearn-svm")), + xl=7, lg=6, sm=4, xs=12 + ), + dbc.Col( + dbc.Row([ + dcc.Loading(dcc.Graph(id="graph-line-roc-curve", style={'height': '40vh'})), + dcc.Loading(dcc.Graph(id="graph-pie-confusion-matrix", style={'height': '40vh'})), + ]), xl=3, lg=3, sm=4, xs=12 + ), + ], className="app-body") + ], + fluid=True ) @app.callback( Output("slider-svm-parameter-gamma-coef", "marks"), - [Input("slider-svm-parameter-gamma-power", "value")], + Input("slider-svm-parameter-gamma-power", "value"), ) def update_slider_svm_parameter_gamma_coef(power): scale = 10 ** power - return {i: str(round(i * scale, 8)) for i in range(1, 10, 2)} + if power < 1: + return {i: str(round(i * scale, 5)) for i in range(1, 10, 4)} + else: + return {i: str(int(i * scale)) for i in range(1, 10, 4)} @app.callback( Output("slider-svm-parameter-C-coef", "marks"), - [Input("slider-svm-parameter-C-power", "value")], + Input("slider-svm-parameter-C-power", "value"), ) def update_slider_svm_parameter_C_coef(power): scale = 10 ** power - return {i: str(round(i * scale, 8)) for i in range(1, 10, 2)} + if power < 1: + return {i: str(round(i * scale, 2)) for i in range(1, 10, 4)} + else: + return {i: str(int(i * scale)) for i in range(1, 10, 4)} @app.callback( Output("slider-threshold", "value"), - [Input("button-zero-threshold", "n_clicks")], - [State("graph-sklearn-svm", "figure")], + Input("button-zero-threshold", "n_clicks"), + State("graph-sklearn-svm", "figure"), ) def reset_threshold_center(n_clicks, figure): if n_clicks: Z = np.array(figure["data"][0]["z"]) value = -Z.min() / (Z.max() - Z.min()) else: - value = 0.4959986285375595 + value = 0.4 return value -# Disable Sliders if kernel not in the given list @app.callback( Output("slider-svm-parameter-degree", "disabled"), - [Input("dropdown-svm-parameter-kernel", "value")], + Input("dropdown-svm-parameter-kernel", "value"), ) def disable_slider_param_degree(kernel): return kernel != "poly" @@ -326,7 +86,7 @@ def disable_slider_param_degree(kernel): @app.callback( Output("slider-svm-parameter-gamma-coef", "disabled"), - [Input("dropdown-svm-parameter-kernel", "value")], + Input("dropdown-svm-parameter-kernel", "value"), ) def disable_slider_param_gamma_coef(kernel): return kernel not in ["rbf", "poly", "sigmoid"] @@ -334,122 +94,31 @@ def disable_slider_param_gamma_coef(kernel): @app.callback( Output("slider-svm-parameter-gamma-power", "disabled"), - [Input("dropdown-svm-parameter-kernel", "value")], + Input("dropdown-svm-parameter-kernel", "value"), ) def disable_slider_param_gamma_power(kernel): return kernel not in ["rbf", "poly", "sigmoid"] @app.callback( - Output("div-graphs", "children"), - [ - Input("dropdown-svm-parameter-kernel", "value"), - Input("slider-svm-parameter-degree", "value"), - Input("slider-svm-parameter-C-coef", "value"), - Input("slider-svm-parameter-C-power", "value"), - Input("slider-svm-parameter-gamma-coef", "value"), - Input("slider-svm-parameter-gamma-power", "value"), - Input("dropdown-select-dataset", "value"), - Input("slider-dataset-noise-level", "value"), - Input("radio-svm-parameter-shrinking", "value"), - Input("slider-threshold", "value"), - Input("slider-dataset-sample-size", "value"), - ], + Output("graph-sklearn-svm", "figure"), + Output("graph-line-roc-curve", "figure"), + Output("graph-pie-confusion-matrix", "figure"), + Input("dropdown-svm-parameter-kernel", "value"), + Input("slider-svm-parameter-degree", "value"), + Input("slider-svm-parameter-C-coef", "value"), + Input("slider-svm-parameter-C-power", "value"), + Input("slider-svm-parameter-gamma-coef", "value"), + Input("slider-svm-parameter-gamma-power", "value"), + Input("dropdown-select-dataset", "value"), + Input("slider-dataset-noise-level", "value"), + Input("radio-svm-parameter-shrinking", "value"), + Input("slider-threshold", "value"), + Input("slider-dataset-sample-size", "value"), ) -def update_svm_graph( - kernel, - degree, - C_coef, - C_power, - gamma_coef, - gamma_power, - dataset, - noise, - shrinking, - threshold, - sample_size, -): - t_start = time.time() - h = 0.3 # step size in the mesh - - # Data Pre-processing - X, y = generate_data(n_samples=sample_size, dataset=dataset, noise=noise) - X = StandardScaler().fit_transform(X) - X_train, X_test, y_train, y_test = train_test_split( - X, y, test_size=0.4, random_state=42 - ) - - x_min = X[:, 0].min() - 0.5 - x_max = X[:, 0].max() + 0.5 - y_min = X[:, 1].min() - 0.5 - y_max = X[:, 1].max() + 0.5 - xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) - - C = C_coef * 10 ** C_power - gamma = gamma_coef * 10 ** gamma_power - - if shrinking == "True": - flag = True - else: - flag = False - - # Train SVM - clf = SVC(C=C, kernel=kernel, degree=degree, gamma=gamma, shrinking=flag) - clf.fit(X_train, y_train) - - # Plot the decision boundary. For that, we will assign a color to each - # point in the mesh [x_min, x_max]x[y_min, y_max]. - if hasattr(clf, "decision_function"): - Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) - else: - Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1] - - prediction_figure = figs.serve_prediction_plot( - model=clf, - X_train=X_train, - X_test=X_test, - y_train=y_train, - y_test=y_test, - Z=Z, - xx=xx, - yy=yy, - mesh_step=h, - threshold=threshold, - ) - - roc_figure = figs.serve_roc_curve(model=clf, X_test=X_test, y_test=y_test) - - confusion_figure = figs.serve_pie_confusion_matrix( - model=clf, X_test=X_test, y_test=y_test, Z=Z, threshold=threshold - ) - - return [ - html.Div( - id="svm-graph-container", - children=dcc.Loading( - className="graph-wrapper", - children=dcc.Graph(id="graph-sklearn-svm", figure=prediction_figure), - style={"display": "none"}, - ), - ), - html.Div( - id="graphs-container", - children=[ - dcc.Loading( - className="graph-wrapper", - children=dcc.Graph(id="graph-line-roc-curve", figure=roc_figure), - ), - dcc.Loading( - className="graph-wrapper", - children=dcc.Graph( - id="graph-pie-confusion-matrix", figure=confusion_figure - ), - ), - ], - ), - ] +def update_svm_graph(kernel, degree, C_coef, C_power, gamma_coef, gamma_power, dataset, noise, shrinking, threshold, sample_size): + return svm_prediction(kernel, degree, C_coef, C_power, gamma_coef, gamma_power, dataset, noise, shrinking, threshold, sample_size) -# Running the server if __name__ == "__main__": app.run_server(debug=True) diff --git a/apps/dash-svm/assets/base-styles.css b/apps/dash-svm/assets/base-styles.css deleted file mode 100644 index 37e03533a..000000000 --- a/apps/dash-svm/assets/base-styles.css +++ /dev/null @@ -1,393 +0,0 @@ -/* Table of contents -–––––––––––––––––––––––––––––––––––––––––––––––––– -- Grid -- Base Styles -- Typography -- Links -- Buttons -- Forms -- Lists -- Code -- Tables -- Spacing -- Utilities -- Clearing -- Media Queries -*/ - - -/* Grid -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -.container { - position: relative; - width: 100%; - max-width: 960px; - margin: 0 auto; - padding: 0 20px; - box-sizing: border-box; } -.column, -.columns { - width: 100%; - float: left; - box-sizing: border-box; } - -/* For devices larger than 400px */ -@media (min-width: 400px) { - .container { - width: 85%; - padding: 0; } -} - -/* For devices larger than 550px */ -@media (min-width: 550px) { - .container { - width: 80%; } - .column, - .columns { - margin-left: 0.5%; } - .column:first-child, - .columns:first-child { - margin-left: 0; } - - .one.column, - .one.columns { width: 8%; } - .two.columns { width: 16.25%; } - .three.columns { width: 22%; } - .four.columns { width: 33%; } - .five.columns { width: 39.3333333333%; } - .six.columns { width: 49.75%; } - .seven.columns { width: 56.6666666667%; } - .eight.columns { width: 66.5%; } - .nine.columns { width: 74.0%; } - .ten.columns { width: 82.6666666667%; } - .eleven.columns { width: 91.5%; } - .twelve.columns { width: 100%; margin-left: 0; } - - .one-third.column { width: 30.6666666667%; } - .two-thirds.column { width: 65.3333333333%; } - - .one-half.column { width: 48%; } - - /* Offsets */ - .offset-by-one.column, - .offset-by-one.columns { margin-left: 8.66666666667%; } - .offset-by-two.column, - .offset-by-two.columns { margin-left: 17.3333333333%; } - .offset-by-three.column, - .offset-by-three.columns { margin-left: 26%; } - .offset-by-four.column, - .offset-by-four.columns { margin-left: 34.6666666667%; } - .offset-by-five.column, - .offset-by-five.columns { margin-left: 43.3333333333%; } - .offset-by-six.column, - .offset-by-six.columns { margin-left: 52%; } - .offset-by-seven.column, - .offset-by-seven.columns { margin-left: 60.6666666667%; } - .offset-by-eight.column, - .offset-by-eight.columns { margin-left: 69.3333333333%; } - .offset-by-nine.column, - .offset-by-nine.columns { margin-left: 78.0%; } - .offset-by-ten.column, - .offset-by-ten.columns { margin-left: 86.6666666667%; } - .offset-by-eleven.column, - .offset-by-eleven.columns { margin-left: 95.3333333333%; } - - .offset-by-one-third.column, - .offset-by-one-third.columns { margin-left: 34.6666666667%; } - .offset-by-two-thirds.column, - .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } - - .offset-by-one-half.column, - .offset-by-one-half.columns { margin-left: 52%; } - -} - -/* Base Styles -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -/* NOTE -html is set to 62.5% so that all the REM measurements throughout Skeleton -are based on 10px sizing. So basically 1.5rem = 15px :) */ -html { - font-size: 62.5%; } -body { - font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ - line-height: 1.6; - font-weight: 400; - font-family: "Open Sans", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; - color: #a5b1cd; - background-color: #282b38; -} - -/* Typography -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -h1, h2, h3, h4, h5, h6 { - margin-top: 0; - margin-bottom: 0; - font-weight: 300; } -h1 { font-size: 4.5rem; line-height: 1.2; letter-spacing: -.1rem; margin-bottom: 2rem; } -h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; margin-bottom: 1.8rem; margin-top: 1.8rem;} -h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; margin-bottom: 1.5rem; margin-top: 1.5rem;} -h4 { font-size: 2.6rem; line-height: 1.35; letter-spacing: -.08rem; margin-bottom: 1.2rem; margin-top: 1.2rem;} -h5 { font-size: 2.2rem; line-height: 1.5; letter-spacing: -.05rem; margin-bottom: 0.6rem; margin-top: 0.6rem;} -h6 { font-size: 2.0rem; line-height: 1.6; letter-spacing: 0; margin-bottom: 0.75rem; margin-top: 0.75rem;} - -p { - margin-top: 0; } - - -/* Blockquotes -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -blockquote { - border-left: 4px #282b38 solid; - padding-left: 1rem; - margin-top: 2rem; - margin-bottom: 2rem; - margin-left: 0rem; -} - - -/* Links -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -a { - color: #1EAEDB; } -a:hover { - color: #0FA0CE; } - - -/* Buttons -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -.button, -button, -input[type="submit"], -input[type="reset"], -input[type="button"] { - display: inline-block; - height: 38px; - padding: 0 30px; - color: #555; - text-align: center; - font-size: 11px; - font-weight: 600; - line-height: 38px; - letter-spacing: .1rem; - text-transform: uppercase; - text-decoration: none; - white-space: nowrap; - background-color: #282b38; - border-radius: 4px; - border: 1px solid #bbb; - cursor: pointer; - box-sizing: border-box; } -.button:hover, -button:hover, -input[type="submit"]:hover, -input[type="reset"]:hover, -input[type="button"]:hover, -.button:focus, -button:focus, -input[type="submit"]:focus, -input[type="reset"]:focus, -input[type="button"]:focus { - color: #333; - border-color: #888; - outline: 0; } -.button.button-primary, -button.button-primary, -input[type="submit"].button-primary, -input[type="reset"].button-primary, -input[type="button"].button-primary { - color: #FFF; - background-color: #33C3F0; - border-color: #33C3F0; } -.button.button-primary:hover, -button.button-primary:hover, -input[type="submit"].button-primary:hover, -input[type="reset"].button-primary:hover, -input[type="button"].button-primary:hover, -.button.button-primary:focus, -button.button-primary:focus, -input[type="submit"].button-primary:focus, -input[type="reset"].button-primary:focus, -input[type="button"].button-primary:focus { - color: #FFF; - background-color: #1EAEDB; - border-color: #1EAEDB; } - - -/* Forms -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -input[type="email"], -input[type="number"], -input[type="search"], -input[type="text"], -input[type="tel"], -input[type="url"], -input[type="password"], -textarea, -select { - height: 38px; - padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ - background-color: #282b38; - border: 1px solid #D1D1D1; - border-radius: 4px; - box-shadow: none; - box-sizing: border-box; - font-family: inherit; - font-size: inherit; /*https://stackoverflow.com/questions/6080413/why-doesnt-input-inherit-the-font-from-body*/} -/* Removes awkward default styles on some inputs for iOS */ -input[type="email"], -input[type="number"], -input[type="search"], -input[type="text"], -input[type="tel"], -input[type="url"], -input[type="password"], -textarea { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; } -textarea { - min-height: 65px; - padding-top: 6px; - padding-bottom: 6px; } -input[type="email"]:focus, -input[type="number"]:focus, -input[type="search"]:focus, -input[type="text"]:focus, -input[type="tel"]:focus, -input[type="url"]:focus, -input[type="password"]:focus, -textarea:focus, -select:focus { - border: 1px solid #33C3F0; - outline: 0; } -label, -legend { - display: block; - margin-bottom: 0px; } -fieldset { - padding: 0; - border-width: 0; } -input[type="checkbox"], -input[type="radio"] { - display: inline; } -label > .label-body { - display: inline-block; - margin-left: .5rem; - font-weight: normal; } - - -/* Lists -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -ul { - list-style: circle inside; } -ol { - list-style: decimal inside; } -ol, ul { - padding-left: 0; - margin-top: 0; } -ul ul, -ul ol, -ol ol, -ol ul { - margin: 1.5rem 0 1.5rem 3rem; - font-size: 90%; } -li { - margin-bottom: 1rem; } - - -/* Tables -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -th, -td { - padding: 12px 15px; - text-align: left; - border-bottom: 1px solid #282b38; } -th:first-child, -td:first-child { - padding-left: 0; } -th:last-child, -td:last-child { - padding-right: 0; } - - -/* Spacing -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -button, -.button { - margin-bottom: 0rem; } -input, -textarea, -select, -fieldset { - margin-bottom: 0rem; } -pre, -dl, -figure, -table, -form { - margin-bottom: 0rem; } -p, -ul, -ol { - margin-bottom: 0.75rem; } - -/* Utilities -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -.u-full-width { - width: 100%; - box-sizing: border-box; } -.u-max-full-width { - max-width: 100%; - box-sizing: border-box; } -.u-pull-right { - float: right; } -.u-pull-left { - float: left; } - - -/* Misc -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -hr { - margin-top: 3rem; - margin-bottom: 3.5rem; - border-width: 0; - border-top: 1px solid #282b38; } - - -/* Clearing -–––––––––––––––––––––––––––––––––––––––––––––––––– */ - -/* Self Clearing Goodness */ -.container:after, -.row:after, -.u-cf { - content: ""; - display: table; - clear: both; } - - -/* Media Queries -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -/* -Note: The best way to structure the use of media queries is to create the queries -near the relevant code. For example, if you wanted to change the styles for buttons -on small devices, paste the mobile query code up in the buttons section and style it -there. -*/ - - -/* Larger than mobile */ -@media (min-width: 400px) {} - -/* Larger than phablet (also point when grid becomes active) */ -@media (min-width: 550px) {} - -/* Larger than tablet */ -@media (min-width: 750px) {} - -/* Larger than desktop */ -@media (min-width: 1000px) {} - -/* Larger than Desktop HD */ -@media (min-width: 1200px) {} diff --git a/apps/dash-svm/assets/css/app.css b/apps/dash-svm/assets/css/app.css new file mode 100644 index 000000000..972167699 --- /dev/null +++ b/apps/dash-svm/assets/css/app.css @@ -0,0 +1,150 @@ +/* App style */ +body { + font-family: "Open Sans", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #a5b1cd; + background-color: #282b38; +} + +.container-fluid { + padding-top: 1vh; +} + +.control-pannel { + overflow-y: auto; + overflow-x: hidden; + max-height: 80vh; + position: relative; +} + +.card { + background-color: #292B37; + width: auto; + padding: 20px 30px; + border: none; + border-bottom: 1px solid #3A3D4A; +} + +.app-body { + padding: 20px 10px 10px 10px; +} + +/* Scrollbar */ + +::-webkit-scrollbar { + width: 20px; +} + +::-webkit-scrollbar-track { + box-shadow: inset 0 0 5px grey; + border-radius: 10px; +} + +::-webkit-scrollbar-thumb { + background: #2f3445; + border-radius: 10px; +} + +::-webkit-scrollbar-thumb:hover { + background: #2f3445; +} + +/* Header */ +.header { + height: 10vh; + display: flex; + background-color: #2f3445; + padding-left: 2%; + padding-right: 2%; + font-family: playfair display,sans-serif; +} +.header .header-title { + color: #9EA7C0 !important; + font-size: 5vh; +} +.header-logos { + margin-left: auto; + align-self: center; +} +.header-logos img { + margin-left: 3vh !important; + max-height: 5vh; +} +.subheader-title { + font-size: 1.5vh; +} + + +/* buttons and dropdowns */ +#button-zero-threshold { + background-color: #2f3445; + color: #a5b1cd; + border-color: gray; + border: 1px solid #bbb; + padding: 0 30px; + line-height: 38px; + border-radius: 4px; +} +.Select-control { + color: #a5b1cd; +} + +.Select { + color: #a5b1cd; +} + +.Select-menu-outer { + background-color: #2f3445; + border: 1px solid gray; +} + +.Select div { + background-color: #2f3445; +} + +.Select-menu-outer div:hover { + background-color: rgba(255, 255, 255, 0.01); +} + +.Select-value-label { + color: #a5b1cd !important; +} + +.Select--single > .Select-control .Select-value, .Select-placeholder { + border: 1px solid gray; + border-radius: 4px; +} + + +/* Demo button css */ +.demo-button { + font-size: 1.5vh; + font-family: Open Sans,sans-serif; + text-decoration: none; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + border-radius: 8px; + font-weight: 700; + -webkit-padding-start: 1rem; + padding-inline-start: 1rem; + -webkit-padding-end: 1rem; + padding-inline-end: 1rem; + color: #ffffff; + letter-spacing: 1.5px; + border: solid 1.5px transparent; + box-shadow: 2px 1000px 1px #0c0c0c inset; + background-image: linear-gradient(135deg, #7A76FF, #7A76FF, #7FE4FF); + -webkit-background-size: 200% 100%; + background-size: 200% 100%; + -webkit-background-position: 99%; + background-position: 99%; + background-origin: border-box; + transition: all .4s ease-in-out; + padding-top: 1vh; + padding-bottom: 1vh; +} +.demo-button:hover { + color: #7A76FF; + background-position: 0%; +} \ No newline at end of file diff --git a/apps/dash-svm/assets/custom-styles.css b/apps/dash-svm/assets/custom-styles.css deleted file mode 100644 index 892a1d5de..000000000 --- a/apps/dash-svm/assets/custom-styles.css +++ /dev/null @@ -1,446 +0,0 @@ -@import url('https://fonts.googleapis.com/css?family=Playfair+Display'); - -#body { - padding-bottom: 5rem; -} - -@media (max-width: 700px) { - #body { - width: 100%; - } -} - -/* Scalable Container -–––––––––––––––––––––––––––––––––––––––––––––––––– */ - -.container.scalable { - width: 95%; - max-width: none; -} - -/* Banner -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -.banner { - background-color: #2f3445; /* Machine Learning Color is orange */ - padding: 3rem 0; - width: 100%; - margin-bottom: 5rem; -} - -.banner h2 { - color: #a5b1cd; - display: inline-block; - font-family: 'Playfair Display', sans-serif; - font-size: 4rem; - line-height: 1; - text-align: center; -} - -.banner h2:hover { - color: #b4b5bf; - cursor: pointer; -} - -.banner Img { - position: relative; - float: right; - height: 5rem; - margin-top: 1.25rem -} - -@media (max-width: 1300px) { - - .banner .container.scalable { - display: flex; - flex-direction: column-reverse; - justify-content: space-between; - align-items: center; - } - - .banner h2 { - font-size: 4rem; - } - - .banner Img { - height: 8rem; - margin-bottom: 3rem; - } -} - -@media (max-width: 700px) { - .banner .container.scalable { - padding: 0 - } - - .banner Img { - height: 4rem; - margin-bottom: 2rem; - } - - .banner h2 { - font-size: 3rem; - } -} - -@media (max-width: 500px) { - .banner { - padding: 1rem 0; - margin-bottom: 1rem; - } - - .banner Img { - height: 3rem; - margin-bottom: 1rem; - } - - .banner h2 { - font-size: 2.5rem; - } -} - -/* app-container -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -@media (min-width: 951px) { - #app-container { - width: 100%; - display: flex; - flex-direction: row; - align-items: flex-start; - } -} - -/* Dropdowns -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -.Select-control { - color: #a5b1cd; -} - -.Select { - color: #a5b1cd; -} - -.Select-menu-outer { - background-color: #2f3445; - border: 1px solid gray; -} - -.Select div { - background-color: #2f3445; -} - -.Select-menu-outer div:hover { - background-color: rgba(255, 255, 255, 0.01); -} - -.Select-value-label { - color: #a5b1cd !important; -} - -.Select--single > .Select-control .Select-value, .Select-placeholder { - border: 1px solid gray; - border-radius: 4px; -} - -.card { - border-bottom: 1px solid rgba(255, 255, 255, 0.1); - width: 80%; - padding: 2rem 0; -} - -#last-card { - border-bottom: none; -} - -.graph-title { - font-size: 2rem; - margin: 15% 0 0 25%; -} - -#button-zero-threshold { - background-color: #2f3445; - color: #a5b1cd; - border-color: gray; -} - -#button-zero-threshold:hover { - border-color: white; -} - -#button-card { - display: flex; - flex-direction: column; -} - -#first-card { - padding-top: 0; -} - -@media (max-width: 1500px) { - .rc-slider-mark-text { - font-size: 0.7vw; - } -} - -@media (max-width: 950px) { - .rc-slider-mark-text { - font-size: 1.5vw; - } -} - - -@media (max-width: 650px) { - .rc-slider-mark-text { - font-size: 2vw; - } -} - - -@media (min-width: 1301px) { - #left-column { - flex: 1 20%; - margin: 0 3rem 0 0; - max-height: 83rem; - overflow-x: hidden; - overflow-y: auto; - } - - .card { - padding-left: 2rem; - } -} - -@media (max-width: 1300px) { - #left-column { - flex: 1 20%; - margin: 0 3rem 0 0; - max-height: 70rem; - overflow-x: hidden; - overflow-y: auto; - } -} - -@media (max-width: 1200px) { - #button-zero-threshold { - font-size: 0.8rem; - padding: 0 - } -} - -@media (max-width: 950px) { - #button-zero-threshold { - font-size: 1.0rem; - padding: 0 - } -} - -@media (max-width: 950px) { - #left-column { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - margin: 0; - max-height: none; - max-width: none; - } -} - -@media (max-width: 700px) { - #first-card { - margin: 0; - padding-right: 0; - padding-left: 0; - } -} - -/* Slider -–––––––––––––––––––––––––––––––––––––––––––––––––– */ - -.rc-slider-track { - background-color: #13c6e9; -} - -.rc-slider-handle { - border: solid 2px #13c6e9; -} - -/* Left column -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -#slider-svm-parameter-C-coef { - padding: 5px 10px 25px; -} - -#slider-svm-parameter-gamma-coef { - padding: 5px 10px 25px -} - -#shrinking-container { - padding: 20px 10px 25px 4px -} - -/* Graph container -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -#svm-graph-container { - margin-top: 0.5rem; -} - -#svm-graph-container .graph-wrapper { - height: 100%; - width: 100%; -} - -@media (min-width: 951px) { - #graphs-container { - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: flex-start; - } - - #graphs-container .graph-wrapper { - flex: 1 50%; - } - - #graph-line-roc-curve, #graph-pie-confusion-matrix { - height: 100%; - width: 100%; - } - - #div-graphs { - flex: 4 80%; - display: flex; - flex-direction: row; - justify-content: center; - align-items: stretch; - } - - #graph-sklearn-svm { - height: 100%; - } - - #svm-graph-container { - flex: 2 66%; - } - - #graphs-container { - flex: 1 33%; - margin-top: 0.5rem; - } -} - -@media (min-width: 1301px) { - #div-graphs { - height: 83rem; - } -} - -@media (max-width: 1300px) { - #div-graphs { - height: 70rem; - } - - .gtitle { - font-size: 1.25rem !important; - } - - .xtitle, .ytitle { - font-size: 0.9rem !important; - } -} - -@media (max-width: 950px) { - #div-graphs { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: flex-start; - } - - #svm-graph-container { - width: 80vw; - height: 100vw; - display: flex; - flex-direction: column; - align-items: center; - padding-bottom: 4rem; - border-bottom: solid 1px rgba(255, 255, 255, 0.1); - } - - #svm-graph-container .graph-wrapper { - width: 80vw; - height: 80vw; - } - - #graphs-container { - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: flex-start; - width: 90%; - max-width: 90%; - margin: 0 0 0 -1rem; - max-height: none; - } - - #graphs-container .graph-wrapper:nth-of-type(1) { - height: 40rem; - margin: 3rem 0 1rem -3rem; - width: 100%; - } - - #graphs-container .graph-wrapper:nth-of-type(2) { - height: 60rem; - margin-bottom: 5rem; - width: 100%; - } - - #graph-sklearn-svm, #graph-line-roc-curve, #graph-pie-confusion-matrix { - height: 100%; - width: 100%; - } -} - -@media (max-width: 650px) { - #graphs-container { - align-items: center; - width: 100%; - max-width: 100%; - } - - #graph-line-roc-curve { - width: 95%; - margin: 3rem 0 1rem 0; - } - - #graph-pie-confusion-matrix { - width: 95%; - margin-bottom: 5rem; - } - - #graphs-container .graph-wrapper:nth-of-type(1) { - height: 35rem; - } - - #graphs-container .graph-wrapper:nth-of-type(2) { - height: 45rem; - padding-left: 3rem; - } -} - -@media (max-width: 400px) { - #graphs-container .graph-wrapper:nth-of-type(1) { - height: 25rem; - } - - #graphs-container .graph-wrapper:nth-of-type(2) { - height: 40rem; - padding-left: 3rem; - } -} - -/* Remove Undo -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -#graph-line-roc-curve .modebar, #graph-pie-confusion-matrix .modebar { - display: none; -} diff --git a/apps/dash-svm/assets/dash-logo-new.png b/apps/dash-svm/assets/dash-logo-new.png deleted file mode 100644 index eb700fc71..000000000 Binary files a/apps/dash-svm/assets/dash-logo-new.png and /dev/null differ diff --git a/apps/dash-svm/images/animated1.gif b/apps/dash-svm/assets/github/images/animated1.gif similarity index 100% rename from apps/dash-svm/images/animated1.gif rename to apps/dash-svm/assets/github/images/animated1.gif diff --git a/apps/dash-svm/images/screenshot.png b/apps/dash-svm/assets/github/images/screenshot.png similarity index 100% rename from apps/dash-svm/images/screenshot.png rename to apps/dash-svm/assets/github/images/screenshot.png diff --git a/apps/dash-svm/assets/images/plotly-logo.png b/apps/dash-svm/assets/images/plotly-logo.png new file mode 100644 index 000000000..984dd57ab Binary files /dev/null and b/apps/dash-svm/assets/images/plotly-logo.png differ diff --git a/apps/dash-svm/requirements.txt b/apps/dash-svm/requirements.txt index 3367c0cce..77d66217f 100644 --- a/apps/dash-svm/requirements.txt +++ b/apps/dash-svm/requirements.txt @@ -1,10 +1,5 @@ -# Core -gunicorn>=19.8.1 -dash>=1.0.0 - -# Additional -colorlover>=0.2.1 -numpy>=1.16.2 -pandas>=0.24.2 -scikit-learn>=0.20.3 -scipy>=1.2.1 +dash==2.4.1 +pandas==1.4.2 +scikit-learn==1.1.1 +colorlover==0.3.0 +gunicorn==20.1.0 diff --git a/apps/dash-svm/utils/README.md b/apps/dash-svm/utils/README.md deleted file mode 100644 index 0eac67920..000000000 --- a/apps/dash-svm/utils/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Utility Files - -## Dash Reusable Components - -Creating custom, reusable components lets you improve workflow and keep repetitions to a minimum (DRY). In this app, there are a few components that have the same pattern, but with only small differences; for example, a dropdown menu with an associated name. In these cases, reusable components were useful to keep the design of those repeated components consistent, and make the app layout less crowded. - -To read more about Reusable components, check out [this workshop by Plotly](https://dash-workshop.plot.ly/reusable-components). diff --git a/apps/dash-svm/utils/components.py b/apps/dash-svm/utils/components.py new file mode 100644 index 000000000..8c6abfc90 --- /dev/null +++ b/apps/dash-svm/utils/components.py @@ -0,0 +1,193 @@ +from dash import html, dcc + +def Header(app, header, subheader=None): + left_headers = html.Div([html.Div(header, className="header-title"), html.Div(subheader, className="subheader-title")]) + + logo = html.Img(src=app.get_asset_url("images/plotly-logo.png")) + link = html.A(logo, href="https://plotly.com/dash/", target="_blank") + demo_link = html.A("LEARN MORE", href="https://plotly.com/dash/", target="_blank", className="demo-button") + right_logos = html.Div([demo_link, link], className="header-logos") + + return html.Div([left_headers, right_logos], className="header") + + +def _omit(omitted_keys, d): + return {k: v for k, v in d.items() if k not in omitted_keys} + +def FormattedSlider(**kwargs): + return html.Div( + style=kwargs.get("style", {}), children=dcc.Slider(**_omit(["style"], kwargs)) + ) + +def NamedSlider(name, **kwargs): + return html.Div( + style={"padding": "20px 10px 25px 4px"}, + children=[ + html.P(f"{name}:"), + html.Div(style={"margin-left": "6px"}, children=dcc.Slider(**kwargs)), + ], + ) + +def NamedDropdown(name, **kwargs): + return html.Div( + style={"margin": "10px 0px"}, + children=[ + html.P(children=f"{name}:", style={"margin-left": "3px"}), + dcc.Dropdown(**kwargs), + ], + ) + + +def NamedRadioItems(name, **kwargs): + return html.Div( + style={"padding": "20px 10px 25px 4px"}, + children=[html.P(children=f"{name}:"), dcc.RadioItems(**kwargs)], + ) + + +controls_first = children=[ + NamedDropdown( + name="Select Dataset", + id="dropdown-select-dataset", + options=[ + {"label": "Moons", "value": "moons"}, + { + "label": "Linearly Separable", + "value": "linear", + }, + { + "label": "Circles", + "value": "circles", + }, + ], + clearable=False, + searchable=False, + value="moons", + ), + NamedSlider( + name="Sample Size", + id="slider-dataset-sample-size", + min=100, + max=500, + step=100, + marks={ + str(i): str(i) + for i in [100, 200, 300, 400, 500] + }, + value=300, + ), + NamedSlider( + name="Noise Level", + id="slider-dataset-noise-level", + min=0, + max=1, + marks={ + i / 10: str(i / 10) + for i in range(0, 11, 2) + }, + step=0.1, + value=0.2, + ), +] + +controls_second = [ + NamedSlider( + name="Threshold", + id="slider-threshold", + min=0, + max=1, + value=0.4, + step=0.01, + marks={ i / 10: str(i / 10) for i in range(0, 11, 2) }, + ), + html.Button( + "Reset Threshold", + id="button-zero-threshold", + ), +] + +controls_third = [ + NamedDropdown( + name="Kernel", + id="dropdown-svm-parameter-kernel", + options=[ + { + "label": "Radial basis function (RBF)", + "value": "rbf", + }, + {"label": "Linear", "value": "linear"}, + { + "label": "Polynomial", + "value": "poly", + }, + { + "label": "Sigmoid", + "value": "sigmoid", + }, + ], + value="rbf", + clearable=False, + searchable=False, + ), + NamedSlider( + name="Cost (C)", + id="slider-svm-parameter-C-power", + min=-2, + max=4, + value=0, + marks={ i: str(10 ** i) for i in range(-2, 5, 2) }, + ), + FormattedSlider( + id="slider-svm-parameter-C-coef", + min=1, + max=9, + value=1, + ), + NamedSlider( + name="Degree", + id="slider-svm-parameter-degree", + min=2, + max=10, + value=3, + step=1, + marks={ + str(i): str(i) for i in range(2, 11, 2) + }, + ), + NamedSlider( + name="Gamma", + id="slider-svm-parameter-gamma-power", + min=-5, + max=0, + value=-1, + marks={ + i: "{}".format(10 ** i) + for i in range(-5, 1, 2) + }, + ), + FormattedSlider( + id="slider-svm-parameter-gamma-coef", + min=1, + max=9, + value=5, + ), + NamedRadioItems( + name="Shrinking", + id="radio-svm-parameter-shrinking", + labelStyle={ + "margin-right": "7px", + "display": "inline-block", + }, + options=[ + { + "label": " Enabled", + "value": "True", + }, + { + "label": " Disabled", + "value": "False", + }, + ], + value="True", + ), +] \ No newline at end of file diff --git a/apps/dash-svm/utils/dash_reusable_components.py b/apps/dash-svm/utils/dash_reusable_components.py deleted file mode 100644 index e080d0876..000000000 --- a/apps/dash-svm/utils/dash_reusable_components.py +++ /dev/null @@ -1,75 +0,0 @@ -from textwrap import dedent - -import dash_core_components as dcc -import dash_html_components as html - - -# Display utility functions -def _merge(a, b): - return dict(a, **b) - - -def _omit(omitted_keys, d): - return {k: v for k, v in d.items() if k not in omitted_keys} - - -# Custom Display Components -def Card(children, **kwargs): - return html.Section(className="card", children=children, **_omit(["style"], kwargs)) - - -def FormattedSlider(**kwargs): - return html.Div( - style=kwargs.get("style", {}), children=dcc.Slider(**_omit(["style"], kwargs)) - ) - - -def NamedSlider(name, **kwargs): - return html.Div( - style={"padding": "20px 10px 25px 4px"}, - children=[ - html.P(f"{name}:"), - html.Div(style={"margin-left": "6px"}, children=dcc.Slider(**kwargs)), - ], - ) - - -def NamedDropdown(name, **kwargs): - return html.Div( - style={"margin": "10px 0px"}, - children=[ - html.P(children=f"{name}:", style={"margin-left": "3px"}), - dcc.Dropdown(**kwargs), - ], - ) - - -def NamedRadioItems(name, **kwargs): - return html.Div( - style={"padding": "20px 10px 25px 4px"}, - children=[html.P(children=f"{name}:"), dcc.RadioItems(**kwargs)], - ) - - -# Non-generic -def DemoDescription(filename, strip=False): - with open(filename, "r") as file: - text = file.read() - - if strip: - text = text.split("")[-1] - text = text.split("")[0] - - return html.Div( - className="row", - style={ - "padding": "15px 30px 27px", - "margin": "45px auto 45px", - "width": "80%", - "max-width": "1024px", - "borderRadius": 5, - "border": "thin lightgrey solid", - "font-family": "Roboto, sans-serif", - }, - children=dcc.Markdown(dedent(text)), - ) diff --git a/apps/dash-svm/utils/figures.py b/apps/dash-svm/utils/figures.py index 75c994516..90daf54f5 100644 --- a/apps/dash-svm/utils/figures.py +++ b/apps/dash-svm/utils/figures.py @@ -1,7 +1,7 @@ -import colorlover as cl import plotly.graph_objs as go import numpy as np from sklearn import metrics +import colorlover as cl def serve_prediction_plot( diff --git a/apps/dash-svm/utils/helper_functions.py b/apps/dash-svm/utils/helper_functions.py new file mode 100644 index 000000000..a372e7ede --- /dev/null +++ b/apps/dash-svm/utils/helper_functions.py @@ -0,0 +1,32 @@ +from sklearn import datasets +import numpy as np + +def generate_data(n_samples, dataset, noise): + if dataset == "moons": + return datasets.make_moons(n_samples=n_samples, noise=noise, random_state=0) + + elif dataset == "circles": + return datasets.make_circles( + n_samples=n_samples, noise=noise, factor=0.5, random_state=1 + ) + + elif dataset == "linear": + X, y = datasets.make_classification( + n_samples=n_samples, + n_features=2, + n_redundant=0, + n_informative=2, + random_state=2, + n_clusters_per_class=1, + ) + + rng = np.random.RandomState(2) + X += noise * rng.uniform(size=X.shape) + linearly_separable = (X, y) + + return linearly_separable + + else: + raise ValueError( + "Data type incorrectly specified. Please choose an existing dataset." + ) diff --git a/apps/dash-svm/utils/model.py b/apps/dash-svm/utils/model.py new file mode 100644 index 000000000..a3f26ac28 --- /dev/null +++ b/apps/dash-svm/utils/model.py @@ -0,0 +1,62 @@ +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import StandardScaler +from sklearn.svm import SVC +import numpy as np + +import utils.figures as figs +from utils.helper_functions import generate_data + +def svm_prediction(kernel, degree, C_coef, C_power, gamma_coef, gamma_power, dataset, noise, shrinking, threshold, sample_size): + h = 0.3 # step size in the mesh + + # Data Pre-processing + X, y = generate_data(n_samples=sample_size, dataset=dataset, noise=noise) + X = StandardScaler().fit_transform(X) + X_train, X_test, y_train, y_test = train_test_split( + X, y, test_size=0.4, random_state=42 + ) + + x_min = X[:, 0].min() - 0.5 + x_max = X[:, 0].max() + 0.5 + y_min = X[:, 1].min() - 0.5 + y_max = X[:, 1].max() + 0.5 + xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) + + C = C_coef * 10 ** C_power + gamma = gamma_coef * 10 ** gamma_power + + if shrinking == "True": + flag = True + else: + flag = False + + # Train SVM + clf = SVC(C=C, kernel=kernel, degree=degree, gamma=gamma, shrinking=flag) + clf.fit(X_train, y_train) + + # Plot the decision boundary. For that, we will assign a color to each + # point in the mesh [x_min, x_max]x[y_min, y_max]. + if hasattr(clf, "decision_function"): + Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) + else: + Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1] + + prediction_figure = figs.serve_prediction_plot( + model=clf, + X_train=X_train, + X_test=X_test, + y_train=y_train, + y_test=y_test, + Z=Z, + xx=xx, + yy=yy, + mesh_step=h, + threshold=threshold, + ) + + roc_figure = figs.serve_roc_curve(model=clf, X_test=X_test, y_test=y_test) + + confusion_figure = figs.serve_pie_confusion_matrix( + model=clf, X_test=X_test, y_test=y_test, Z=Z, threshold=threshold + ) + return prediction_figure, roc_figure, confusion_figure