Skip to main content

Dashboard Creation with Streamlit

Build Data Apps in Pure Python! 🚀

Streamlit turns data scripts into shareable web apps in minutes. All in pure Python. No front-end experience required. It's the fastest way to build and share data applications - perfect for machine learning demos, data exploration tools, and interactive reports!

Why Streamlit?

Streamlit revolutionizes how data scientists create web applications:

Installation and Setup

# Install Streamlit
# pip install streamlit

# Optional: Install additional components
# pip install streamlit-aggrid  # For advanced data tables
# pip install streamlit-folium  # For maps
# pip install streamlit-plotly-events  # For Plotly interactions

# Create your first app (app.py)
import streamlit as st

st.title("My First Streamlit App")
st.write("Hello, World! 👋")

# Run the app
# streamlit run app.py

# Check Streamlit version
import streamlit as st
print(f"Streamlit version: {st.__version__}")

Basic Components

Text and Markdown

import streamlit as st

# Title and headers
st.title("Dashboard Title")
st.header("This is a header")
st.subheader("This is a subheader")

# Text
st.text("Fixed width text")
st.write("This supports _markdown_ **formatting**")

# Markdown
st.markdown("""
### Features
- Bullet points
- **Bold text**
- *Italic text*
- [Links](https://streamlit.io)
- `code snippets`
""")

# LaTeX
st.latex(r'''
     e^{i\pi} + 1 = 0
     ''')

# Code
code = '''def hello():
    print("Hello, Streamlit!")'''
st.code(code, language='python')

# Success, info, warning, error messages
st.success("Success message")
st.info("Information message")
st.warning("Warning message")
st.error("Error message")
st.exception(Exception("This is an exception"))

Data Display

import streamlit as st
import pandas as pd
import numpy as np

# Create sample data
df = pd.DataFrame(
    np.random.randn(10, 5),
    columns=['A', 'B', 'C', 'D', 'E']
)

# Display DataFrame
st.dataframe(df)  # Interactive table
st.table(df)  # Static table

# Styled DataFrame
st.dataframe(df.style.highlight_max(axis=0))

# Metrics
col1, col2, col3 = st.columns(3)
col1.metric("Temperature", "70 °F", "1.2 °F")
col2.metric("Wind", "9 mph", "-8%")
col3.metric("Humidity", "86%", "4%")

# JSON
json_data = {"name": "John", "age": 30, "city": "New York"}
st.json(json_data)

Interactive Widgets

Input Widgets

import streamlit as st
import datetime

# Button
if st.button('Click me'):
    st.write('Button clicked!')

# Checkbox
agree = st.checkbox('I agree to the terms')
if agree:
    st.write('Thank you for agreeing!')

# Radio buttons
genre = st.radio(
    "What's your favorite movie genre?",
    ('Comedy', 'Drama', 'Documentary')
)
st.write(f'You selected: {genre}')

# Selectbox
option = st.selectbox(
    'How would you like to be contacted?',
    ('Email', 'Home phone', 'Mobile phone')
)

# Multiselect
options = st.multiselect(
    'What are your favorite colors?',
    ['Green', 'Yellow', 'Red', 'Blue'],
    ['Yellow', 'Red']
)

# Slider
age = st.slider('How old are you?', 0, 130, 25)
st.write(f"I'm {age} years old")

# Range slider
values = st.slider(
    'Select a range of values',
    0.0, 100.0, (25.0, 75.0)
)

# Text input
title = st.text_input('Movie title', 'Life of Brian')
st.write('The current movie title is', title)

# Text area
txt = st.text_area('Text to analyze', '''
    It was the best of times, it was the worst of times...
    ''')

# Number input
number = st.number_input('Insert a number', value=0, step=1)

# Date input
d = st.date_input("When's your birthday", datetime.date(2000, 1, 1))

# Time input
t = st.time_input('Set an alarm for', datetime.time(8, 45))

