JohnAlexander23's picture
Update app.py
9ff27a2 verified
import streamlit as st
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error, r2_score
import altair as alt
import time
import zipfile
import os
# Page title
st.set_page_config(page_title='ML Model Building', page_icon='πŸ€–', layout='wide') # Set layout to wide for better use of space
st.title('πŸ€– ML Model Building')
with st.expander('About this app'):
st.markdown('**What can this app do?**')
st.info('This app allow users to build a machine learning (ML) model in an end-to-end workflow. Particularly, this encompasses data upload, data pre-processing, ML model building and post-model analysis.')
st.markdown('**How to use the app?**')
st.warning('To engage with the app, go to the sidebar and 1. Select a data set and 2. Adjust the model parameters by adjusting the various slider widgets. As a result, this would initiate the ML model building process, display the model results as well as allowing users to download the generated models and accompanying data.')
st.markdown('**Under the hood**')
st.markdown('Data sets:')
st.code('''- Drug solubility data set
''', language='markdown')
st.markdown('Libraries used:')
st.code('''- Pandas for data wrangling
- Scikit-learn for building a machine learning model
- Altair for chart creation
- Streamlit for user interface
''', language='markdown')
# Sidebar for accepting input parameters
with st.sidebar:
# Load data
st.header('1.1. Input data')
st.markdown('**1. Use custom data**')
uploaded_file = st.file_uploader("Upload a CSV file", type=["csv"])
if uploaded_file is not None:
df = pd.read_csv(uploaded_file, index_col=False)
# Download example data
@st.cache_data
def convert_df(input_df):
return input_df.to_csv(index=False).encode('utf-8')
example_csv = pd.read_csv('https://raw.githubusercontent.com/dataprofessor/data/master/delaney_solubility_with_descriptors.csv')
csv = convert_df(example_csv)
st.download_button(
label="Download example CSV",
data=csv,
file_name='delaney_solubility_with_descriptors.csv',
mime='text/csv',
)
# Select example data
st.markdown('**1.2. Use example data**')
example_data = st.toggle('Load example data')
if example_data:
df = pd.read_csv('https://raw.githubusercontent.com/dataprofessor/data/master/delaney_solubility_with_descriptors.csv')
st.header('2. Set Parameters')
parameter_split_size = st.slider('Data split ratio (% for Training Set)', 10, 90, 80, 5)
st.subheader('2.1. Learning Parameters')
with st.expander('See parameters'):
parameter_n_estimators = st.slider('Number of estimators (n_estimators)', 0, 1000, 100, 100)
parameter_max_features = st.select_slider('Max features (max_features)', options=['all', 'sqrt', 'log2'])
parameter_min_samples_split = st.slider('Minimum number of samples required to split an internal node (min_samples_split)', 2, 10, 2, 1)
parameter_min_samples_leaf = st.slider('Minimum number of samples required to be at a leaf node (min_samples_leaf)', 1, 10, 2, 1)
st.subheader('2.2. General Parameters')
with st.expander('See parameters', expanded=False):
parameter_random_state = st.slider('Seed number (random_state)', 0, 1000, 42, 1)
parameter_criterion = st.select_slider('Performance measure (criterion)', options=['squared_error', 'absolute_error', 'friedman_mse'])
parameter_bootstrap = st.select_slider('Bootstrap samples when building trees (bootstrap)', options=[True, False])
parameter_oob_score = st.select_slider('Whether to use out-of-bag samples to estimate the R^2 on unseen data (oob_score)', options=[False, True])
sleep_time = st.slider('Sleep time', 0, 3, 0)
# Initiate the model building process
if uploaded_file or example_data:
with st.status("Running ...", expanded=True) as status:
st.write("Loading data ...")
time.sleep(sleep_time)
st.write("Preparing data ...")
time.sleep(sleep_time)
X = df.iloc[:,:-1]
y = df.iloc[:,-1]
st.write("Splitting data ...")
time.sleep(sleep_time)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=(100-parameter_split_size)/100, random_state=parameter_random_state)
st.write("Model training ...")
time.sleep(sleep_time)
if parameter_max_features == 'all':
parameter_max_features = None
parameter_max_features_metric = X.shape[1]
rf = RandomForestRegressor(
n_estimators=parameter_n_estimators,
max_features=parameter_max_features,
min_samples_split=parameter_min_samples_split,
min_samples_leaf=parameter_min_samples_leaf,
random_state=parameter_random_state,
criterion=parameter_criterion,
bootstrap=parameter_bootstrap,
oob_score=parameter_oob_score)
rf.fit(X_train, y_train)
st.write("Applying model to make predictions ...")
time.sleep(sleep_time)
y_train_pred = rf.predict(X_train)
y_test_pred = rf.predict(X_test)
st.write("Evaluating performance metrics ...")
time.sleep(sleep_time)
train_mse = mean_squared_error(y_train, y_train_pred)
train_r2 = r2_score(y_train, y_train_pred)
test_mse = mean_squared_error(y_test, y_test_pred)
test_r2 = r2_score(y_test, y_test_pred)
st.write("Displaying performance metrics ...")
time.sleep(sleep_time)
parameter_criterion_string = ' '.join([x.capitalize() for x in parameter_criterion.split('_')])
rf_results = pd.DataFrame(['Random forest', train_mse, train_r2, test_mse, test_r2]).transpose()
rf_results.columns = ['Method', f'Training {parameter_criterion_string}', 'Training R2', f'Test {parameter_criterion_string}', 'Test R2']
rf_results = rf_results.round(3)
status.update(label="Status", state="complete", expanded=False)
# Display data info
st.header('Input data', divider='rainbow')
col = st.columns(4)
col[0].metric(label="No. of samples", value=X.shape[0], delta="")
col[1].metric(label="No. of X variables", value=X.shape[1], delta="")
col[2].metric(label="No. of Training samples", value=X_train.shape[0], delta="")
col[3].metric(label="No. of Test samples", value=X_test.shape[0], delta="")
with st.expander('Initial dataset', expanded=True):
st.dataframe(df, height=210, use_container_width=True)
with st.expander('Train split', expanded=False):
train_col = st.columns((3,1))
with train_col[0]:
st.markdown('**X**')
st.dataframe(X_train, height=210, hide_index=True, use_container_width=True)
with train_col[1]:
st.markdown('**y**')
st.dataframe(y_train, height=210, hide_index=True, use_container_width=True)
with st.expander('Test split', expanded=False):
test_col = st.columns((3,1))
with test_col[0]:
st.markdown('**X**')
st.dataframe(X_test, height=210, hide_index=True, use_container_width=True)
with test_col[1]:
st.markdown('**y**')
st.dataframe(y_test, height=210, hide_index=True, use_container_width=True)
# Zip dataset files
df.to_csv('dataset.csv', index=False)
X_train.to_csv('X_train.csv', index=False)
y_train.to_csv('y_train.csv', index=False)
X_test.to_csv('X_test.csv', index=False)
y_test.to_csv('y_test.csv', index=False)
list_files = ['dataset.csv', 'X_train.csv', 'y_train.csv', 'X_test.csv', 'y_test.csv']
with zipfile.ZipFile('dataset.zip', 'w') as zipF:
for file in list_files:
zipF.write(file, compress_type=zipfile.ZIP_DEFLATED)
with open('dataset.zip', 'rb') as datazip:
btn = st.download_button(
label='Download ZIP',
data=datazip,
file_name="dataset.zip",
mime="application/octet-stream"
)
# Display model parameters
st.header('Model parameters', divider='rainbow')
parameters_col = st.columns(3)
parameters_col[0].metric(label="Data split ratio (% for Training Set)", value=parameter_split_size, delta="")
parameters_col[1].metric(label="Number of estimators (n_estimators)", value=parameter_n_estimators, delta="")
parameters_col[2].metric(label="Max features (max_features)", value=parameter_max_features_metric, delta="")
# Display feature importance plot
importances = rf.feature_importances_
feature_names = list(X.columns)
forest_importances = pd.Series(importances, index=feature_names)
df_importance = forest_importances.reset_index().rename(columns={'index': 'feature', 0: 'value'})
bars = alt.Chart(df_importance).mark_bar(size=40).encode(
x='value:Q',
y=alt.Y('feature:N', sort='-x')
).properties(height=250)
performance_col = st.columns((2, 0.2, 3))
with performance_col[0]:
st.header('Model performance', divider='rainbow')
st.dataframe(rf_results.T.reset_index().rename(columns={'index': 'Parameter', 0: 'Value'}))
with performance_col[2]:
st.header('Feature importance', divider='rainbow')
st.altair_chart(bars, theme='streamlit', use_container_width=True)
# Prediction results
st.header('Prediction results', divider='rainbow')
s_y_train = pd.Series(y_train, name='actual').reset_index(drop=True)
s_y_train_pred = pd.Series(y_train_pred, name='predicted').reset_index(drop=True)
df_train = pd.DataFrame(data=[s_y_train, s_y_train_pred], index=None).T
df_train['class'] = 'train'
s_y_test = pd.Series(y_test, name='actual').reset_index(drop=True)
s_y_test_pred = pd.Series(y_test_pred, name='predicted').reset_index(drop=True)
df_test = pd.DataFrame(data=[s_y_test, s_y_test_pred], index=None).T
df_test['class'] = 'test'
df_prediction = pd.concat([df_train, df_test], axis=0)
prediction_col = st.columns((2, 0.2, 3))
# Display dataframe
with prediction_col[0]:
st.dataframe(df_prediction, height=320, use_container_width=True)
# Display scatter plot of actual vs predicted values
with prediction_col[2]:
scatter = alt.Chart(df_prediction).mark_circle(size=60).encode(
x='actual',
y='predicted',
color='class'
)
st.altair_chart(scatter, theme='streamlit', use_container_width=True)
else:
st.warning('πŸ‘ˆ Upload a CSV file or click *"Load example data"* to get started!')