Mathew K Analytics

Lesson 12 · Python for Data Analysts

Building a Streamlit Data App

Everything you need to build a real, interactive Streamlit dashboard: widgets, layout, charts, caching, and running the finished app. No prior Streamlit…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb

Building a Streamlit Data App#

  • Everything you need to build a real, interactive Streamlit dashboard: widgets, layout, charts, caching, and running the finished app.
  • No prior Streamlit experience needed. Let's get straight into it.

Before You Start#

  • Open a new Jupyter Notebook in VS Code and select your Python interpreter as the kernel.
  • If streamlit isn't installed yet, open a terminal in VS Code and run: pip install streamlit

Part 1: Why We Build a File, Not Notebook Cells#

Streamlit Apps Are Scripts, Not Cells#

  • A Streamlit app is a single Python script that Streamlit re-runs top to bottom every time you interact with a widget.
  • Calling Streamlit functions directly inside a notebook cell doesn't raise an error, but it also doesn't render anything useful; there's no running app around it.
  • Instead, we'll write genuine Streamlit code into app.py using regular file writes, then launch it properly from a terminal.
with open('app.py', 'w') as f:
    f.write('# Sales Dashboard App\n')
    f.write('# Generated step by step throughout this lesson\n\n')
print('app.py created')
app.py created

Part 2: Page Setup and Text#

page_setup = '''import streamlit as st
import pandas as pd
import numpy as np

st.set_page_config(page_title='Sales Dashboard', layout='wide')

st.title('Sales Dashboard')
st.write('An interactive view of synthetic regional sales data.')
'''

with open('app.py', 'a') as f:
    f.write(page_setup)
print('Page setup written')
Page setup written

Part 3: Widgets#

widgets_code = '''
st.sidebar.header('Filters')
user_name = st.sidebar.text_input('Your name', value='Analyst')
min_amount = st.sidebar.slider('Minimum order amount ($)', 0, 300, 50)
region_choice = st.sidebar.selectbox('Region', ['All', 'North', 'South', 'East', 'West'])
show_raw = st.sidebar.checkbox('Show raw data table')

st.sidebar.write(f'Hello, {user_name}!')
'''

with open('app.py', 'a') as f:
    f.write(widgets_code)
print('Widgets written')
Widgets written

Part 4: Displaying Data#

data_code = '''
@st.cache_data
def load_sales_data():
    rng = np.random.default_rng(seed=11)
    regions = ['North', 'South', 'East', 'West']
    rows = []
    for _ in range(200):
        rows.append({
            'region': rng.choice(regions),
            'product': rng.choice(['Mouse', 'Keyboard', 'Monitor', 'Webcam']),
            'amount': round(float(rng.uniform(10, 300)), 2),
        })
    return pd.DataFrame(rows)

sales = load_sales_data()
'''

with open('app.py', 'a') as f:
    f.write(data_code)
print('Data loading written')
Data loading written
filter_and_display_code = '''
filtered = sales[sales['amount'] >= min_amount]
if region_choice != 'All':
    filtered = filtered[filtered['region'] == region_choice]

st.metric('Orders shown', len(filtered))
st.metric('Total revenue', f"${filtered['amount'].sum():,.2f}")

if show_raw:
    st.dataframe(filtered)
'''

with open('app.py', 'a') as f:
    f.write(filter_and_display_code)
print('Filtering and display written')
Filtering and display written

Part 5: Charts#

charts_code = '''
st.subheader('Revenue by Region')
region_totals = filtered.groupby('region')['amount'].sum()
st.bar_chart(region_totals)

st.subheader('Order Amount Distribution')
amount_counts = filtered['amount'].round(-1).value_counts().sort_index()
st.line_chart(amount_counts)
'''

with open('app.py', 'a') as f:
    f.write(charts_code)
print('Charts written')
Charts written

Part 6: Layout#

columns_code = '''
st.subheader('Regional Comparison')
col1, col2, col3, col4 = st.columns(4)
for col, region in zip([col1, col2, col3, col4], ['North', 'South', 'East', 'West']):
    region_total = filtered[filtered['region'] == region]['amount'].sum()
    col.metric(region, f'${region_total:,.0f}')
'''

with open('app.py', 'a') as f:
    f.write(columns_code)
print('Columns layout written')
Columns layout written
tabs_code = '''
tab1, tab2 = st.tabs(['Summary', 'About'])

with tab1:
    st.write(f'Showing {len(filtered)} orders totaling ${filtered["amount"].sum():,.2f}.')

with tab2:
    st.write('This dashboard was built entirely in Python using Streamlit, pandas, and numpy.')
'''

with open('app.py', 'a') as f:
    f.write(tabs_code)
print('Tabs written')
Tabs written

Part 7: Caching#