# File uploader
uploaded_file = st.file_uploader("Choose a CSV file", type="csv")
if uploaded_file is not None:
    data = pd.read_csv(uploaded_file)
    st.write(data)

# Color picker
color = st.color_picker('Pick A Color', '#00f900')
st.write('The current color is', color)

Layouts and Containers

Organizing Your App

import streamlit as st

# Sidebar
with st.sidebar:
    st.title("Sidebar Title")
    option = st.selectbox("Choose option", ["A", "B", "C"])
    
# Alternative sidebar syntax
st.sidebar.title("Navigation")
page = st.sidebar.radio("Go to", ["Home", "Data", "Model"])

# Columns
col1, col2, col3 = st.columns(3)

with col1:
    st.header("Column 1")
    st.write("Content for column 1")

with col2:
    st.header("Column 2")
    st.write("Content for column 2")

with col3:
    st.header("Column 3")
    st.write("Content for column 3")

# Columns with different widths
col1, col2 = st.columns([2, 1])  # 2:1 ratio

# Tabs
tab1, tab2, tab3 = st.tabs(["Chart", "Data", "Settings"])

with tab1:
    st.header("Chart")
    st.line_chart(data)

with tab2:
    st.header("Data")
    st.dataframe(data)

with tab3:
    st.header("Settings")
    st.write("Configuration options here")

# Expander
with st.expander("See explanation"):
    st.write("""
        The chart above shows some numbers I picked for you.
        I rolled actual dice for these, so they're *guaranteed* to be random.
    """)
    st.image("https://static.streamlit.io/examples/dice.jpg")

# Container
with st.container():
    st.write("This is inside the container")
    # You can add any elements here

# Empty placeholder
placeholder = st.empty()
# Later update it
placeholder.text("Hello")
# Replace with something else
placeholder.line_chart(data)

Data Visualization

Built-in Charts

import streamlit as st
import pandas as pd
import numpy as np

# Generate sample data
chart_data = pd.DataFrame(
    np.random.randn(20, 3),
    columns=['a', 'b', 'c']
)

# Line chart
st.line_chart(chart_data)

# Area chart
st.area_chart(chart_data)

# Bar chart
st.bar_chart(chart_data)

# Scatter plot (using Altair behind the scenes)
scatter_data = pd.DataFrame({
    'x': np.random.randn(100),
    'y': np.random.randn(100),
    'c': np.random.choice(['A', 'B', 'C'], 100)
})

st.scatter_chart(
    scatter_data,
    x='x',
    y='y',
    color='c'
)

# Map
map_data = pd.DataFrame(
    np.random.randn(100, 2) / [50, 50] + [37.76, -122.4],
    columns=['lat', 'lon']
)
st.map(map_data)

Integration with Plotting Libraries

import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.express as px
import altair as alt

# Matplotlib
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [1, 4, 2, 3])
ax.set_title("Matplotlib Plot")
st.pyplot(fig)

# Plotly
df = px.data.iris()
fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species")
st.plotly_chart(fig, use_container_width=True)

# Altair
chart = alt.Chart(df).mark_circle().encode(
    x='sepal_width',
    y='sepal_length',
    color='species',
    tooltip=['sepal_width', 'sepal_length', 'species']
).interactive()
st.altair_chart(chart, use_container_width=True)

# Bokeh
from bokeh.plotting import figure

p = figure(
    title='Simple line example',
    x_axis_label='x',
    y_axis_label='y'
)
p.line([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], line_width=2)
st.bokeh_chart(p, use_container_width=True)

# Seaborn (via matplotlib)
import seaborn as sns
fig, ax = plt.subplots()
sns.heatmap(df.select_dtypes(include=np.number).corr(), ax=ax)
st.pyplot(fig)

Session State and Caching

Managing State

import streamlit as st

# Initialize session state
if 'counter' not in st.session_state:
    st.session_state.counter = 0

# Increment counter button
if st.button('Increment'):
    st.session_state.counter += 1

