├── README.md ├── app_10_regression_bioinformatics_solubility ├── solubility-app.py ├── solubility-logo.jpg ├── solubility-web-app.ipynb └── solubility_model.pkl ├── app_1_simple_stock_price ├── myapp.py └── myapp2.py ├── app_2_simple_bioinformatics_dna ├── dna-app.py └── dna-logo.jpg ├── app_3_eda_basketball └── basketball_app.py ├── app_4_eda_football └── football_app.py ├── app_5_eda_sp500_stock └── sp500-app.py ├── app_6_eda_cryptocurrency ├── crypto-price-app.py └── logo.jpg ├── app_7_classification_iris └── iris-ml-app.py ├── app_8_classification_penguins ├── penguins-app.py ├── penguins-model-building.py ├── penguins_cleaned.csv ├── penguins_clf.pkl └── penguins_example.csv └── app_9_regression_boston_housing └── boston-house-ml-app.py /README.md: -------------------------------------------------------------------------------- 1 | # streamlit_freecodecamp 2 | ## Build 12 Data Apps in Python with Streamlit 3 | 4 | Learn how to build interactive and data-driven web apps in Python using the Streamlit library. 5 | 6 | Video | Title 7 | ---|--- 8 | Build 12 Data Science Apps with Python and Streamlit - Full Course | [Build 12 Data Science Apps with Python and Streamlit - Full Course](https://youtu.be/JwSS70SZdyM) 9 | 10 | -------------------------------------------------------------------------------- /app_10_regression_bioinformatics_solubility/solubility-app.py: -------------------------------------------------------------------------------- 1 | ###################### 2 | # Import libraries 3 | ###################### 4 | import numpy as np 5 | import pandas as pd 6 | import streamlit as st 7 | import pickle 8 | from PIL import Image 9 | from rdkit import Chem 10 | from rdkit.Chem import Descriptors 11 | 12 | ###################### 13 | # Custom function 14 | ###################### 15 | ## Calculate molecular descriptors 16 | def AromaticProportion(m): 17 | aromatic_atoms = [m.GetAtomWithIdx(i).GetIsAromatic() for i in range(m.GetNumAtoms())] 18 | aa_count = [] 19 | for i in aromatic_atoms: 20 | if i==True: 21 | aa_count.append(1) 22 | AromaticAtom = sum(aa_count) 23 | HeavyAtom = Descriptors.HeavyAtomCount(m) 24 | AR = AromaticAtom/HeavyAtom 25 | return AR 26 | 27 | def generate(smiles, verbose=False): 28 | 29 | moldata= [] 30 | for elem in smiles: 31 | mol=Chem.MolFromSmiles(elem) 32 | moldata.append(mol) 33 | 34 | baseData= np.arange(1,1) 35 | i=0 36 | for mol in moldata: 37 | 38 | desc_MolLogP = Descriptors.MolLogP(mol) 39 | desc_MolWt = Descriptors.MolWt(mol) 40 | desc_NumRotatableBonds = Descriptors.NumRotatableBonds(mol) 41 | desc_AromaticProportion = AromaticProportion(mol) 42 | 43 | row = np.array([desc_MolLogP, 44 | desc_MolWt, 45 | desc_NumRotatableBonds, 46 | desc_AromaticProportion]) 47 | 48 | if(i==0): 49 | baseData=row 50 | else: 51 | baseData=np.vstack([baseData, row]) 52 | i=i+1 53 | 54 | columnNames=["MolLogP","MolWt","NumRotatableBonds","AromaticProportion"] 55 | descriptors = pd.DataFrame(data=baseData,columns=columnNames) 56 | 57 | return descriptors 58 | 59 | ###################### 60 | # Page Title 61 | ###################### 62 | 63 | image = Image.open('solubility-logo.jpg') 64 | 65 | st.image(image, use_column_width=True) 66 | 67 | st.write(""" 68 | # Molecular Solubility Prediction Web App 69 | 70 | This app predicts the **Solubility (LogS)** values of molecules! 71 | 72 | Data obtained from the John S. Delaney. [ESOL:  Estimating Aqueous Solubility Directly from Molecular Structure](https://pubs.acs.org/doi/10.1021/ci034243x). ***J. Chem. Inf. Comput. Sci.*** 2004, 44, 3, 1000-1005. 73 | *** 74 | """) 75 | 76 | 77 | ###################### 78 | # Input molecules (Side Panel) 79 | ###################### 80 | 81 | st.sidebar.header('User Input Features') 82 | 83 | ## Read SMILES input 84 | SMILES_input = "NCCCC\nCCC\nCN" 85 | 86 | SMILES = st.sidebar.text_area("SMILES input", SMILES_input) 87 | SMILES = "C\n" + SMILES #Adds C as a dummy, first item 88 | SMILES = SMILES.split('\n') 89 | 90 | st.header('Input SMILES') 91 | SMILES[1:] # Skips the dummy first item 92 | 93 | ## Calculate molecular descriptors 94 | st.header('Computed molecular descriptors') 95 | X = generate(SMILES) 96 | X[1:] # Skips the dummy first item 97 | 98 | ###################### 99 | # Pre-built model 100 | ###################### 101 | 102 | # Reads in saved model 103 | load_model = pickle.load(open('solubility_model.pkl', 'rb')) 104 | 105 | # Apply model to make predictions 106 | prediction = load_model.predict(X) 107 | #prediction_proba = load_model.predict_proba(X) 108 | 109 | st.header('Predicted LogS values') 110 | prediction[1:] # Skips the dummy first item 111 | -------------------------------------------------------------------------------- /app_10_regression_bioinformatics_solubility/solubility-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dataprofessor/streamlit_freecodecamp/d44c4c1320f8417b3d5494902e5366b715314528/app_10_regression_bioinformatics_solubility/solubility-logo.jpg -------------------------------------------------------------------------------- /app_10_regression_bioinformatics_solubility/solubility-web-app.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "solubility-web-app.ipynb", 7 | "provenance": [], 8 | "toc_visible": true 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | } 14 | }, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "metadata": { 19 | "id": "QQHZHevuXdEy", 20 | "colab_type": "text" 21 | }, 22 | "source": [ 23 | "# **Model Building for Solubility Dataset**\n", 24 | "\n", 25 | "Chanin Nantasenamat\n", 26 | "\n", 27 | "*Data Professor YouTube channel, http://youtube.com/dataprofessor*" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": { 33 | "id": "g1qtHa0zXfWM", 34 | "colab_type": "text" 35 | }, 36 | "source": [ 37 | "# Read in data" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "metadata": { 43 | "id": "9MdfbvFKXtXq", 44 | "colab_type": "code", 45 | "colab": {} 46 | }, 47 | "source": [ 48 | "import pandas as pd" 49 | ], 50 | "execution_count": 1, 51 | "outputs": [] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "metadata": { 56 | "id": "nerGP0fCXfgP", 57 | "colab_type": "code", 58 | "colab": { 59 | "base_uri": "https://localhost:8080/", 60 | "height": 419 61 | }, 62 | "outputId": "2bb155a6-2710-4461-accb-df64045ba70d" 63 | }, 64 | "source": [ 65 | "delaney_with_descriptors_url = 'https://raw.githubusercontent.com/dataprofessor/data/master/delaney_solubility_with_descriptors.csv'\n", 66 | "dataset = pd.read_csv(delaney_with_descriptors_url)\n", 67 | "dataset" 68 | ], 69 | "execution_count": 2, 70 | "outputs": [ 71 | { 72 | "output_type": "execute_result", 73 | "data": { 74 | "text/html": [ 75 | "
\n", 76 | "\n", 89 | "\n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 190 | "
MolLogPMolWtNumRotatableBondsAromaticProportionlogS
02.59540167.8500.00.000000-2.180
12.37650133.4050.00.000000-2.000
22.59380167.8501.00.000000-1.740
32.02890133.4051.00.000000-1.480
42.91890187.3751.00.000000-3.040
..................
11391.98820287.3438.00.0000001.144
11403.42130286.1142.00.333333-4.925
11413.60960308.3334.00.695652-3.893
11422.56214354.8153.00.521739-3.790
11432.02164179.2191.00.461538-2.581
\n", 191 | "

1144 rows × 5 columns

\n", 192 | "
" 193 | ], 194 | "text/plain": [ 195 | " MolLogP MolWt NumRotatableBonds AromaticProportion logS\n", 196 | "0 2.59540 167.850 0.0 0.000000 -2.180\n", 197 | "1 2.37650 133.405 0.0 0.000000 -2.000\n", 198 | "2 2.59380 167.850 1.0 0.000000 -1.740\n", 199 | "3 2.02890 133.405 1.0 0.000000 -1.480\n", 200 | "4 2.91890 187.375 1.0 0.000000 -3.040\n", 201 | "... ... ... ... ... ...\n", 202 | "1139 1.98820 287.343 8.0 0.000000 1.144\n", 203 | "1140 3.42130 286.114 2.0 0.333333 -4.925\n", 204 | "1141 3.60960 308.333 4.0 0.695652 -3.893\n", 205 | "1142 2.56214 354.815 3.0 0.521739 -3.790\n", 206 | "1143 2.02164 179.219 1.0 0.461538 -2.581\n", 207 | "\n", 208 | "[1144 rows x 5 columns]" 209 | ] 210 | }, 211 | "metadata": { 212 | "tags": [] 213 | }, 214 | "execution_count": 2 215 | } 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "metadata": { 221 | "id": "tgFxx8m_YEUy", 222 | "colab_type": "code", 223 | "colab": { 224 | "base_uri": "https://localhost:8080/", 225 | "height": 419 226 | }, 227 | "outputId": "fd6feedd-253b-4189-d400-a8d3f5bf1f25" 228 | }, 229 | "source": [ 230 | "X = dataset.drop(['logS'], axis=1)\n", 231 | "X" 232 | ], 233 | "execution_count": 5, 234 | "outputs": [ 235 | { 236 | "output_type": "execute_result", 237 | "data": { 238 | "text/html": [ 239 | "
\n", 240 | "\n", 253 | "\n", 254 | " \n", 255 | " \n", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | " \n", 300 | " \n", 301 | " \n", 302 | " \n", 303 | " \n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | "
MolLogPMolWtNumRotatableBondsAromaticProportion
02.59540167.8500.00.000000
12.37650133.4050.00.000000
22.59380167.8501.00.000000
32.02890133.4051.00.000000
42.91890187.3751.00.000000
...............
11391.98820287.3438.00.000000
11403.42130286.1142.00.333333
11413.60960308.3334.00.695652
11422.56214354.8153.00.521739
11432.02164179.2191.00.461538
\n", 343 | "

