├── 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](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 | " MolLogP | \n",
94 | " MolWt | \n",
95 | " NumRotatableBonds | \n",
96 | " AromaticProportion | \n",
97 | " logS | \n",
98 | "
\n",
99 | " \n",
100 | " \n",
101 | " \n",
102 | " 0 | \n",
103 | " 2.59540 | \n",
104 | " 167.850 | \n",
105 | " 0.0 | \n",
106 | " 0.000000 | \n",
107 | " -2.180 | \n",
108 | "
\n",
109 | " \n",
110 | " 1 | \n",
111 | " 2.37650 | \n",
112 | " 133.405 | \n",
113 | " 0.0 | \n",
114 | " 0.000000 | \n",
115 | " -2.000 | \n",
116 | "
\n",
117 | " \n",
118 | " 2 | \n",
119 | " 2.59380 | \n",
120 | " 167.850 | \n",
121 | " 1.0 | \n",
122 | " 0.000000 | \n",
123 | " -1.740 | \n",
124 | "
\n",
125 | " \n",
126 | " 3 | \n",
127 | " 2.02890 | \n",
128 | " 133.405 | \n",
129 | " 1.0 | \n",
130 | " 0.000000 | \n",
131 | " -1.480 | \n",
132 | "
\n",
133 | " \n",
134 | " 4 | \n",
135 | " 2.91890 | \n",
136 | " 187.375 | \n",
137 | " 1.0 | \n",
138 | " 0.000000 | \n",
139 | " -3.040 | \n",
140 | "
\n",
141 | " \n",
142 | " ... | \n",
143 | " ... | \n",
144 | " ... | \n",
145 | " ... | \n",
146 | " ... | \n",
147 | " ... | \n",
148 | "
\n",
149 | " \n",
150 | " 1139 | \n",
151 | " 1.98820 | \n",
152 | " 287.343 | \n",
153 | " 8.0 | \n",
154 | " 0.000000 | \n",
155 | " 1.144 | \n",
156 | "
\n",
157 | " \n",
158 | " 1140 | \n",
159 | " 3.42130 | \n",
160 | " 286.114 | \n",
161 | " 2.0 | \n",
162 | " 0.333333 | \n",
163 | " -4.925 | \n",
164 | "
\n",
165 | " \n",
166 | " 1141 | \n",
167 | " 3.60960 | \n",
168 | " 308.333 | \n",
169 | " 4.0 | \n",
170 | " 0.695652 | \n",
171 | " -3.893 | \n",
172 | "
\n",
173 | " \n",
174 | " 1142 | \n",
175 | " 2.56214 | \n",
176 | " 354.815 | \n",
177 | " 3.0 | \n",
178 | " 0.521739 | \n",
179 | " -3.790 | \n",
180 | "
\n",
181 | " \n",
182 | " 1143 | \n",
183 | " 2.02164 | \n",
184 | " 179.219 | \n",
185 | " 1.0 | \n",
186 | " 0.461538 | \n",
187 | " -2.581 | \n",
188 | "
\n",
189 | " \n",
190 | "
\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 | " MolLogP | \n",
258 | " MolWt | \n",
259 | " NumRotatableBonds | \n",
260 | " AromaticProportion | \n",
261 | "
\n",
262 | " \n",
263 | " \n",
264 | " \n",
265 | " 0 | \n",
266 | " 2.59540 | \n",
267 | " 167.850 | \n",
268 | " 0.0 | \n",
269 | " 0.000000 | \n",
270 | "
\n",
271 | " \n",
272 | " 1 | \n",
273 | " 2.37650 | \n",
274 | " 133.405 | \n",
275 | " 0.0 | \n",
276 | " 0.000000 | \n",
277 | "
\n",
278 | " \n",
279 | " 2 | \n",
280 | " 2.59380 | \n",
281 | " 167.850 | \n",
282 | " 1.0 | \n",
283 | " 0.000000 | \n",
284 | "
\n",
285 | " \n",
286 | " 3 | \n",
287 | " 2.02890 | \n",
288 | " 133.405 | \n",
289 | " 1.0 | \n",
290 | " 0.000000 | \n",
291 | "
\n",
292 | " \n",
293 | " 4 | \n",
294 | " 2.91890 | \n",
295 | " 187.375 | \n",
296 | " 1.0 | \n",
297 | " 0.000000 | \n",
298 | "
\n",
299 | " \n",
300 | " ... | \n",
301 | " ... | \n",
302 | " ... | \n",
303 | " ... | \n",
304 | " ... | \n",
305 | "
\n",
306 | " \n",
307 | " 1139 | \n",
308 | " 1.98820 | \n",
309 | " 287.343 | \n",
310 | " 8.0 | \n",
311 | " 0.000000 | \n",
312 | "
\n",
313 | " \n",
314 | " 1140 | \n",
315 | " 3.42130 | \n",
316 | " 286.114 | \n",
317 | " 2.0 | \n",
318 | " 0.333333 | \n",
319 | "
\n",
320 | " \n",
321 | " 1141 | \n",
322 | " 3.60960 | \n",
323 | " 308.333 | \n",
324 | " 4.0 | \n",
325 | " 0.695652 | \n",
326 | "
\n",
327 | " \n",
328 | " 1142 | \n",
329 | " 2.56214 | \n",
330 | " 354.815 | \n",
331 | " 3.0 | \n",
332 | " 0.521739 | \n",
333 | "
\n",
334 | " \n",
335 | " 1143 | \n",
336 | " 2.02164 | \n",
337 | " 179.219 | \n",
338 | " 1.0 | \n",
339 | " 0.461538 | \n",
340 | "
\n",
341 | " \n",
342 | "
\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 |
--------------------------------------------------------------------------------