st.write(f'Counter: {st.session_state.counter}')

# Callback functions with session state
def increment_counter():
    st.session_state.counter += 1

st.button('Increment with callback', on_click=increment_counter)

# Form with session state
with st.form("my_form"):
    st.write("Inside the form")
    slider_val = st.slider("Form slider", 0, 10, 5)
    checkbox_val = st.checkbox("Form checkbox")
    
    # Every form must have a submit button
    submitted = st.form_submit_button("Submit")
    if submitted:
        st.session_state.form_slider = slider_val
        st.session_state.form_checkbox = checkbox_val
        st.write("Form submitted!")

# Display form values from session state
if 'form_slider' in st.session_state:
    st.write(f"Slider value: {st.session_state.form_slider}")
    st.write(f"Checkbox value: {st.session_state.form_checkbox}")

Performance Optimization with Caching

import streamlit as st
import pandas as pd
import time

# Cache data loading
@st.cache_data  # New caching decorator (replaces st.cache)
def load_data():
    # Simulate expensive data loading
    time.sleep(2)
    return pd.read_csv("large_dataset.csv")

# Cache resource initialization (models, database connections)
@st.cache_resource
def init_model():
    # Load ML model (this runs only once)
    from sklearn.ensemble import RandomForestClassifier
    model = RandomForestClassifier()
    # Load pretrained weights...
    return model

# Cache with parameters
@st.cache_data
def expensive_computation(param1, param2):
    # This will cache results for each unique combination of parameters
    result = param1 ** 2 + param2 ** 2
    time.sleep(2)  # Simulate expensive computation
    return result

# Clear cache
if st.button("Clear all cache"):
    st.cache_data.clear()
    st.success("Cache cleared!")

# Use cached functions
data = load_data()
st.write(f"Loaded {len(data)} rows")

result = expensive_computation(5, 10)
st.write(f"Result: {result}")

# TTL (Time To Live) for cache
@st.cache_data(ttl=3600)  # Cache for 1 hour
def get_live_data():
    # Fetch data from API
    return fetch_from_api()

# Show spinner while computing
with st.spinner('Loading data...'):
    data = load_data()
st.success('Data loaded!')

Building a Complete Dashboard

# app.py - Complete Sales Dashboard
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta

# Page configuration
st.set_page_config(
    page_title="Sales Dashboard",
    page_icon="📊",
    layout="wide",
    initial_sidebar_state="expanded"
)

# Custom CSS
st.markdown("""

""", unsafe_allow_html=True)

