Data Visualization
Building a Dashboard with Streamlit
Every visualization so far — even Plotly's interactive charts (Chapter 4) — is still a single, static analysis output. A dashboard lets a non-technical user exp
Jr Codex Data Science Notes
Level: Intermediate–Advanced Prerequisites: Chapter 4 Time to complete: ~30 minutes
Table of Contents
- From Charts to a Shareable App
- Your First Streamlit App
- Displaying Data & Charts
- Interactive Widgets
- Layout — Columns & Sidebars
- Putting It Together: a Small Sales Dashboard
- Caching for Performance
- Summary & Next Steps
1. From Charts to a Shareable App
Every visualization so far — even Plotly's interactive charts (Chapter 4) — is still a single, static analysis output. A dashboard lets a non-technical user explore data themselves: pick a date range, filter by region, and see charts update live, without touching any code.
The Progression
─────────────────────────────────────────
Static chart (Ch.2-3) → Interactive chart (Ch.4) → Interactive APP (this chapter)
"here's a picture" "explore this ONE chart" "explore the WHOLE dataset,
your own way"
─────────────────────────────────────────
Streamlit turns a plain Python script into a web app — no HTML, CSS, or JavaScript required, making it the fastest path from "I have a pandas analysis" to "I have a shareable tool."
pip install streamlit2. Your First Streamlit App
# app.py
import streamlit as st
st.title("My First Dashboard")
st.write("Hello! This is a Streamlit app.")
name = st.text_input("What's your name?")
if name:
st.write(f"Hello, {name}!")streamlit run app.pyThis opens a browser tab with a live, interactive page — every time a user interacts with a widget, Streamlit re-runs the entire script top to bottom, updating anything downstream of that widget automatically.
The Streamlit Mental Model
─────────────────────────────────────────
Script runs top-to-bottom, ONE TIME per interaction.
A widget's current value is just a normal Python variable —
no callback functions or event listeners to write yourself.
─────────────────────────────────────────
3. Displaying Data & Charts
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
st.title("Sales Overview")
df = pd.read_csv("sales_data.csv")
st.dataframe(df.head(10)) # an interactive, sortable table
st.write(df.describe()) # Module 1's descriptive stats, rendered directly
fig, ax = plt.subplots()
ax.hist(df["revenue"], bins=30)
st.pyplot(fig) # renders a matplotlib figure (Chapter 2) directly in the app
import plotly.express as px
fig2 = px.line(df, x="month", y="revenue")
st.plotly_chart(fig2) # renders a Plotly figure (Chapter 4), fully interactiveStreamlit natively displays pandas DataFrames, matplotlib figures, and Plotly charts — everything built in earlier chapters plugs directly in without conversion.
4. Interactive Widgets
import streamlit as st
import pandas as pd
df = pd.read_csv("sales_data.csv")
# Dropdown / selectbox
region = st.selectbox("Choose a region", options=df["region"].unique())
# Slider
min_spend, max_spend = st.slider(
"Spend range", min_value=0, max_value=int(df["spend"].max()), value=(0, 500)
)
# Multi-select
selected_regions = st.multiselect("Compare regions", options=df["region"].unique())
# Date input
start_date = st.date_input("Start date")
# Checkbox
show_raw_data = st.checkbox("Show raw data")
# Using the widget values to FILTER the data — this is where the interactivity pays off
filtered_df = df[
(df["region"] == region) &
(df["spend"] >= min_spend) &
(df["spend"] <= max_spend)
]
st.write(f"Showing {len(filtered_df)} of {len(df)} rows")
if show_raw_data:
st.dataframe(filtered_df)| Widget | Function |
|---|---|
| Dropdown | st.selectbox() |
| Slider | st.slider() |
| Multi-select | st.multiselect() |
| Text input | st.text_input() |
| Checkbox | st.checkbox() |
| Button | st.button() |
5. Layout — Columns & Sidebars
import streamlit as st
import pandas as pd
df = pd.read_csv("sales_data.csv")
# Sidebar — great for filters, keeping the main area for RESULTS
with st.sidebar:
st.header("Filters")
region = st.selectbox("Region", df["region"].unique())
min_date = st.date_input("Start date")
# Columns — arrange elements side by side, instead of stacked vertically
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Total Revenue", f"${df['revenue'].sum():,.0f}")
with col2:
st.metric("Average Order", f"${df['revenue'].mean():.2f}")
with col3:
st.metric("Total Orders", len(df))Layout Structure
─────────────────────────────────────────
┌─────────┬───────────────────────────────┐
│ │ Total Rev │ Avg Order │ Count │ ← st.columns(3)
│ Sidebar │───────────────────────────────┤
│ Filters │ │
│ │ [ main chart area ] │
└─────────┴───────────────────────────────┘
─────────────────────────────────────────
st.metric() is purpose-built for exactly this kind of dashboard "headline number" display — directly connecting to Chapter 1's audience-driven visualization principle: a business stakeholder often wants three clear numbers before any chart at all.
6. Putting It Together: a Small Sales Dashboard
A complete, small dashboard combining everything above:
# sales_dashboard.py
import streamlit as st
import pandas as pd
import plotly.express as px
st.set_page_config(page_title="Sales Dashboard", layout="wide")
st.title("📊 Sales Dashboard")
@st.cache_data # covered in Section 7
def load_data():
return pd.read_csv("sales_data.csv", parse_dates=["order_date"])
df = load_data()
# Sidebar filters
with st.sidebar:
st.header("Filters")
regions = st.multiselect("Region", options=df["region"].unique(), default=df["region"].unique())
date_range = st.date_input(
"Date range",
value=(df["order_date"].min(), df["order_date"].max()),
)
# Apply filters — the same boolean masking from Python Notes' NumPy/Pandas primers
filtered = df[
(df["region"].isin(regions)) &
(df["order_date"] >= pd.Timestamp(date_range[0])) &
(df["order_date"] <= pd.Timestamp(date_range[1]))
]
# Headline metrics
col1, col2, col3 = st.columns(3)
col1.metric("Total Revenue", f"${filtered['revenue'].sum():,.0f}")
col2.metric("Total Orders", f"{len(filtered):,}")
col3.metric("Avg Order Value", f"${filtered['revenue'].mean():.2f}")
# Charts
st.subheader("Revenue Over Time")
revenue_by_date = filtered.groupby("order_date")["revenue"].sum().reset_index()
fig_line = px.line(revenue_by_date, x="order_date", y="revenue")
st.plotly_chart(fig_line, use_container_width=True)
st.subheader("Revenue by Region")
revenue_by_region = filtered.groupby("region")["revenue"].sum().reset_index()
fig_bar = px.bar(revenue_by_region, x="region", y="revenue", color="region")
st.plotly_chart(fig_bar, use_container_width=True)
if st.checkbox("Show raw filtered data"):
st.dataframe(filtered)streamlit run sales_dashboard.pyThis single file becomes a live, shareable tool — anyone with access to it can filter by region and date range and see every number and chart update instantly, with zero code changes required on their end.
7. Caching for Performance
Recall Section 2's mental model: Streamlit re-runs the entire script on every interaction. Without caching, this would mean re-reading sales_data.csv from disk every single time a user moves a slider — wasteful and slow.
import streamlit as st
import pandas as pd
@st.cache_data # decorator (Python Notes, Module 5, Ch.2) — caches the function's return value
def load_data():
print("Loading data from disk...") # this only prints ONCE, even across many reruns
return pd.read_csv("sales_data.csv")
df = load_data() # subsequent calls with the SAME inputs return the cached result instantly@st.cache_data — Directly Related to functools.lru_cache
─────────────────────────────────────────
Python Notes, Module 5, Ch.4 covered @lru_cache for memoizing
expensive function calls. @st.cache_data is Streamlit's version
of the exact same idea, specialized for data loading and
transformation steps inside a dashboard.
─────────────────────────────────────────
Rule of thumb: cache any function that loads data from disk/network or does an expensive computation that doesn't change between reruns — never cache functions whose result should change every time (like one incorporating live randomness or a live timestamp).
8. Summary & Next Steps
Key Takeaways
- Streamlit turns a plain Python script into a shareable web app, re-running the entire script on every widget interaction — no HTML/JS/callback functions required.
- Widgets (
selectbox,slider,multiselect) become plain Python variables whose current value flows directly into filtering logic, using the same boolean masking from the Python Notes. st.dataframe,st.pyplot, andst.plotly_chartdisplay pandas, matplotlib, and Plotly output directly — everything from Chapters 2-4 plugs straight in.@st.cache_data(the dashboard analog of@lru_cache) prevents expensive data loading from re-running on every single user interaction.
Module 4 Complete — Next Module
You now have the full visualization toolkit — from choosing the right chart type, to static (matplotlib/seaborn) and interactive (Plotly) charts, up to a fully shareable dashboard (Streamlit). Module 5 shifts from describing data to testing claims about it rigorously — A/B testing and applied statistics.
Concept Check
- What happens to the entire Streamlit script every time a user interacts with a widget?
- Why would you wrap a
pd.read_csv()call in@st.cache_data? - How does a Streamlit widget's value (e.g. from
st.slider()) actually get used to filter a DataFrame?
Next Chapter
→ Module 5: Experimentation & Applied Statistics
Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index