cache_note_code = '''
st.sidebar.divider()
if st.sidebar.button('Clear cached data'):
    load_sales_data.clear()
    st.sidebar.write('Cache cleared, data will reload on next run.')
'''

with open('app.py', 'a') as f:
    f.write(cache_note_code)
print('Cache control written')
Cache control written

Part 8: Running the App#

import py_compile
py_compile.compile('app.py', doraise=True)
print('app.py is valid, runnable Python')
app.py is valid, runnable Python
with open('app.py') as f:
    contents = f.read()
print(contents)
# Sales Dashboard App
# Generated step by step throughout this lesson

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

st.set_page_config(page_title='Sales Dashboard', layout='wide')

st.title('Sales Dashboard')
st.write('An interactive view of synthetic regional sales data.')

st.sidebar.header('Filters')
user_name = st.sidebar.text_input('Your name', value='Analyst')
min_amount = st.sidebar.slider('Minimum order amount ($)', 0, 300, 50)
region_choice = st.sidebar.selectbox('Region', ['All', 'North', 'South', 'East', 'West'])
show_raw = st.sidebar.checkbox('Show raw data table')

st.sidebar.write(f'Hello, {user_name}!')

@st.cache_data
def load_sales_data():
    rng = np.random.default_rng(seed=11)
    regions = ['North', 'South', 'East', 'West']
    rows = []
    for _ in range(200):
        rows.append({
            'region': rng.choice(regions),
            'product': rng.choice(['Mouse', 'Keyboard', 'Monitor', 'Webcam']),
            'amount': round(float(rng.uniform(10, 300)), 2),
        })
    return pd.DataFrame(rows)

sales = load_sales_data()

filtered = sales[sales['amount'] >= min_amount]
if region_choice != 'All':
    filtered = filtered[filtered['region'] == region_choice]

st.metric('Orders shown', len(filtered))
st.metric('Total revenue', f"${filtered['amount'].sum():,.2f}")

if show_raw:
    st.dataframe(filtered)

st.subheader('Revenue by Region')
region_totals = filtered.groupby('region')['amount'].sum()
st.bar_chart(region_totals)

st.subheader('Order Amount Distribution')
amount_counts = filtered['amount'].round(-1).value_counts().sort_index()
st.line_chart(amount_counts)

st.subheader('Regional Comparison')
col1, col2, col3, col4 = st.columns(4)
for col, region in zip([col1, col2, col3, col4], ['North', 'South', 'East', 'West']):
    region_total = filtered[filtered['region'] == region]['amount'].sum()
    col.metric(region, f'${region_total:,.0f}')

tab1, tab2 = st.tabs(['Summary', 'About'])

with tab1:
    st.write(f'Showing {len(filtered)} orders totaling ${filtered["amount"].sum():,.2f}.')

with tab2:
    st.write('This dashboard was built entirely in Python using Streamlit, pandas, and numpy.')

st.sidebar.divider()
if st.sidebar.button('Clear cached data'):
    load_sales_data.clear()
    st.sidebar.write('Cache cleared, data will reload on next run.')

Launching It For Real#

  • Open a terminal in VS Code, in the same folder as app.py.
  • Run: streamlit run app.py
  • Streamlit opens a browser tab automatically; the sidebar filters, metrics, charts, columns, and tabs all become fully interactive.
  • Press Ctrl+C in the terminal to stop the app when you're done.

Capstone Project: Final Polish#

download_code = '''
st.sidebar.divider()
csv_data = filtered.to_csv(index=False).encode('utf-8')
st.sidebar.download_button(
    label='Download filtered data as CSV',
    data=csv_data,
    file_name='filtered_sales.csv',
    mime='text/csv',
)

st.divider()
st.caption('Built with Streamlit, pandas, and numpy.')
'''

with open('app.py', 'a') as f:
    f.write(download_code)
print('Download button and footer written')
Download button and footer written
py_compile.compile('app.py', doraise=True)
with open('app.py') as f:
    final_line_count = len(f.readlines())
print(f'app.py is valid and complete: {final_line_count} lines')
app.py is valid and complete: 83 lines

Wrap-Up: What You Learned#

  • Why Streamlit apps are scripts, not notebook cells, and building app.py progressively as a real file.
  • Page setup, titles, and text with set_page_config, title, and write.
  • Interactive widgets: text_input, slider, selectbox, checkbox, and button.
  • Displaying data with metric and dataframe.
  • Charts with bar_chart and line_chart.
  • Layout with columns and tabs.
  • Caching with @st.cache_data, and clearing it on demand.
  • Running the finished app for real with streamlit run.
  • A capstone polish adding a live download button and a footer.
  • You went from an empty file to a complete, interactive dashboard in one sitting. If you want the next build to land in your feed automatically, subscribing is the move see you in the next one.

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.