# Title
st.markdown('

📊 Sales Analytics Dashboard

', unsafe_allow_html=True) # Generate sample data @st.cache_data def generate_data(): np.random.seed(42) dates = pd.date_range(start='2023-01-01', end='2024-12-31', freq='D') n = len(dates) data = pd.DataFrame({ 'date': dates, 'sales': np.random.normal(10000, 2000, n) + np.sin(np.arange(n) * 2 * np.pi / 365) * 3000, 'customers': np.random.poisson(100, n), 'region': np.random.choice(['North', 'South', 'East', 'West'], n), 'product': np.random.choice(['Product A', 'Product B', 'Product C'], n), 'satisfaction': np.random.uniform(3, 5, n) }) data['profit'] = data['sales'] * np.random.uniform(0.1, 0.3, n) return data # Load data df = generate_data() # Sidebar filters st.sidebar.header("Filters") # Date range filter date_range = st.sidebar.date_input( "Select Date Range", value=(df['date'].min(), df['date'].max()), min_value=df['date'].min(), max_value=df['date'].max() ) # Region filter regions = st.sidebar.multiselect( "Select Regions", options=df['region'].unique(), default=df['region'].unique() ) # Product filter products = st.sidebar.multiselect( "Select Products", options=df['product'].unique(), default=df['product'].unique() ) # Filter data filtered_df = df[ (df['date'] >= pd.Timestamp(date_range[0])) & (df['date'] <= pd.Timestamp(date_range[1])) & (df['region'].isin(regions)) & (df['product'].isin(products)) ] # Key metrics col1, col2, col3, col4 = st.columns(4) with col1: total_sales = filtered_df['sales'].sum() st.metric( label="Total Sales", value=f"${total_sales:,.0f}", delta=f"{np.random.uniform(-10, 10):.1f}%" ) with col2: total_profit = filtered_df['profit'].sum() st.metric( label="Total Profit", value=f"${total_profit:,.0f}", delta=f"{np.random.uniform(-10, 10):.1f}%" ) with col3: avg_satisfaction = filtered_df['satisfaction'].mean() st.metric( label="Avg Satisfaction", value=f"{avg_satisfaction:.2f} ⭐", delta=f"{np.random.uniform(-0.5, 0.5):.2f}" ) with col4: total_customers = filtered_df['customers'].sum() st.metric( label="Total Customers", value=f"{total_customers:,}", delta=f"{np.random.uniform(-5, 5):.1f}%" ) # Create two columns for charts col1, col2 = st.columns(2) with col1: # Sales trend st.subheader("Sales Trend Over Time") daily_sales = filtered_df.groupby('date')['sales'].sum().reset_index() fig_trend = px.line( daily_sales, x='date', y='sales', title="Daily Sales" ) fig_trend.update_layout(showlegend=False) st.plotly_chart(fig_trend, use_container_width=True) # Sales by region st.subheader("Sales by Region") region_sales = filtered_df.groupby('region')['sales'].sum().reset_index() fig_region = px.pie( region_sales, values='sales', names='region', hole=0.4 ) st.plotly_chart(fig_region, use_container_width=True) with col2: # Product performance st.subheader("Product Performance") product_metrics = filtered_df.groupby('product').agg({ 'sales': 'sum', 'profit': 'sum', 'customers': 'sum' }).reset_index() fig_product = px.bar( product_metrics, x='product', y=['sales', 'profit'], title="Sales and Profit by Product", barmode='group' ) st.plotly_chart(fig_product, use_container_width=True) # Customer satisfaction distribution st.subheader("Customer Satisfaction Distribution") fig_satisfaction = px.histogram( filtered_df, x='satisfaction', nbins=20, title="Satisfaction Score Distribution" ) st.plotly_chart(fig_satisfaction, use_container_width=True) # Detailed data table with st.expander("View Detailed Data"): st.subheader("Raw Data") st.dataframe( filtered_df.style.format({ 'sales': '${:,.0f}', 'profit': '${:,.0f}', 'satisfaction': '{:.2f}' }), use_container_width=True ) # Download button csv = filtered_df.to_csv(index=False) st.download_button( label="Download data as CSV", data=csv, file_name='sales_data.csv', mime='text/csv' ) # Insights section st.header("📈 Key Insights") col1, col2, col3 = st.columns(3) with col1: best_region = filtered_df.groupby('region')['sales'].sum().idxmax() st.info(f"**Best Performing Region:** {best_region}") with col2: best_product = filtered_df.groupby('product')['profit'].sum().idxmax() st.info(f"**Most Profitable Product:** {best_product}") with col3: peak_day = filtered_df.groupby('date')['sales'].sum().idxmax() st.info(f"**Peak Sales Day:** {peak_day.strftime('%Y-%m-%d')}") # Footer st.markdown("---") st.markdown("Dashboard created with Streamlit • Data updated in real-time")

Advanced Features

File Upload and Processing

import streamlit as st
import pandas as pd
import io

st.title("Data Upload and Processing")

# Multiple file types
uploaded_file = st.file_uploader(
    "Choose a file",
    type=['csv', 'xlsx', 'json', 'txt'],
    accept_multiple_files=False
)

if uploaded_file is not None:
    # Get file details
    file_details = {
        "filename": uploaded_file.name,
        "filetype": uploaded_file.type,
        "filesize": uploaded_file.size
    }
    st.write(file_details)
    
    # Process based on file type
    if uploaded_file.type == "text/csv":
        df = pd.read_csv(uploaded_file)
        st.dataframe(df)
        
        # Data profiling
        st.subheader("Data Profile")
        st.write(f"Shape: {df.shape}")
        st.write("Data types:")
        st.write(df.dtypes)
        st.write("Summary statistics:")
        st.write(df.describe())
        
    elif uploaded_file.type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
        df = pd.read_excel(uploaded_file)
        st.dataframe(df)
        
    elif uploaded_file.type == "application/json":
        data = pd.read_json(uploaded_file)
        st.json(data.to_dict())
        
    else:
        # Read as text
        stringio = io.StringIO(uploaded_file.getvalue().decode("utf-8"))
        string_data = stringio.read()
        st.text(string_data)

# Download processed data
if st.button("Process and Download"):
    # Process data
    processed_data = df.copy()  # Your processing here
    
    # Convert to CSV
    csv = processed_data.to_csv(index=False)
    
    # Download button
    st.download_button(
        label="Download processed data",
        data=csv,
        file_name='processed_data.csv',
        mime='text/csv'
    )

Real-time Updates

import streamlit as st
import time
import random

st.title("Real-time Dashboard")

# Placeholder for dynamic content
placeholder = st.empty()

# Auto-refresh using rerun
if st.button("Start Real-time Updates"):
    for i in range(10):
        with placeholder.container():
            # Generate new data
            value = random.random()
            
            # Display metrics
            col1, col2, col3 = st.columns(3)
            col1.metric("Metric 1", f"{value:.2f}")
            col2.metric("Metric 2", f"{value*100:.0f}")
            col3.metric("Metric 3", f"{value*1000:.0f}")
            
            # Update chart
            st.line_chart([random.random() for _ in range(20)])
            
        time.sleep(1)
        
# Alternative: Using st.experimental_rerun() for auto-refresh
auto_refresh = st.checkbox("Auto-refresh every 5 seconds")
if auto_refresh:
    time.sleep(5)
    st.experimental_rerun()

Multi-page Applications

# Create a 'pages' directory with Python files:
# pages/
#   1_📊_Dashboard.py
#   2_📈_Analytics.py
#   3_⚙️_Settings.py

# Main app.py
import streamlit as st

st.set_page_config(
    page_title="Multi-Page App",
    page_icon="🎯",
)

st.title("Main Page")
st.sidebar.success("Select a page above.")

st.markdown("""
    ## Welcome to the Multi-Page App!
    
    **👈 Select a page from the sidebar**
    
    ### Pages:
    - Dashboard: View key metrics
    - Analytics: Detailed analysis
    - Settings: Configure the app
""")

# pages/1_📊_Dashboard.py
import streamlit as st

st.set_page_config(page_title="Dashboard", page_icon="📊")
st.title("Dashboard Page")
st.write("This is the dashboard page")

# Navigation between pages programmatically
if st.button("Go to Analytics"):
    st.switch_page("pages/2_📈_Analytics.py")

Deployment

Streamlit Cloud Deployment

# 1. Create requirements.txt
streamlit
pandas
numpy
plotly
scikit-learn

# 2. Create .streamlit/config.toml (optional)
[theme]
primaryColor = "#FF6B6B"
backgroundColor = "#FFFFFF"
secondaryBackgroundColor = "#F0F2F6"
textColor = "#262730"
font = "sans serif"

# 3. Push to GitHub

# 4. Deploy on Streamlit Cloud:
# - Go to share.streamlit.io
# - Connect GitHub account
# - Select repository and branch
# - Click Deploy!

# 5. Environment variables (secrets.toml)
# .streamlit/secrets.toml (local)
# Add to Streamlit Cloud dashboard for production
[database]
host = "localhost"
port = 3306
username = "user"
password = "pass"

# Access in app:
import streamlit as st
db_host = st.secrets["database"]["host"]

Docker Deployment

# Dockerfile
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

EXPOSE 8501

CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]