1144 rows × 4 columns

\n", 344 | "
" 345 | ], 346 | "text/plain": [ 347 | " MolLogP MolWt NumRotatableBonds AromaticProportion\n", 348 | "0 2.59540 167.850 0.0 0.000000\n", 349 | "1 2.37650 133.405 0.0 0.000000\n", 350 | "2 2.59380 167.850 1.0 0.000000\n", 351 | "3 2.02890 133.405 1.0 0.000000\n", 352 | "4 2.91890 187.375 1.0 0.000000\n", 353 | "... ... ... ... ...\n", 354 | "1139 1.98820 287.343 8.0 0.000000\n", 355 | "1140 3.42130 286.114 2.0 0.333333\n", 356 | "1141 3.60960 308.333 4.0 0.695652\n", 357 | "1142 2.56214 354.815 3.0 0.521739\n", 358 | "1143 2.02164 179.219 1.0 0.461538\n", 359 | "\n", 360 | "[1144 rows x 4 columns]" 361 | ] 362 | }, 363 | "metadata": { 364 | "tags": [] 365 | }, 366 | "execution_count": 5 367 | } 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "metadata": { 373 | "id": "JDwxgKHqYmD4", 374 | "colab_type": "code", 375 | "colab": { 376 | "base_uri": "https://localhost:8080/", 377 | "height": 221 378 | }, 379 | "outputId": "a725d7b7-baad-4a99-9686-4dfe1d852c22" 380 | }, 381 | "source": [ 382 | "Y = dataset.iloc[:,-1]\n", 383 | "Y" 384 | ], 385 | "execution_count": 6, 386 | "outputs": [ 387 | { 388 | "output_type": "execute_result", 389 | "data": { 390 | "text/plain": [ 391 | "0 -2.180\n", 392 | "1 -2.000\n", 393 | "2 -1.740\n", 394 | "3 -1.480\n", 395 | "4 -3.040\n", 396 | " ... \n", 397 | "1139 1.144\n", 398 | "1140 -4.925\n", 399 | "1141 -3.893\n", 400 | "1142 -3.790\n", 401 | "1143 -2.581\n", 402 | "Name: logS, Length: 1144, dtype: float64" 403 | ] 404 | }, 405 | "metadata": { 406 | "tags": [] 407 | }, 408 | "execution_count": 6 409 | } 410 | ] 411 | }, 412 | { 413 | "cell_type": "markdown", 414 | "metadata": { 415 | "id": "LNohCdqQY5VZ", 416 | "colab_type": "text" 417 | }, 418 | "source": [ 419 | "# Linear Regression Model" 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "metadata": { 425 | "id": "EanoyG2eX9cV", 426 | "colab_type": "code", 427 | "colab": {} 428 | }, 429 | "source": [ 430 | "from sklearn import linear_model\n", 431 | "from sklearn.metrics import mean_squared_error, r2_score" 432 | ], 433 | "execution_count": 3, 434 | "outputs": [] 435 | }, 436 | { 437 | "cell_type": "code", 438 | "metadata": { 439 | "id": "mLQJ2KLLY_9a", 440 | "colab_type": "code", 441 | "colab": { 442 | "base_uri": "https://localhost:8080/", 443 | "height": 34 444 | }, 445 | "outputId": "6349fa74-f087-4d81-916e-294789c6455c" 446 | }, 447 | "source": [ 448 | "model = linear_model.LinearRegression()\n", 449 | "model.fit(X, Y)" 450 | ], 451 | "execution_count": 7, 452 | "outputs": [ 453 | { 454 | "output_type": "execute_result", 455 | "data": { 456 | "text/plain": [ 457 | "LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)" 458 | ] 459 | }, 460 | "metadata": { 461 | "tags": [] 462 | }, 463 | "execution_count": 7 464 | } 465 | ] 466 | }, 467 | { 468 | "cell_type": "markdown", 469 | "metadata": { 470 | "id": "F5f8KGWjZRSc", 471 | "colab_type": "text" 472 | }, 473 | "source": [ 474 | "## Model Prediction" 475 | ] 476 | }, 477 | { 478 | "cell_type": "code", 479 | "metadata": { 480 | "id": "MI3c8LB2ZCYW", 481 | "colab_type": "code", 482 | "colab": { 483 | "base_uri": "https://localhost:8080/", 484 | "height": 51 485 | }, 486 | "outputId": "19b50c6a-7d1c-4bfd-8789-d5884b42d594" 487 | }, 488 | "source": [ 489 | "Y_pred = model.predict(X)\n", 490 | "Y_pred" 491 | ], 492 | "execution_count": 8, 493 | "outputs": [ 494 | { 495 | "output_type": "execute_result", 496 | "data": { 497 | "text/plain": [ 498 | "array([-2.77628837, -2.38661054, -2.77190108, ..., -4.73721496,\n", 499 | " -4.19663007, -2.61784284])" 500 | ] 501 | }, 502 | "metadata": { 503 | "tags": [] 504 | }, 505 | "execution_count": 8 506 | } 507 | ] 508 | }, 509 | { 510 | "cell_type": "markdown", 511 | "metadata": { 512 | "id": "fXv7bcolZqa-", 513 | "colab_type": "text" 514 | }, 515 | "source": [ 516 | "## Model Performance" 517 | ] 518 | }, 519 | { 520 | "cell_type": "code", 521 | "metadata": { 522 | "id": "6f13gYleZVKy", 523 | "colab_type": "code", 524 | "colab": { 525 | "base_uri": "https://localhost:8080/", 526 | "height": 85 527 | }, 528 | "outputId": "99894d58-83b4-4b64-f54c-848e8430cd5e" 529 | }, 530 | "source": [ 531 | "print('Coefficients:', model.coef_)\n", 532 | "print('Intercept:', model.intercept_)\n", 533 | "print('Mean squared error (MSE): %.2f'\n", 534 | " % mean_squared_error(Y, Y_pred))\n", 535 | "print('Coefficient of determination (R^2): %.2f'\n", 536 | " % r2_score(Y, Y_pred))" 537 | ], 538 | "execution_count": 9, 539 | "outputs": [ 540 | { 541 | "output_type": "stream", 542 | "text": [ 543 | "Coefficients: [-0.74173609 -0.00659927 0.00320051 -0.42316387]\n", 544 | "Intercept: 0.2565006830997194\n", 545 | "Mean squared error (MSE): 1.01\n", 546 | "Coefficient of determination (R^2): 0.77\n" 547 | ], 548 | "name": "stdout" 549 | } 550 | ] 551 | }, 552 | { 553 | "cell_type": "markdown", 554 | "metadata": { 555 | "id": "Yhuc402dZsk3", 556 | "colab_type": "text" 557 | }, 558 | "source": [ 559 | "## Model Equation" 560 | ] 561 | }, 562 | { 563 | "cell_type": "code", 564 | "metadata": { 565 | "id": "QnoUESmXZcMo", 566 | "colab_type": "code", 567 | "colab": { 568 | "base_uri": "https://localhost:8080/", 569 | "height": 34 570 | }, 571 | "outputId": "c2e3b76f-4d9a-425c-99e4-5793dc6c1620" 572 | }, 573 | "source": [ 574 | "print('LogS = %.2f %.2f LogP %.4f MW + %.4f RB %.2f AP' % (model.intercept_, model.coef_[0], model.coef_[1], model.coef_[2], model.coef_[3] ) )" 575 | ], 576 | "execution_count": 10, 577 | "outputs": [ 578 | { 579 | "output_type": "stream", 580 | "text": [ 581 | "LogS = 0.26 -0.74 LogP -0.0066 MW + 0.0032 RB -0.42 AP\n" 582 | ], 583 | "name": "stdout" 584 | } 585 | ] 586 | }, 587 | { 588 | "cell_type": "markdown", 589 | "metadata": { 590 | "id": "uWvxj1iSaL3n", 591 | "colab_type": "text" 592 | }, 593 | "source": [ 594 | "# Data Visualization (Experimental vs Predicted LogS for Training Data)" 595 | ] 596 | }, 597 | { 598 | "cell_type": "code", 599 | "metadata": { 600 | "id": "iPcFF0MjZlh8", 601 | "colab_type": "code", 602 | "colab": {} 603 | }, 604 | "source": [ 605 | "import matplotlib.pyplot as plt\n", 606 | "import numpy as np" 607 | ], 608 | "execution_count": 11, 609 | "outputs": [] 610 | }, 611 | { 612 | "cell_type": "code", 613 | "metadata": { 614 | "id": "QRNyIlGAaQQI", 615 | "colab_type": "code", 616 | "colab": { 617 | "base_uri": "https://localhost:8080/", 618 | "height": 351 619 | }, 620 | "outputId": "949bd284-5952-496f-a57e-47a1333fd50b" 621 | }, 622 | "source": [ 623 | "plt.figure(figsize=(5,5))\n", 624 | "plt.scatter(x=Y, y=Y_pred, c=\"#7CAE00\", alpha=0.3)\n", 625 | "\n", 626 | "# Add trendline\n", 627 | "# https://stackoverflow.com/questions/26447191/how-to-add-trendline-in-python-matplotlib-dot-scatter-graphs\n", 628 | "z = np.polyfit(Y, Y_pred, 1)\n", 629 | "p = np.poly1d(z)\n", 630 | "\n", 631 | "plt.plot(Y,p(Y),\"#F8766D\")\n", 632 | "plt.ylabel('Predicted LogS')\n", 633 | "plt.xlabel('Experimental LogS')" 634 | ], 635 | "execution_count": 17, 636 | "outputs": [ 637 | { 638 | "output_type": "execute_result", 639 | "data": { 640 | "text/plain": [ 641 | "Text(0.5, 0, 'Experimental LogS')" 642 | ] 643 | }, 644 | "metadata": { 645 | "tags": [] 646 | }, 647 | "execution_count": 17 648 | }, 649 | { 650 | "output_type": "display_data", 651 | "data": { 652 | "image/png": "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\n", 653 | "text/plain": [ 654 | "
" 655 | ] 656 | }, 657 | "metadata": { 658 | "tags": [], 659 | "needs_background": "light" 660 | } 661 | } 662 | ] 663 | }, 664 | { 665 | "cell_type": "markdown", 666 | "metadata": { 667 | "id": "YzKTmvZrbFVI", 668 | "colab_type": "text" 669 | }, 670 | "source": [ 671 | "# Save Model as Pickle Object" 672 | ] 673 | }, 674 | { 675 | "cell_type": "code", 676 | "metadata": { 677 | "id": "DzjpPyVyb8XO", 678 | "colab_type": "code", 679 | "colab": {} 680 | }, 681 | "source": [ 682 | "import pickle" 683 | ], 684 | "execution_count": 18, 685 | "outputs": [] 686 | }, 687 | { 688 | "cell_type": "code", 689 | "metadata": { 690 | "id": "b2K9ajBaaYUk", 691 | "colab_type": "code", 692 | "colab": {} 693 | }, 694 | "source": [ 695 | "pickle.dump(model, open('solubility_model.pkl', 'wb'))" 696 | ], 697 | "execution_count": 19, 698 | "outputs": [] 699 | }, 700 | { 701 | "cell_type": "code", 702 | "metadata": { 703 | "id": "ef4fyvrEb-NC", 704 | "colab_type": "code", 705 | "colab": {} 706 | }, 707 | "source": [ 708 | "" 709 | ], 710 | "execution_count": null, 711 | "outputs": [] 712 | } 713 | ] 714 | } 715 | -------------------------------------------------------------------------------- /app_10_regression_bioinformatics_solubility/solubility_model.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dataprofessor/streamlit_freecodecamp/d44c4c1320f8417b3d5494902e5366b715314528/app_10_regression_bioinformatics_solubility/solubility_model.pkl -------------------------------------------------------------------------------- /app_1_simple_stock_price/myapp.py: -------------------------------------------------------------------------------- 1 | import yfinance as yf 2 | import streamlit as st 3 | 4 | st.write(""" 5 | # Simple Stock Price App 6 | 7 | Shown are the stock closing price and volume of Google! 8 | 9 | """) 10 | 11 | # https://towardsdatascience.com/how-to-get-stock-data-using-python-c0de1df17e75 12 | #define the ticker symbol 13 | tickerSymbol = 'GOOGL' 14 | #get data on this ticker 15 | tickerData = yf.Ticker(tickerSymbol) 16 | #get the historical prices for this ticker 17 | tickerDf = tickerData.history(period='1d', start='2010-5-31', end='2020-5-31') 18 | # Open High Low Close Volume Dividends Stock Splits 19 | 20 | st.line_chart(tickerDf.Close) 21 | st.line_chart(tickerDf.Volume) 22 | -------------------------------------------------------------------------------- /app_1_simple_stock_price/myapp2.py: -------------------------------------------------------------------------------- 1 | import yfinance as yf 2 | import streamlit as st 3 | 4 | st.write(""" 5 | # Simple Stock Price App 6 | 7 | Shown are the stock **closing price** and ***volume*** of Google! 8 | 9 | """) 10 | 11 | # https://towardsdatascience.com/how-to-get-stock-data-using-python-c0de1df17e75 12 | #define the ticker symbol 13 | tickerSymbol = 'GOOGL' 14 | #get data on this ticker 15 | tickerData = yf.Ticker(tickerSymbol) 16 | #get the historical prices for this ticker 17 | tickerDf = tickerData.history(period='1d', start='2010-5-31', end='2020-5-31') 18 | # Open High Low Close Volume Dividends Stock Splits 19 | 20 | st.write(""" 21 | ## Closing Price 22 | """) 23 | st.line_chart(tickerDf.Close) 24 | st.write(""" 25 | ## Volume Price 26 | """) 27 | st.line_chart(tickerDf.Volume) 28 | -------------------------------------------------------------------------------- /app_2_simple_bioinformatics_dna/dna-app.py: -------------------------------------------------------------------------------- 1 | ###################### 2 | # Import libraries 3 | ###################### 4 | 5 | import pandas as pd 6 | import streamlit as st 7 | import altair as alt 8 | from PIL import Image 9 | 10 | ###################### 11 | # Page Title 12 | ###################### 13 | 14 | image = Image.open('dna-logo.jpg') 15 | 16 | st.image(image, use_column_width=True) 17 | 18 | st.write(""" 19 | # DNA Nucleotide Count Web App 20 | 21 | This app counts the nucleotide composition of query DNA! 22 | 23 | *** 24 | """) 25 | 26 | 27 | ###################### 28 | # Input Text Box 29 | ###################### 30 | 31 | #st.sidebar.header('Enter DNA sequence') 32 | st.header('Enter DNA sequence') 33 | 34 | sequence_input = ">DNA Query 2\nGAACACGTGGAGGCAAACAGGAAGGTGAAGAAGAACTTATCCTATCAGGACGGAAGGTCCTGTGCTCGGG\nATCTTCCAGACGTCGCGACTCTAAATTGCCCCCTCTGAGGTCAAGGAACACAAGATGGTTTTGGAAATGC\nTGAACCCGATACATTATAACATCACCAGCATCGTGCCTGAAGCCATGCCTGCTGCCACCATGCCAGTCCT" 35 | 36 | #sequence = st.sidebar.text_area("Sequence input", sequence_input, height=250) 37 | sequence = st.text_area("Sequence input", sequence_input, height=250) 38 | sequence = sequence.splitlines() 39 | sequence = sequence[1:] # Skips the sequence name (first line) 40 | sequence = ''.join(sequence) # Concatenates list to string 41 | 42 | st.write(""" 43 | *** 44 | """) 45 | 46 | ## Prints the input DNA sequence 47 | st.header('INPUT (DNA Query)') 48 | sequence 49 | 50 | ## DNA nucleotide count 51 | st.header('OUTPUT (DNA Nucleotide Count)') 52 | 53 | ### 1. Print dictionary 54 | st.subheader('1. Print dictionary') 55 | def DNA_nucleotide_count(seq): 56 | d = dict([ 57 | ('A',seq.count('A')), 58 | ('T',seq.count('T')), 59 | ('G',seq.count('G')), 60 | ('C',seq.count('C')) 61 | ]) 62 | return d 63 | 64 | X = DNA_nucleotide_count(sequence) 65 | 66 | #X_label = list(X) 67 | #X_values = list(X.values()) 68 | 69 | X 70 | 71 | ### 2. Print text 72 | st.subheader('2. Print text') 73 | st.write('There are ' + str(X['A']) + ' adenine (A)') 74 | st.write('There are ' + str(X['T']) + ' thymine (T)') 75 | st.write('There are ' + str(X['G']) + ' guanine (G)') 76 | st.write('There are ' + str(X['C']) + ' cytosine (C)') 77 | 78 | ### 3. Display DataFrame 79 | st.subheader('3. Display DataFrame') 80 | df = pd.DataFrame.from_dict(X, orient='index') 81 | df = df.rename({0: 'count'}, axis='columns') 82 | df.reset_index(inplace=True) 83 | df = df.rename(columns = {'index':'nucleotide'}) 84 | st.write(df) 85 | 86 | ### 4. Display Bar Chart using Altair 87 | st.subheader('4. Display Bar chart') 88 | p = alt.Chart(df).mark_bar().encode( 89 | x='nucleotide', 90 | y='count' 91 | ) 92 | p = p.properties( 93 | width=alt.Step(80) # controls width of bar. 94 | ) 95 | st.write(p) 96 | -------------------------------------------------------------------------------- /app_2_simple_bioinformatics_dna/dna-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dataprofessor/streamlit_freecodecamp/d44c4c1320f8417b3d5494902e5366b715314528/app_2_simple_bioinformatics_dna/dna-logo.jpg -------------------------------------------------------------------------------- /app_3_eda_basketball/basketball_app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import base64 4 | import matplotlib.pyplot as plt 5 | import seaborn as sns 6 | import numpy as np 7 | 8 | st.title('NBA Player Stats Explorer') 9 | 10 | st.markdown(""" 11 | This app performs simple webscraping of NBA player stats data! 12 | * **Python libraries:** base64, pandas, streamlit 13 | * **Data source:** [Basketball-reference.com](https://www.basketball-reference.com/). 14 | """) 15 | 16 | st.sidebar.header('User Input Features') 17 | selected_year = st.sidebar.selectbox('Year', list(reversed(range(1950,2020)))) 18 | 19 | # Web scraping of NBA player stats 20 | @st.cache 21 | def load_data(year): 22 | url = "https://www.basketball-reference.com/leagues/NBA_" + str(year) + "_per_game.html" 23 | html = pd.read_html(url, header = 0) 24 | df = html[0] 25 | raw = df.drop(df[df.Age == 'Age'].index) # Deletes repeating headers in content 26 | raw = raw.fillna(0) 27 | playerstats = raw.drop(['Rk'], axis=1) 28 | return playerstats 29 | playerstats = load_data(selected_year) 30 | 31 | # Sidebar - Team selection 32 | sorted_unique_team = sorted(playerstats.Tm.unique()) 33 | selected_team = st.sidebar.multiselect('Team', sorted_unique_team, sorted_unique_team) 34 | 35 | # Sidebar - Position selection 36 | unique_pos = ['C','PF','SF','PG','SG'] 37 | selected_pos = st.sidebar.multiselect('Position', unique_pos, unique_pos) 38 | 39 | # Filtering data 40 | df_selected_team = playerstats[(playerstats.Tm.isin(selected_team)) & (playerstats.Pos.isin(selected_pos))] 41 | 42 | st.header('Display Player Stats of Selected Team(s)') 43 | st.write('Data Dimension: ' + str(df_selected_team.shape[0]) + ' rows and ' + str(df_selected_team.shape[1]) + ' columns.') 44 | st.dataframe(df_selected_team) 45 | 46 | # Download NBA player stats data 47 | # https://discuss.streamlit.io/t/how-to-download-file-in-streamlit/1806 48 | def filedownload(df): 49 | csv = df.to_csv(index=False) 50 | b64 = base64.b64encode(csv.encode()).decode() # strings <-> bytes conversions 51 | href = f'Download CSV File' 52 | return href 53 | 54 | st.markdown(filedownload(df_selected_team), unsafe_allow_html=True) 55 | 56 | # Heatmap 57 | if st.button('Intercorrelation Heatmap'): 58 | st.header('Intercorrelation Matrix Heatmap') 59 | df_selected_team.to_csv('output.csv',index=False) 60 | df = pd.read_csv('output.csv') 61 | 62 | corr = df.corr() 63 | mask = np.zeros_like(corr) 64 | mask[np.triu_indices_from(mask)] = True 65 | with sns.axes_style("white"): 66 | f, ax = plt.subplots(figsize=(7, 5)) 67 | ax = sns.heatmap(corr, mask=mask, vmax=1, square=True) 68 | st.pyplot() 69 | -------------------------------------------------------------------------------- /app_4_eda_football/football_app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import base64 4 | import matplotlib.pyplot as plt 5 | import seaborn as sns 6 | import numpy as np 7 | 8 | st.title('NFL Football Stats (Rushing) Explorer') 9 | 10 | st.markdown(""" 11 | This app performs simple webscraping of NFL Football player stats data (focusing on Rushing)! 12 | * **Python libraries:** base64, pandas, streamlit, numpy, matplotlib, seaborn 13 | * **Data source:** [pro-football-reference.com](https://www.pro-football-reference.com/). 14 | """) 15 | 16 | st.sidebar.header('User Input Features') 17 | selected_year = st.sidebar.selectbox('Year', list(reversed(range(1990,2020)))) 18 | 19 | # Web scraping of NFL player stats 20 | # https://www.pro-football-reference.com/years/2019/rushing.htm 21 | @st.cache 22 | def load_data(year): 23 | url = "https://www.pro-football-reference.com/years/" + str(year) + "/rushing.htm" 24 | html = pd.read_html(url, header = 1) 25 | df = html[0] 26 | raw = df.drop(df[df.Age == 'Age'].index) # Deletes repeating headers in content 27 | raw = raw.fillna(0) 28 | playerstats = raw.drop(['Rk'], axis=1) 29 | return playerstats 30 | playerstats = load_data(selected_year) 31 | 32 | # Sidebar - Team selection 33 | sorted_unique_team = sorted(playerstats.Tm.unique()) 34 | selected_team = st.sidebar.multiselect('Team', sorted_unique_team, sorted_unique_team) 35 | 36 | # Sidebar - Position selection 37 | unique_pos = ['RB','QB','WR','FB','TE'] 38 | selected_pos = st.sidebar.multiselect('Position', unique_pos, unique_pos) 39 | 40 | # Filtering data 41 | df_selected_team = playerstats[(playerstats.Tm.isin(selected_team)) & (playerstats.Pos.isin(selected_pos))] 42 | 43 | st.header('Display Player Stats of Selected Team(s)') 44 | st.write('Data Dimension: ' + str(df_selected_team.shape[0]) + ' rows and ' + str(df_selected_team.shape[1]) + ' columns.') 45 | st.dataframe(df_selected_team) 46 | 47 | # Download NBA player stats data 48 | # https://discuss.streamlit.io/t/how-to-download-file-in-streamlit/1806 49 | def filedownload(df): 50 | csv = df.to_csv(index=False) 51 | b64 = base64.b64encode(csv.encode()).decode() # strings <-> bytes conversions 52 | href = f'Download CSV File' 53 | return href 54 | 55 | st.markdown(filedownload(df_selected_team), unsafe_allow_html=True) 56 | 57 | # Heatmap 58 | if st.button('Intercorrelation Heatmap'): 59 | st.header('Intercorrelation Matrix Heatmap') 60 | df_selected_team.to_csv('output.csv',index=False) 61 | df = pd.read_csv('output.csv') 62 | 63 | corr = df.corr() 64 | mask = np.zeros_like(corr) 65 | mask[np.triu_indices_from(mask)] = True 66 | with sns.axes_style("white"): 67 | f, ax = plt.subplots(figsize=(7, 5)) 68 | ax = sns.heatmap(corr, mask=mask, vmax=1, square=True) 69 | st.pyplot() 70 | -------------------------------------------------------------------------------- /app_5_eda_sp500_stock/sp500-app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import base64 4 | import matplotlib.pyplot as plt 5 | import seaborn as sns 6 | import numpy as np 7 | import yfinance as yf 8 | 9 | st.title('S&P 500 App') 10 | 11 | st.markdown(""" 12 | This app retrieves the list of the **S&P 500** (from Wikipedia) and its corresponding **stock closing price** (year-to-date)! 13 | * **Python libraries:** base64, pandas, streamlit, numpy, matplotlib, seaborn 14 | * **Data source:** [Wikipedia](https://en.wikipedia.org/wiki/List_of_S%26P_500_companies). 15 | """) 16 | 17 | st.sidebar.header('User Input Features') 18 | 19 | # Web scraping of S&P 500 data 20 | # 21 | @st.cache 22 | def load_data(): 23 | url = 'https://en.wikipedia.org/wiki/List_of_S%26P_500_companies' 24 | html = pd.read_html(url, header = 0) 25 | df = html[0] 26 | return df 27 | 28 | df = load_data() 29 | sector = df.groupby('GICS Sector') 30 | 31 | # Sidebar - Sector selection 32 | sorted_sector_unique = sorted( df['GICS Sector'].unique() ) 33 | selected_sector = st.sidebar.multiselect('Sector', sorted_sector_unique, sorted_sector_unique) 34 | 35 | # Filtering data 36 | df_selected_sector = df[ (df['GICS Sector'].isin(selected_sector)) ] 37 | 38 | st.header('Display Companies in Selected Sector') 39 | st.write('Data Dimension: ' + str(df_selected_sector.shape[0]) + ' rows and ' + str(df_selected_sector.shape[1]) + ' columns.') 40 | st.dataframe(df_selected_sector) 41 | 42 | # Download S&P500 data 43 | # https://discuss.streamlit.io/t/how-to-download-file-in-streamlit/1806 44 | def filedownload(df): 45 | csv = df.to_csv(index=False) 46 | b64 = base64.b64encode(csv.encode()).decode() # strings <-> bytes conversions 47 | href = f'Download CSV File' 48 | return href 49 | 50 | st.markdown(filedownload(df_selected_sector), unsafe_allow_html=True) 51 | 52 | # https://pypi.org/project/yfinance/ 53 | 54 | data = yf.download( 55 | tickers = list(df_selected_sector[:10].Symbol), 56 | period = "ytd", 57 | interval = "1d", 58 | group_by = 'ticker', 59 | auto_adjust = True, 60 | prepost = True, 61 | threads = True, 62 | proxy = None 63 | ) 64 | 65 | # Plot Closing Price of Query Symbol 66 | def price_plot(symbol): 67 | df = pd.DataFrame(data[symbol].Close) 68 | df['Date'] = df.index 69 | plt.fill_between(df.Date, df.Close, color='skyblue', alpha=0.3) 70 | plt.plot(df.Date, df.Close, color='skyblue', alpha=0.8) 71 | plt.xticks(rotation=90) 72 | plt.title(symbol, fontweight='bold') 73 | plt.xlabel('Date', fontweight='bold') 74 | plt.ylabel('Closing Price', fontweight='bold') 75 | return st.pyplot() 76 | 77 | num_company = st.sidebar.slider('Number of Companies', 1, 5) 78 | 79 | if st.button('Show Plots'): 80 | st.header('Stock Closing Price') 81 | for i in list(df_selected_sector.Symbol)[:num_company]: 82 | price_plot(i) 83 | -------------------------------------------------------------------------------- /app_6_eda_cryptocurrency/crypto-price-app.py: -------------------------------------------------------------------------------- 1 | # This app is for educational purpose only. Insights gained is not financial advice. Use at your own risk! 2 | import streamlit as st 3 | from PIL import Image 4 | import pandas as pd 5 | import base64 6 | import matplotlib.pyplot as plt 7 | from bs4 import BeautifulSoup 8 | import requests 9 | import json 10 | import time 11 | #---------------------------------# 12 | # New feature (make sure to upgrade your streamlit library) 13 | # pip install --upgrade streamlit 14 | 15 | #---------------------------------# 16 | # Page layout 17 | ## Page expands to full width 18 | st.set_page_config(layout="wide") 19 | #---------------------------------# 20 | # Title 21 | 22 | image = Image.open('logo.jpg') 23 | 24 | st.image(image, width = 500) 25 | 26 | st.title('Crypto Price App') 27 | st.markdown(""" 28 | This app retrieves cryptocurrency prices for the top 100 cryptocurrency from the **CoinMarketCap**! 29 | 30 | """) 31 | #---------------------------------# 32 | # About 33 | expander_bar = st.beta_expander("About") 34 | expander_bar.markdown(""" 35 | * **Python libraries:** base64, pandas, streamlit, numpy, matplotlib, seaborn, BeautifulSoup, requests, json, time 36 | * **Data source:** [CoinMarketCap](http://coinmarketcap.com). 37 | * **Credit:** Web scraper adapted from the Medium article *[Web Scraping Crypto Prices With Python](https://towardsdatascience.com/web-scraping-crypto-prices-with-python-41072ea5b5bf)* written by [Bryan Feng](https://medium.com/@bryanf). 38 | """) 39 | 40 | 41 | #---------------------------------# 42 | # Page layout (continued) 43 | ## Divide page to 3 columns (col1 = sidebar, col2 and col3 = page contents) 44 | col1 = st.sidebar 45 | col2, col3 = st.beta_columns((2,1)) 46 | 47 | #---------------------------------# 48 | # Sidebar + Main panel 49 | col1.header('Input Options') 50 | 51 | ## Sidebar - Currency price unit 52 | currency_price_unit = col1.selectbox('Select currency for price', ('USD', 'BTC', 'ETH')) 53 | 54 | # Web scraping of CoinMarketCap data 55 | @st.cache 56 | def load_data(): 57 | cmc = requests.get('https://coinmarketcap.com') 58 | soup = BeautifulSoup(cmc.content, 'html.parser') 59 | 60 | data = soup.find('script', id='__NEXT_DATA__', type='application/json') 61 | coins = {} 62 | coin_data = json.loads(data.contents[0]) 63 | listings = coin_data['props']['initialState']['cryptocurrency']['listingLatest']['data'] 64 | for i in listings: 65 | coins[str(i['id'])] = i['slug'] 66 | 67 | coin_name = [] 68 | coin_symbol = [] 69 | market_cap = [] 70 | percent_change_1h = [] 71 | percent_change_24h = [] 72 | percent_change_7d = [] 73 | price = [] 74 | volume_24h = [] 75 | 76 | for i in listings: 77 | coin_name.append(i['slug']) 78 | coin_symbol.append(i['symbol']) 79 | price.append(i['quote'][currency_price_unit]['price']) 80 | percent_change_1h.append(i['quote'][currency_price_unit]['percent_change_1h']) 81 | percent_change_24h.append(i['quote'][currency_price_unit]['percent_change_24h']) 82 | percent_change_7d.append(i['quote'][currency_price_unit]['percent_change_7d']) 83 | market_cap.append(i['quote'][currency_price_unit]['market_cap']) 84 | volume_24h.append(i['quote'][currency_price_unit]['volume_24h']) 85 | 86 | df = pd.DataFrame(columns=['coin_name', 'coin_symbol', 'market_cap', 'percent_change_1h', 'percent_change_24h', 'percent_change_7d', 'price', 'volume_24h']) 87 | df['coin_name'] = coin_name 88 | df['coin_symbol'] = coin_symbol 89 | df['price'] = price 90 | df['percent_change_1h'] = percent_change_1h 91 | df['percent_change_24h'] = percent_change_24h 92 | df['percent_change_7d'] = percent_change_7d 93 | df['market_cap'] = market_cap 94 | df['volume_24h'] = volume_24h 95 | return df 96 | 97 | df = load_data() 98 | 99 | ## Sidebar - Cryptocurrency selections 100 | sorted_coin = sorted( df['coin_symbol'] ) 101 | selected_coin = col1.multiselect('Cryptocurrency', sorted_coin, sorted_coin) 102 | 103 | df_selected_coin = df[ (df['coin_symbol'].isin(selected_coin)) ] # Filtering data 104 | 105 | ## Sidebar - Number of coins to display 106 | num_coin = col1.slider('Display Top N Coins', 1, 100, 100) 107 | df_coins = df_selected_coin[:num_coin] 108 | 109 | ## Sidebar - Percent change timeframe 110 | percent_timeframe = col1.selectbox('Percent change time frame', 111 | ['7d','24h', '1h']) 112 | percent_dict = {"7d":'percent_change_7d',"24h":'percent_change_24h',"1h":'percent_change_1h'} 113 | selected_percent_timeframe = percent_dict[percent_timeframe] 114 | 115 | ## Sidebar - Sorting values 116 | sort_values = col1.selectbox('Sort values?', ['Yes', 'No']) 117 | 118 | col2.subheader('Price Data of Selected Cryptocurrency') 119 | col2.write('Data Dimension: ' + str(df_selected_coin.shape[0]) + ' rows and ' + str(df_selected_coin.shape[1]) + ' columns.') 120 | 121 | col2.dataframe(df_coins) 122 | 123 | # Download CSV data 124 | # https://discuss.streamlit.io/t/how-to-download-file-in-streamlit/1806 125 | def filedownload(df): 126 | csv = df.to_csv(index=False) 127 | b64 = base64.b64encode(csv.encode()).decode() # strings <-> bytes conversions 128 | href = f'Download CSV File' 129 | return href 130 | 131 | col2.markdown(filedownload(df_selected_coin), unsafe_allow_html=True) 132 | 133 | #---------------------------------# 134 | # Preparing data for Bar plot of % Price change 135 | col2.subheader('Table of % Price Change') 136 | df_change = pd.concat([df_coins.coin_symbol, df_coins.percent_change_1h, df_coins.percent_change_24h, df_coins.percent_change_7d], axis=1) 137 | df_change = df_change.set_index('coin_symbol') 138 | df_change['positive_percent_change_1h'] = df_change['percent_change_1h'] > 0 139 | df_change['positive_percent_change_24h'] = df_change['percent_change_24h'] > 0 140 | df_change['positive_percent_change_7d'] = df_change['percent_change_7d'] > 0 141 | col2.dataframe(df_change) 142 | 143 | # Conditional creation of Bar plot (time frame) 144 | col3.subheader('Bar plot of % Price Change') 145 | 146 | if percent_timeframe == '7d': 147 | if sort_values == 'Yes': 148 | df_change = df_change.sort_values(by=['percent_change_7d']) 149 | col3.write('*7 days period*') 150 | plt.figure(figsize=(5,25)) 151 | plt.subplots_adjust(top = 1, bottom = 0) 152 | df_change['percent_change_7d'].plot(kind='barh', color=df_change.positive_percent_change_7d.map({True: 'g', False: 'r'})) 153 | col3.pyplot(plt) 154 | elif percent_timeframe == '24h': 155 | if sort_values == 'Yes': 156 | df_change = df_change.sort_values(by=['percent_change_24h']) 157 | col3.write('*24 hour period*') 158 | plt.figure(figsize=(5,25)) 159 | plt.subplots_adjust(top = 1, bottom = 0) 160 | df_change['percent_change_24h'].plot(kind='barh', color=df_change.positive_percent_change_24h.map({True: 'g', False: 'r'})) 161 | col3.pyplot(plt) 162 | else: 163 | if sort_values == 'Yes': 164 | df_change = df_change.sort_values(by=['percent_change_1h']) 165 | col3.write('*1 hour period*') 166 | plt.figure(figsize=(5,25)) 167 | plt.subplots_adjust(top = 1, bottom = 0) 168 | df_change['percent_change_1h'].plot(kind='barh', color=df_change.positive_percent_change_1h.map({True: 'g', False: 'r'})) 169 | col3.pyplot(plt) 170 | -------------------------------------------------------------------------------- /app_6_eda_cryptocurrency/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dataprofessor/streamlit_freecodecamp/d44c4c1320f8417b3d5494902e5366b715314528/app_6_eda_cryptocurrency/logo.jpg -------------------------------------------------------------------------------- /app_7_classification_iris/iris-ml-app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | from sklearn import datasets 4 | from sklearn.ensemble import RandomForestClassifier 5 | 6 | st.write(""" 7 | # Simple Iris Flower Prediction App 8 | 9 | This app predicts the **Iris flower** type! 10 | """) 11 | 12 | st.sidebar.header('User Input Parameters') 13 | 14 | def user_input_features(): 15 | sepal_length = st.sidebar.slider('Sepal length', 4.3, 7.9, 5.4) 16 | sepal_width = st.sidebar.slider('Sepal width', 2.0, 4.4, 3.4) 17 | petal_length = st.sidebar.slider('Petal length', 1.0, 6.9, 1.3) 18 | petal_width = st.sidebar.slider('Petal width', 0.1, 2.5, 0.2) 19 | data = {'sepal_length': sepal_length, 20 | 'sepal_width': sepal_width, 21 | 'petal_length': petal_length, 22 | 'petal_width': petal_width} 23 | features = pd.DataFrame(data, index=[0]) 24 | return features 25 | 26 | df = user_input_features() 27 | 28 | st.subheader('User Input parameters') 29 | st.write(df) 30 | 31 | iris = datasets.load_iris() 32 | X = iris.data 33 | Y = iris.target 34 | 35 | clf = RandomForestClassifier() 36 | clf.fit(X, Y) 37 | 38 | prediction = clf.predict(df) 39 | prediction_proba = clf.predict_proba(df) 40 | 41 | st.subheader('Class labels and their corresponding index number') 42 | st.write(iris.target_names) 43 | 44 | st.subheader('Prediction') 45 | st.write(iris.target_names[prediction]) 46 | #st.write(prediction) 47 | 48 | st.subheader('Prediction Probability') 49 | st.write(prediction_proba) 50 | -------------------------------------------------------------------------------- /app_8_classification_penguins/penguins-app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import numpy as np 4 | import pickle 5 | from sklearn.ensemble import RandomForestClassifier 6 | 7 | st.write(""" 8 | # Penguin Prediction App 9 | 10 | This app predicts the **Palmer Penguin** species! 11 | 12 | Data obtained from the [palmerpenguins library](https://github.com/allisonhorst/palmerpenguins) in R by Allison Horst. 13 | """) 14 | 15 | st.sidebar.header('User Input Features') 16 | 17 | st.sidebar.markdown(""" 18 | [Example CSV input file](https://raw.githubusercontent.com/dataprofessor/data/master/penguins_example.csv) 19 | """) 20 | 21 | # Collects user input features into dataframe 22 | uploaded_file = st.sidebar.file_uploader("Upload your input CSV file", type=["csv"]) 23 | if uploaded_file is not None: 24 | input_df = pd.read_csv(uploaded_file) 25 | else: 26 | def user_input_features(): 27 | island = st.sidebar.selectbox('Island',('Biscoe','Dream','Torgersen')) 28 | sex = st.sidebar.selectbox('Sex',('male','female')) 29 | bill_length_mm = st.sidebar.slider('Bill length (mm)', 32.1,59.6,43.9) 30 | bill_depth_mm = st.sidebar.slider('Bill depth (mm)', 13.1,21.5,17.2) 31 | flipper_length_mm = st.sidebar.slider('Flipper length (mm)', 172.0,231.0,201.0) 32 | body_mass_g = st.sidebar.slider('Body mass (g)', 2700.0,6300.0,4207.0) 33 | data = {'island': island, 34 | 'bill_length_mm': bill_length_mm, 35 | 'bill_depth_mm': bill_depth_mm, 36 | 'flipper_length_mm': flipper_length_mm, 37 | 'body_mass_g': body_mass_g, 38 | 'sex': sex} 39 | features = pd.DataFrame(data, index=[0]) 40 | return features 41 | input_df = user_input_features() 42 | 43 | # Combines user input features with entire penguins dataset 44 | # This will be useful for the encoding phase 45 | penguins_raw = pd.read_csv('penguins_cleaned.csv') 46 | penguins = penguins_raw.drop(columns=['species']) 47 | df = pd.concat([input_df,penguins],axis=0) 48 | 49 | # Encoding of ordinal features 50 | # https://www.kaggle.com/pratik1120/penguin-dataset-eda-classification-and-clustering 51 | encode = ['sex','island'] 52 | for col in encode: 53 | dummy = pd.get_dummies(df[col], prefix=col) 54 | df = pd.concat([df,dummy], axis=1) 55 | del df[col] 56 | df = df[:1] # Selects only the first row (the user input data) 57 | 58 | # Displays the user input features 59 | st.subheader('User Input features') 60 | 61 | if uploaded_file is not None: 62 | st.write(df) 63 | else: 64 | st.write('Awaiting CSV file to be uploaded. Currently using example input parameters (shown below).') 65 | st.write(df) 66 | 67 | # Reads in saved classification model 68 | load_clf = pickle.load(open('penguins_clf.pkl', 'rb')) 69 | 70 | # Apply model to make predictions 71 | prediction = load_clf.predict(df) 72 | prediction_proba = load_clf.predict_proba(df) 73 | 74 | 75 | st.subheader('Prediction') 76 | penguins_species = np.array(['Adelie','Chinstrap','Gentoo']) 77 | st.write(penguins_species[prediction]) 78 | 79 | st.subheader('Prediction Probability') 80 | st.write(prediction_proba) 81 | -------------------------------------------------------------------------------- /app_8_classification_penguins/penguins-model-building.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | penguins = pd.read_csv('penguins_cleaned.csv') 3 | 4 | # Ordinal feature encoding 5 | # https://www.kaggle.com/pratik1120/penguin-dataset-eda-classification-and-clustering 6 | df = penguins.copy() 7 | target = 'species' 8 | encode = ['sex','island'] 9 | 10 | for col in encode: 11 | dummy = pd.get_dummies(df[col], prefix=col) 12 | df = pd.concat([df,dummy], axis=1) 13 | del df[col] 14 | 15 | target_mapper = {'Adelie':0, 'Chinstrap':1, 'Gentoo':2} 16 | def target_encode(val): 17 | return target_mapper[val] 18 | 19 | df['species'] = df['species'].apply(target_encode) 20 | 21 | # Separating X and y 22 | X = df.drop('species', axis=1) 23 | Y = df['species'] 24 | 25 | # Build random forest model 26 | from sklearn.ensemble import RandomForestClassifier 27 | clf = RandomForestClassifier() 28 | clf.fit(X, Y) 29 | 30 | # Saving the model 31 | import pickle 32 | pickle.dump(clf, open('penguins_clf.pkl', 'wb')) 33 | -------------------------------------------------------------------------------- /app_8_classification_penguins/penguins_cleaned.csv: -------------------------------------------------------------------------------- 1 | "species","island","bill_length_mm","bill_depth_mm","flipper_length_mm","body_mass_g","sex" 2 | "Adelie","Torgersen",39.1,18.7,181,3750,"male" 3 | "Adelie","Torgersen",39.5,17.4,186,3800,"female" 4 | "Adelie","Torgersen",40.3,18,195,3250,"female" 5 | "Adelie","Torgersen",36.7,19.3,193,3450,"female" 6 | "Adelie","Torgersen",39.3,20.6,190,3650,"male" 7 | "Adelie","Torgersen",38.9,17.8,181,3625,"female" 8 | "Adelie","Torgersen",39.2,19.6,195,4675,"male" 9 | "Adelie","Torgersen",41.1,17.6,182,3200,"female" 10 | "Adelie","Torgersen",38.6,21.2,191,3800,"male" 11 | "Adelie","Torgersen",34.6,21.1,198,4400,"male" 12 | "Adelie","Torgersen",36.6,17.8,185,3700,"female" 13 | "Adelie","Torgersen",38.7,19,195,3450,"female" 14 | "Adelie","Torgersen",42.5,20.7,197,4500,"male" 15 | "Adelie","Torgersen",34.4,18.4,184,3325,"female" 16 | "Adelie","Torgersen",46,21.5,194,4200,"male" 17 | "Adelie","Biscoe",37.8,18.3,174,3400,"female" 18 | "Adelie","Biscoe",37.7,18.7,180,3600,"male" 19 | "Adelie","Biscoe",35.9,19.2,189,3800,"female" 20 | "Adelie","Biscoe",38.2,18.1,185,3950,"male" 21 | "Adelie","Biscoe",38.8,17.2,180,3800,"male" 22 | "Adelie","Biscoe",35.3,18.9,187,3800,"female" 23 | "Adelie","Biscoe",40.6,18.6,183,3550,"male" 24 | "Adelie","Biscoe",40.5,17.9,187,3200,"female" 25 | "Adelie","Biscoe",37.9,18.6,172,3150,"female" 26 | "Adelie","Biscoe",40.5,18.9,180,3950,"male" 27 | "Adelie","Dream",39.5,16.7,178,3250,"female" 28 | "Adelie","Dream",37.2,18.1,178,3900,"male" 29 | "Adelie","Dream",39.5,17.8,188,3300,"female" 30 | "Adelie","Dream",40.9,18.9,184,3900,"male" 31 | "Adelie","Dream",36.4,17,195,3325,"female" 32 | "Adelie","Dream",39.2,21.1,196,4150,"male" 33 | "Adelie","Dream",38.8,20,190,3950,"male" 34 | "Adelie","Dream",42.2,18.5,180,3550,"female" 35 | "Adelie","Dream",37.6,19.3,181,3300,"female" 36 | "Adelie","Dream",39.8,19.1,184,4650,"male" 37 | "Adelie","Dream",36.5,18,182,3150,"female" 38 | "Adelie","Dream",40.8,18.4,195,3900,"male" 39 | "Adelie","Dream",36,18.5,186,3100,"female" 40 | "Adelie","Dream",44.1,19.7,196,4400,"male" 41 | "Adelie","Dream",37,16.9,185,3000,"female" 42 | "Adelie","Dream",39.6,18.8,190,4600,"male" 43 | "Adelie","Dream",41.1,19,182,3425,"male" 44 | "Adelie","Dream",36,17.9,190,3450,"female" 45 | "Adelie","Dream",42.3,21.2,191,4150,"male" 46 | "Adelie","Biscoe",39.6,17.7,186,3500,"female" 47 | "Adelie","Biscoe",40.1,18.9,188,4300,"male" 48 | "Adelie","Biscoe",35,17.9,190,3450,"female" 49 | "Adelie","Biscoe",42,19.5,200,4050,"male" 50 | "Adelie","Biscoe",34.5,18.1,187,2900,"female" 51 | "Adelie","Biscoe",41.4,18.6,191,3700,"male" 52 | "Adelie","Biscoe",39,17.5,186,3550,"female" 53 | "Adelie","Biscoe",40.6,18.8,193,3800,"male" 54 | "Adelie","Biscoe",36.5,16.6,181,2850,"female" 55 | "Adelie","Biscoe",37.6,19.1,194,3750,"male" 56 | "Adelie","Biscoe",35.7,16.9,185,3150,"female" 57 | "Adelie","Biscoe",41.3,21.1,195,4400,"male" 58 | "Adelie","Biscoe",37.6,17,185,3600,"female" 59 | "Adelie","Biscoe",41.1,18.2,192,4050,"male" 60 | "Adelie","Biscoe",36.4,17.1,184,2850,"female" 61 | "Adelie","Biscoe",41.6,18,192,3950,"male" 62 | "Adelie","Biscoe",35.5,16.2,195,3350,"female" 63 | "Adelie","Biscoe",41.1,19.1,188,4100,"male" 64 | "Adelie","Torgersen",35.9,16.6,190,3050,"female" 65 | "Adelie","Torgersen",41.8,19.4,198,4450,"male" 66 | "Adelie","Torgersen",33.5,19,190,3600,"female" 67 | "Adelie","Torgersen",39.7,18.4,190,3900,"male" 68 | "Adelie","Torgersen",39.6,17.2,196,3550,"female" 69 | "Adelie","Torgersen",45.8,18.9,197,4150,"male" 70 | "Adelie","Torgersen",35.5,17.5,190,3700,"female" 71 | "Adelie","Torgersen",42.8,18.5,195,4250,"male" 72 | "Adelie","Torgersen",40.9,16.8,191,3700,"female" 73 | "Adelie","Torgersen",37.2,19.4,184,3900,"male" 74 | "Adelie","Torgersen",36.2,16.1,187,3550,"female" 75 | "Adelie","Torgersen",42.1,19.1,195,4000,"male" 76 | "Adelie","Torgersen",34.6,17.2,189,3200,"female" 77 | "Adelie","Torgersen",42.9,17.6,196,4700,"male" 78 | "Adelie","Torgersen",36.7,18.8,187,3800,"female" 79 | "Adelie","Torgersen",35.1,19.4,193,4200,"male" 80 | "Adelie","Dream",37.3,17.8,191,3350,"female" 81 | "Adelie","Dream",41.3,20.3,194,3550,"male" 82 | "Adelie","Dream",36.3,19.5,190,3800,"male" 83 | "Adelie","Dream",36.9,18.6,189,3500,"female" 84 | "Adelie","Dream",38.3,19.2,189,3950,"male" 85 | "Adelie","Dream",38.9,18.8,190,3600,"female" 86 | "Adelie","Dream",35.7,18,202,3550,"female" 87 | "Adelie","Dream",41.1,18.1,205,4300,"male" 88 | "Adelie","Dream",34,17.1,185,3400,"female" 89 | "Adelie","Dream",39.6,18.1,186,4450,"male" 90 | "Adelie","Dream",36.2,17.3,187,3300,"female" 91 | "Adelie","Dream",40.8,18.9,208,4300,"male" 92 | "Adelie","Dream",38.1,18.6,190,3700,"female" 93 | "Adelie","Dream",40.3,18.5,196,4350,"male" 94 | "Adelie","Dream",33.1,16.1,178,2900,"female" 95 | "Adelie","Dream",43.2,18.5,192,4100,"male" 96 | "Adelie","Biscoe",35,17.9,192,3725,"female" 97 | "Adelie","Biscoe",41,20,203,4725,"male" 98 | "Adelie","Biscoe",37.7,16,183,3075,"female" 99 | "Adelie","Biscoe",37.8,20,190,4250,"male" 100 | "Adelie","Biscoe",37.9,18.6,193,2925,"female" 101 | "Adelie","Biscoe",39.7,18.9,184,3550,"male" 102 | "Adelie","Biscoe",38.6,17.2,199,3750,"female" 103 | "Adelie","Biscoe",38.2,20,190,3900,"male" 104 | "Adelie","Biscoe",38.1,17,181,3175,"female" 105 | "Adelie","Biscoe",43.2,19,197,4775,"male" 106 | "Adelie","Biscoe",38.1,16.5,198,3825,"female" 107 | "Adelie","Biscoe",45.6,20.3,191,4600,"male" 108 | "Adelie","Biscoe",39.7,17.7,193,3200,"female" 109 | "Adelie","Biscoe",42.2,19.5,197,4275,"male" 110 | "Adelie","Biscoe",39.6,20.7,191,3900,"female" 111 | "Adelie","Biscoe",42.7,18.3,196,4075,"male" 112 | "Adelie","Torgersen",38.6,17,188,2900,"female" 113 | "Adelie","Torgersen",37.3,20.5,199,3775,"male" 114 | "Adelie","Torgersen",35.7,17,189,3350,"female" 115 | "Adelie","Torgersen",41.1,18.6,189,3325,"male" 116 | "Adelie","Torgersen",36.2,17.2,187,3150,"female" 117 | "Adelie","Torgersen",37.7,19.8,198,3500,"male" 118 | "Adelie","Torgersen",40.2,17,176,3450,"female" 119 | "Adelie","Torgersen",41.4,18.5,202,3875,"male" 120 | "Adelie","Torgersen",35.2,15.9,186,3050,"female" 121 | "Adelie","Torgersen",40.6,19,199,4000,"male" 122 | "Adelie","Torgersen",38.8,17.6,191,3275,"female" 123 | "Adelie","Torgersen",41.5,18.3,195,4300,"male" 124 | "Adelie","Torgersen",39,17.1,191,3050,"female" 125 | "Adelie","Torgersen",44.1,18,210,4000,"male" 126 | "Adelie","Torgersen",38.5,17.9,190,3325,"female" 127 | "Adelie","Torgersen",43.1,19.2,197,3500,"male" 128 | "Adelie","Dream",36.8,18.5,193,3500,"female" 129 | "Adelie","Dream",37.5,18.5,199,4475,"male" 130 | "Adelie","Dream",38.1,17.6,187,3425,"female" 131 | "Adelie","Dream",41.1,17.5,190,3900,"male" 132 | "Adelie","Dream",35.6,17.5,191,3175,"female" 133 | "Adelie","Dream",40.2,20.1,200,3975,"male" 134 | "Adelie","Dream",37,16.5,185,3400,"female" 135 | "Adelie","Dream",39.7,17.9,193,4250,"male" 136 | "Adelie","Dream",40.2,17.1,193,3400,"female" 137 | "Adelie","Dream",40.6,17.2,187,3475,"male" 138 | "Adelie","Dream",32.1,15.5,188,3050,"female" 139 | "Adelie","Dream",40.7,17,190,3725,"male" 140 | "Adelie","Dream",37.3,16.8,192,3000,"female" 141 | "Adelie","Dream",39,18.7,185,3650,"male" 142 | "Adelie","Dream",39.2,18.6,190,4250,"male" 143 | "Adelie","Dream",36.6,18.4,184,3475,"female" 144 | "Adelie","Dream",36,17.8,195,3450,"female" 145 | "Adelie","Dream",37.8,18.1,193,3750,"male" 146 | "Adelie","Dream",36,17.1,187,3700,"female" 147 | "Adelie","Dream",41.5,18.5,201,4000,"male" 148 | "Gentoo","Biscoe",46.1,13.2,211,4500,"female" 149 | "Gentoo","Biscoe",50,16.3,230,5700,"male" 150 | "Gentoo","Biscoe",48.7,14.1,210,4450,"female" 151 | "Gentoo","Biscoe",50,15.2,218,5700,"male" 152 | "Gentoo","Biscoe",47.6,14.5,215,5400,"male" 153 | "Gentoo","Biscoe",46.5,13.5,210,4550,"female" 154 | "Gentoo","Biscoe",45.4,14.6,211,4800,"female" 155 | "Gentoo","Biscoe",46.7,15.3,219,5200,"male" 156 | "Gentoo","Biscoe",43.3,13.4,209,4400,"female" 157 | "Gentoo","Biscoe",46.8,15.4,215,5150,"male" 158 | "Gentoo","Biscoe",40.9,13.7,214,4650,"female" 159 | "Gentoo","Biscoe",49,16.1,216,5550,"male" 160 | "Gentoo","Biscoe",45.5,13.7,214,4650,"female" 161 | "Gentoo","Biscoe",48.4,14.6,213,5850,"male" 162 | "Gentoo","Biscoe",45.8,14.6,210,4200,"female" 163 | "Gentoo","Biscoe",49.3,15.7,217,5850,"male" 164 | "Gentoo","Biscoe",42,13.5,210,4150,"female" 165 | "Gentoo","Biscoe",49.2,15.2,221,6300,"male" 166 | "Gentoo","Biscoe",46.2,14.5,209,4800,"female" 167 | "Gentoo","Biscoe",48.7,15.1,222,5350,"male" 168 | "Gentoo","Biscoe",50.2,14.3,218,5700,"male" 169 | "Gentoo","Biscoe",45.1,14.5,215,5000,"female" 170 | "Gentoo","Biscoe",46.5,14.5,213,4400,"female" 171 | "Gentoo","Biscoe",46.3,15.8,215,5050,"male" 172 | "Gentoo","Biscoe",42.9,13.1,215,5000,"female" 173 | "Gentoo","Biscoe",46.1,15.1,215,5100,"male" 174 | "Gentoo","Biscoe",47.8,15,215,5650,"male" 175 | "Gentoo","Biscoe",48.2,14.3,210,4600,"female" 176 | "Gentoo","Biscoe",50,15.3,220,5550,"male" 177 | "Gentoo","Biscoe",47.3,15.3,222,5250,"male" 178 | "Gentoo","Biscoe",42.8,14.2,209,4700,"female" 179 | "Gentoo","Biscoe",45.1,14.5,207,5050,"female" 180 | "Gentoo","Biscoe",59.6,17,230,6050,"male" 181 | "Gentoo","Biscoe",49.1,14.8,220,5150,"female" 182 | "Gentoo","Biscoe",48.4,16.3,220,5400,"male" 183 | "Gentoo","Biscoe",42.6,13.7,213,4950,"female" 184 | "Gentoo","Biscoe",44.4,17.3,219,5250,"male" 185 | "Gentoo","Biscoe",44,13.6,208,4350,"female" 186 | "Gentoo","Biscoe",48.7,15.7,208,5350,"male" 187 | "Gentoo","Biscoe",42.7,13.7,208,3950,"female" 188 | "Gentoo","Biscoe",49.6,16,225,5700,"male" 189 | "Gentoo","Biscoe",45.3,13.7,210,4300,"female" 190 | "Gentoo","Biscoe",49.6,15,216,4750,"male" 191 | "Gentoo","Biscoe",50.5,15.9,222,5550,"male" 192 | "Gentoo","Biscoe",43.6,13.9,217,4900,"female" 193 | "Gentoo","Biscoe",45.5,13.9,210,4200,"female" 194 | "Gentoo","Biscoe",50.5,15.9,225,5400,"male" 195 | "Gentoo","Biscoe",44.9,13.3,213,5100,"female" 196 | "Gentoo","Biscoe",45.2,15.8,215,5300,"male" 197 | "Gentoo","Biscoe",46.6,14.2,210,4850,"female" 198 | "Gentoo","Biscoe",48.5,14.1,220,5300,"male" 199 | "Gentoo","Biscoe",45.1,14.4,210,4400,"female" 200 | "Gentoo","Biscoe",50.1,15,225,5000,"male" 201 | "Gentoo","Biscoe",46.5,14.4,217,4900,"female" 202 | "Gentoo","Biscoe",45,15.4,220,5050,"male" 203 | "Gentoo","Biscoe",43.8,13.9,208,4300,"female" 204 | "Gentoo","Biscoe",45.5,15,220,5000,"male" 205 | "Gentoo","Biscoe",43.2,14.5,208,4450,"female" 206 | "Gentoo","Biscoe",50.4,15.3,224,5550,"male" 207 | "Gentoo","Biscoe",45.3,13.8,208,4200,"female" 208 | "Gentoo","Biscoe",46.2,14.9,221,5300,"male" 209 | "Gentoo","Biscoe",45.7,13.9,214,4400,"female" 210 | "Gentoo","Biscoe",54.3,15.7,231,5650,"male" 211 | "Gentoo","Biscoe",45.8,14.2,219,4700,"female" 212 | "Gentoo","Biscoe",49.8,16.8,230,5700,"male" 213 | "Gentoo","Biscoe",49.5,16.2,229,5800,"male" 214 | "Gentoo","Biscoe",43.5,14.2,220,4700,"female" 215 | "Gentoo","Biscoe",50.7,15,223,5550,"male" 216 | "Gentoo","Biscoe",47.7,15,216,4750,"female" 217 | "Gentoo","Biscoe",46.4,15.6,221,5000,"male" 218 | "Gentoo","Biscoe",48.2,15.6,221,5100,"male" 219 | "Gentoo","Biscoe",46.5,14.8,217,5200,"female" 220 | "Gentoo","Biscoe",46.4,15,216,4700,"female" 221 | "Gentoo","Biscoe",48.6,16,230,5800,"male" 222 | "Gentoo","Biscoe",47.5,14.2,209,4600,"female" 223 | "Gentoo","Biscoe",51.1,16.3,220,6000,"male" 224 | "Gentoo","Biscoe",45.2,13.8,215,4750,"female" 225 | "Gentoo","Biscoe",45.2,16.4,223,5950,"male" 226 | "Gentoo","Biscoe",49.1,14.5,212,4625,"female" 227 | "Gentoo","Biscoe",52.5,15.6,221,5450,"male" 228 | "Gentoo","Biscoe",47.4,14.6,212,4725,"female" 229 | "Gentoo","Biscoe",50,15.9,224,5350,"male" 230 | "Gentoo","Biscoe",44.9,13.8,212,4750,"female" 231 | "Gentoo","Biscoe",50.8,17.3,228,5600,"male" 232 | "Gentoo","Biscoe",43.4,14.4,218,4600,"female" 233 | "Gentoo","Biscoe",51.3,14.2,218,5300,"male" 234 | "Gentoo","Biscoe",47.5,14,212,4875,"female" 235 | "Gentoo","Biscoe",52.1,17,230,5550,"male" 236 | "Gentoo","Biscoe",47.5,15,218,4950,"female" 237 | "Gentoo","Biscoe",52.2,17.1,228,5400,"male" 238 | "Gentoo","Biscoe",45.5,14.5,212,4750,"female" 239 | "Gentoo","Biscoe",49.5,16.1,224,5650,"male" 240 | "Gentoo","Biscoe",44.5,14.7,214,4850,"female" 241 | "Gentoo","Biscoe",50.8,15.7,226,5200,"male" 242 | "Gentoo","Biscoe",49.4,15.8,216,4925,"male" 243 | "Gentoo","Biscoe",46.9,14.6,222,4875,"female" 244 | "Gentoo","Biscoe",48.4,14.4,203,4625,"female" 245 | "Gentoo","Biscoe",51.1,16.5,225,5250,"male" 246 | "Gentoo","Biscoe",48.5,15,219,4850,"female" 247 | "Gentoo","Biscoe",55.9,17,228,5600,"male" 248 | "Gentoo","Biscoe",47.2,15.5,215,4975,"female" 249 | "Gentoo","Biscoe",49.1,15,228,5500,"male" 250 | "Gentoo","Biscoe",46.8,16.1,215,5500,"male" 251 | "Gentoo","Biscoe",41.7,14.7,210,4700,"female" 252 | "Gentoo","Biscoe",53.4,15.8,219,5500,"male" 253 | "Gentoo","Biscoe",43.3,14,208,4575,"female" 254 | "Gentoo","Biscoe",48.1,15.1,209,5500,"male" 255 | "Gentoo","Biscoe",50.5,15.2,216,5000,"female" 256 | "Gentoo","Biscoe",49.8,15.9,229,5950,"male" 257 | "Gentoo","Biscoe",43.5,15.2,213,4650,"female" 258 | "Gentoo","Biscoe",51.5,16.3,230,5500,"male" 259 | "Gentoo","Biscoe",46.2,14.1,217,4375,"female" 260 | "Gentoo","Biscoe",55.1,16,230,5850,"male" 261 | "Gentoo","Biscoe",48.8,16.2,222,6000,"male" 262 | "Gentoo","Biscoe",47.2,13.7,214,4925,"female" 263 | "Gentoo","Biscoe",46.8,14.3,215,4850,"female" 264 | "Gentoo","Biscoe",50.4,15.7,222,5750,"male" 265 | "Gentoo","Biscoe",45.2,14.8,212,5200,"female" 266 | "Gentoo","Biscoe",49.9,16.1,213,5400,"male" 267 | "Chinstrap","Dream",46.5,17.9,192,3500,"female" 268 | "Chinstrap","Dream",50,19.5,196,3900,"male" 269 | "Chinstrap","Dream",51.3,19.2,193,3650,"male" 270 | "Chinstrap","Dream",45.4,18.7,188,3525,"female" 271 | "Chinstrap","Dream",52.7,19.8,197,3725,"male" 272 | "Chinstrap","Dream",45.2,17.8,198,3950,"female" 273 | "Chinstrap","Dream",46.1,18.2,178,3250,"female" 274 | "Chinstrap","Dream",51.3,18.2,197,3750,"male" 275 | "Chinstrap","Dream",46,18.9,195,4150,"female" 276 | "Chinstrap","Dream",51.3,19.9,198,3700,"male" 277 | "Chinstrap","Dream",46.6,17.8,193,3800,"female" 278 | "Chinstrap","Dream",51.7,20.3,194,3775,"male" 279 | "Chinstrap","Dream",47,17.3,185,3700,"female" 280 | "Chinstrap","Dream",52,18.1,201,4050,"male" 281 | "Chinstrap","Dream",45.9,17.1,190,3575,"female" 282 | "Chinstrap","Dream",50.5,19.6,201,4050,"male" 283 | "Chinstrap","Dream",50.3,20,197,3300,"male" 284 | "Chinstrap","Dream",58,17.8,181,3700,"female" 285 | "Chinstrap","Dream",46.4,18.6,190,3450,"female" 286 | "Chinstrap","Dream",49.2,18.2,195,4400,"male" 287 | "Chinstrap","Dream",42.4,17.3,181,3600,"female" 288 | "Chinstrap","Dream",48.5,17.5,191,3400,"male" 289 | "Chinstrap","Dream",43.2,16.6,187,2900,"female" 290 | "Chinstrap","Dream",50.6,19.4,193,3800,"male" 291 | "Chinstrap","Dream",46.7,17.9,195,3300,"female" 292 | "Chinstrap","Dream",52,19,197,4150,"male" 293 | "Chinstrap","Dream",50.5,18.4,200,3400,"female" 294 | "Chinstrap","Dream",49.5,19,200,3800,"male" 295 | "Chinstrap","Dream",46.4,17.8,191,3700,"female" 296 | "Chinstrap","Dream",52.8,20,205,4550,"male" 297 | "Chinstrap","Dream",40.9,16.6,187,3200,"female" 298 | "Chinstrap","Dream",54.2,20.8,201,4300,"male" 299 | "Chinstrap","Dream",42.5,16.7,187,3350,"female" 300 | "Chinstrap","Dream",51,18.8,203,4100,"male" 301 | "Chinstrap","Dream",49.7,18.6,195,3600,"male" 302 | "Chinstrap","Dream",47.5,16.8,199,3900,"female" 303 | "Chinstrap","Dream",47.6,18.3,195,3850,"female" 304 | "Chinstrap","Dream",52,20.7,210,4800,"male" 305 | "Chinstrap","Dream",46.9,16.6,192,2700,"female" 306 | "Chinstrap","Dream",53.5,19.9,205,4500,"male" 307 | "Chinstrap","Dream",49,19.5,210,3950,"male" 308 | "Chinstrap","Dream",46.2,17.5,187,3650,"female" 309 | "Chinstrap","Dream",50.9,19.1,196,3550,"male" 310 | "Chinstrap","Dream",45.5,17,196,3500,"female" 311 | "Chinstrap","Dream",50.9,17.9,196,3675,"female" 312 | "Chinstrap","Dream",50.8,18.5,201,4450,"male" 313 | "Chinstrap","Dream",50.1,17.9,190,3400,"female" 314 | "Chinstrap","Dream",49,19.6,212,4300,"male" 315 | "Chinstrap","Dream",51.5,18.7,187,3250,"male" 316 | "Chinstrap","Dream",49.8,17.3,198,3675,"female" 317 | "Chinstrap","Dream",48.1,16.4,199,3325,"female" 318 | "Chinstrap","Dream",51.4,19,201,3950,"male" 319 | "Chinstrap","Dream",45.7,17.3,193,3600,"female" 320 | "Chinstrap","Dream",50.7,19.7,203,4050,"male" 321 | "Chinstrap","Dream",42.5,17.3,187,3350,"female" 322 | "Chinstrap","Dream",52.2,18.8,197,3450,"male" 323 | "Chinstrap","Dream",45.2,16.6,191,3250,"female" 324 | "Chinstrap","Dream",49.3,19.9,203,4050,"male" 325 | "Chinstrap","Dream",50.2,18.8,202,3800,"male" 326 | "Chinstrap","Dream",45.6,19.4,194,3525,"female" 327 | "Chinstrap","Dream",51.9,19.5,206,3950,"male" 328 | "Chinstrap","Dream",46.8,16.5,189,3650,"female" 329 | "Chinstrap","Dream",45.7,17,195,3650,"female" 330 | "Chinstrap","Dream",55.8,19.8,207,4000,"male" 331 | "Chinstrap","Dream",43.5,18.1,202,3400,"female" 332 | "Chinstrap","Dream",49.6,18.2,193,3775,"male" 333 | "Chinstrap","Dream",50.8,19,210,4100,"male" 334 | "Chinstrap","Dream",50.2,18.7,198,3775,"female" 335 | -------------------------------------------------------------------------------- /app_8_classification_penguins/penguins_clf.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dataprofessor/streamlit_freecodecamp/d44c4c1320f8417b3d5494902e5366b715314528/app_8_classification_penguins/penguins_clf.pkl -------------------------------------------------------------------------------- /app_8_classification_penguins/penguins_example.csv: -------------------------------------------------------------------------------- 1 | island,bill_length_mm,bill_depth_mm,flipper_length_mm,body_mass_g,sex 2 | Biscoe,43.9,17.2,201.0,4207.0,male 3 | -------------------------------------------------------------------------------- /app_9_regression_boston_housing/boston-house-ml-app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import shap 4 | import matplotlib.pyplot as plt 5 | from sklearn import datasets 6 | from sklearn.ensemble import RandomForestRegressor 7 | 8 | st.write(""" 9 | # Boston House Price Prediction App 10 | 11 | This app predicts the **Boston House Price**! 12 | """) 13 | st.write('---') 14 | 15 | # Loads the Boston House Price Dataset 16 | boston = datasets.load_boston() 17 | X = pd.DataFrame(boston.data, columns=boston.feature_names) 18 | Y = pd.DataFrame(boston.target, columns=["MEDV"]) 19 | 20 | # Sidebar 21 | # Header of Specify Input Parameters 22 | st.sidebar.header('Specify Input Parameters') 23 | 24 | def user_input_features(): 25 | CRIM = st.sidebar.slider('CRIM', X.CRIM.min(), X.CRIM.max(), X.CRIM.mean()) 26 | ZN = st.sidebar.slider('ZN', X.ZN.min(), X.ZN.max(), X.ZN.mean()) 27 | INDUS = st.sidebar.slider('INDUS', X.INDUS.min(), X.INDUS.max(), X.INDUS.mean()) 28 | CHAS = st.sidebar.slider('CHAS', X.CHAS.min(), X.CHAS.max(), X.CHAS.mean()) 29 | NOX = st.sidebar.slider('NOX', X.NOX.min(), X.NOX.max(), X.NOX.mean()) 30 | RM = st.sidebar.slider('RM', X.RM.min(), X.RM.max(), X.RM.mean()) 31 | AGE = st.sidebar.slider('AGE', X.AGE.min(), X.AGE.max(), X.AGE.mean()) 32 | DIS = st.sidebar.slider('DIS', X.DIS.min(), X.DIS.max(), X.DIS.mean()) 33 | RAD = st.sidebar.slider('RAD', X.RAD.min(), X.RAD.max(), X.RAD.mean()) 34 | TAX = st.sidebar.slider('TAX', X.TAX.min(), X.TAX.max(), X.TAX.mean()) 35 | PTRATIO = st.sidebar.slider('PTRATIO', X.PTRATIO.min(), X.PTRATIO.max(), X.PTRATIO.mean()) 36 | B = st.sidebar.slider('B', X.B.min(), X.B.max(), X.B.mean()) 37 | LSTAT = st.sidebar.slider('LSTAT', X.LSTAT.min(), X.LSTAT.max(), X.LSTAT.mean()) 38 | data = {'CRIM': CRIM, 39 | 'ZN': ZN, 40 | 'INDUS': INDUS, 41 | 'CHAS': CHAS, 42 | 'NOX': NOX, 43 | 'RM': RM, 44 | 'AGE': AGE, 45 | 'DIS': DIS, 46 | 'RAD': RAD, 47 | 'TAX': TAX, 48 | 'PTRATIO': PTRATIO, 49 | 'B': B, 50 | 'LSTAT': LSTAT} 51 | features = pd.DataFrame(data, index=[0]) 52 | return features 53 | 54 | df = user_input_features() 55 | 56 | # Main Panel 57 | 58 | # Print specified input parameters 59 | st.header('Specified Input parameters') 60 | st.write(df) 61 | st.write('---') 62 | 63 | # Build Regression Model 64 | model = RandomForestRegressor() 65 | model.fit(X, Y) 66 | # Apply Model to Make Prediction 67 | prediction = model.predict(df) 68 | 69 | st.header('Prediction of MEDV') 70 | st.write(prediction) 71 | st.write('---') 72 | 73 | # Explaining the model's predictions using SHAP values 74 | # https://github.com/slundberg/shap 75 | explainer = shap.TreeExplainer(model) 76 | shap_values = explainer.shap_values(X) 77 | 78 | st.header('Feature Importance') 79 | plt.title('Feature importance based on SHAP values') 80 | shap.summary_plot(shap_values, X) 81 | st.pyplot(bbox_inches='tight') 82 | st.write('---') 83 | 84 | plt.title('Feature importance based on SHAP values (Bar)') 85 | shap.summary_plot(shap_values, X, plot_type="bar") 86 | st.pyplot(bbox_inches='tight') 87 | --------------------------------------------------------------------------------