# Build and run
# docker build -t streamlit-app .
# docker run -p 8501:8501 streamlit-app

Best Practices

graph TD A[Streamlit App Design] --> B[Performance] A --> C[User Experience] A --> D[Code Organization] B --> E[Use Caching] B --> F[Optimize Data Loading] B --> G[Minimize Reruns] C --> H[Clear Layout] C --> I[Responsive Design] C --> J[Loading States] D --> K[Modular Functions] D --> L[Separate Pages] D --> M[Configuration Files]

Common Patterns

Authentication

import streamlit as st
import hashlib

def make_hashes(password):
    return hashlib.sha256(str.encode(password)).hexdigest()

def check_hashes(password, hashed_text):
    if make_hashes(password) == hashed_text:
        return hashed_text
    return False

# User database (in production, use a real database)
users = {"admin": make_hashes("admin123"),
         "user": make_hashes("user123")}

def login():
    st.sidebar.header("Login")
    
    username = st.sidebar.text_input("Username")
    password = st.sidebar.text_input("Password", type='password')
    
    if st.sidebar.button("Login"):
        hashed_pswd = make_hashes(password)
        
        if username in users and check_hashes(password, users[username]):
            st.session_state['logged_in'] = True
            st.session_state['username'] = username
            st.success(f"Logged in as {username}")
            return True
        else:
            st.error("Incorrect username/password")
            return False

def main_app():
    st.write(f"Welcome {st.session_state['username']}!")
    # Your app content here
    
    if st.button("Logout"):
        st.session_state['logged_in'] = False
        st.experimental_rerun()

# App logic
if 'logged_in' not in st.session_state:
    st.session_state['logged_in'] = False

if not st.session_state['logged_in']:
    login()
else:
    main_app()

Database Connection

import streamlit as st
import sqlite3
import pandas as pd

# Create connection
@st.cache_resource
def init_connection():
    return sqlite3.connect('database.db', check_same_thread=False)

conn = init_connection()

# Query data
@st.cache_data(ttl=600)
def run_query(query):
    with conn:
        return pd.read_sql(query, conn)

# Display data
df = run_query("SELECT * FROM sales LIMIT 100")
st.dataframe(df)

# Insert data
if st.button("Add Record"):
    cursor = conn.cursor()
    cursor.execute("INSERT INTO sales (date, amount) VALUES (?, ?)",
                   ('2024-01-01', 1000))
    conn.commit()
    st.success("Record added!")
    st.cache_data.clear()  # Clear cache to show new data

Streamlit vs Other Frameworks

Feature Streamlit Dash Panel Gradio
Ease of Use ✅ Excellent ⚠️ Moderate ⚠️ Moderate ✅ Excellent
Pythonic ✅ Very ⚠️ Moderate ✅ Very ✅ Very
Customization ⚠️ Limited ✅ High ✅ High ⚠️ Limited
ML Focus ✅ Good ⚠️ Basic ⚠️ Basic ✅ Excellent
Deployment ✅ Easy ⚠️ Moderate ⚠️ Moderate ✅ Easy

Practice Exercises

Exercise 1: Interactive Data Explorer

Create a data exploration app with:

  1. File upload for CSV/Excel
  2. Automatic data profiling
  3. Interactive filters for each column
  4. Dynamic visualizations based on selected columns
  5. Export filtered data

Exercise 2: ML Model Dashboard

Build a machine learning dashboard:

  1. Train/test split slider
  2. Model selection dropdown
  3. Hyperparameter tuning controls
  4. Real-time model training
  5. Performance metrics display
  6. Prediction interface

Exercise 3: Real-time Monitoring App

Create a monitoring dashboard with:

  1. Live data simulation
  2. Auto-refresh every 5 seconds
  3. Alert thresholds configuration
  4. Historical data view
  5. Export reports

Key Takeaways

Further Resources