The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Build an interactive Python dashboard with Streamlit by loading and validating a dataset, adding filters, calculating metrics, and displaying charts and records. This walkthrough uses a sales CSV and takes the app from local setup to deployment on Streamlit Community Cloud.
What you’ll build
The example is a sales dashboard with filters for date, region, and category; summary metrics; charts for sales and profit; a table of matching records; and a CSV download. It expects a file at data/sales.csv with these columns:
order_date, region, category, product, sales, profit, quantity
Use data you have permission to publish. The example formats amounts as dollars; change that formatting to match your data’s currency.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteChoose Streamlit for a Python-first dashboard
Streamlit is an open-source Python framework for building browser-based data applications. It suits exploratory dashboards, internal tools, machine-learning demos, portfolios, and prototypes where the logic is already in Python and a custom front end is unnecessary. Its documentation is at Streamlit.
#1 Best Overall
A notebook is usually better for investigation and narrative analysis; Streamlit is useful when someone else needs to interact with the result. A BI platform may be a better fit if non-programmers need to maintain reports or your organization depends on governed metrics and established permissions. Choose Flask or FastAPI when the primary product is an API or a conventional web application requiring custom routing and front-end control.
Set up the project and dependencies
Create this simple project structure:
streamlit-dashboard/
├── app.py
├── data/
│ └── sales.csv
└── requirements.txt
Create and activate a virtual environment from the project directory, then install the packages:
python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell
.venvScriptsActivate.ps1
pip install streamlit pandas plotly
Save the dependencies in requirements.txt so a deployment environment can install them:
streamlit
pandas
plotly
For repeatable deployments, pin versions after testing them in your environment rather than copying untested version numbers. See Streamlit’s deployment dependency guidance.
Load and validate the CSV
Use a path based on the application file rather than an absolute path from your computer. Validate the expected fields and convert dates and numeric columns before using them in filters or calculations. Add this near the top of app.py:
Rank #2
from pathlib import Path
import pandas as pd
import plotly.express as px
import streamlit as st
st.set_page_config(
page_title="Sales Dashboard",
page_icon="📊",
layout="wide",
)
DATA_PATH = Path(__file__).parent / "data" / "sales.csv"
REQUIRED_COLUMNS = {
"order_date", "region", "category", "product",
"sales", "profit", "quantity",
}
@st.cache_data
def load_data(path: str) -> pd.DataFrame:
df = pd.read_csv(path)
missing = REQUIRED_COLUMNS - set(df.columns)
if missing:
raise ValueError(
"Dataset is missing required columns: "
+ ", ".join(sorted(missing))
)
df["order_date"] = pd.to_datetime(df["order_date"], errors="coerce")
for column in ("sales", "profit", "quantity"):
df[column] = pd.to_numeric(df[column], errors="coerce")
return df.dropna(
subset=["order_date", "region", "category", "sales", "profit", "quantity"]
)
try:
df = load_data(str(DATA_PATH))
except FileNotFoundError:
st.error(f"Could not find the data file: {DATA_PATH}")
st.stop()
except ValueError as error:
st.error(str(error))
st.stop()
Rows with unparseable dates or missing required values are dropped here so they cannot silently distort the dashboard. For a real report, consider showing how many rows were excluded or resolving data-quality problems upstream. Column names in the CSV must match the expected names; if your source uses different names, rename them explicitly before validation.
Add filters before calculating metrics
Put global controls in the sidebar, then apply them to a copy of the dataset. Every metric and chart below should use that filtered result, not the unfiltered CSV.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
st.title("Sales Dashboard")
st.caption("Explore sales performance by date, region, and category.")
st.sidebar.header("Filters")
region_options = sorted(df["region"].unique())
category_options = sorted(df["category"].unique())
selected_regions = st.sidebar.multiselect(
"Region", region_options, default=region_options
)
selected_categories = st.sidebar.multiselect(
"Category", category_options, default=category_options
)
min_date = df["order_date"].min().date()
max_date = df["order_date"].max().date()
selected_dates = st.sidebar.date_input(
"Order date",
value=(min_date, max_date),
min_value=min_date,
max_value=max_date,
)
filtered_df = df[
df["region"].isin(selected_regions)
& df["category"].isin(selected_categories)
].copy()
if len(selected_dates) == 2:
start_date, end_date = selected_dates
filtered_df = filtered_df[
filtered_df["order_date"].dt.date.between(start_date, end_date)
]
if filtered_df.empty:
st.warning("No records match these filters. Broaden the date range or select more options.")
st.stop()
A multiselect can be cleared completely, producing an empty selection and therefore no matching rows. A date input configured as a range can also temporarily return just one date, so check its length before unpacking it. The empty-result message prevents blank charts from looking like an app failure.
Show useful metrics
Compute the metrics from filtered_df. Guard against division by zero when calculating profit margin:
total_sales = filtered_df["sales"].sum()
total_profit = filtered_df["profit"].sum()
total_quantity = filtered_df["quantity"].sum()
profit_margin = total_profit / total_sales if total_sales else 0
col1, col2, col3, col4 = st.columns(4)
col1.metric("Sales", f"${total_sales:,.0f}")
col2.metric("Profit", f"${total_profit:,.0f}")
col3.metric("Quantity", f"{total_quantity:,.0f}")
col4.metric("Profit margin", f"{profit_margin:.1%}")
These field names do not establish the dataset’s grain. If each row is a product line, len(filtered_df) is a row count, not necessarily an order count. If the CSV has an order_id and one order can have several rows, count distinct IDs with filtered_df["order_id"].nunique(). Likewise, define the denominator for any percentage in a way that matches the business question.
Build charts that answer specific questions
Use a line chart for change over time and bars for category comparisons. Aggregate first so the charts show totals at the intended level:
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11daily_sales = (
filtered_df.groupby("order_date", as_index=False)["sales"].sum()
)
sales_chart = px.line(
daily_sales,
x="order_date",
y="sales",
title="Sales over time",
markers=True,
)
st.plotly_chart(sales_chart, use_container_width=True)
left, right = st.columns(2)
with left:
category_sales = (
filtered_df.groupby("category", as_index=False)["sales"]
.sum().sort_values("sales", ascending=False)
)
st.plotly_chart(
px.bar(category_sales, x="category", y="sales",
title="Sales by category", text_auto=".2s"),
use_container_width=True,
)
with right:
region_profit = (
filtered_df.groupby("region", as_index=False)["profit"]
.sum().sort_values("profit", ascending=False)
)
st.plotly_chart(
px.bar(region_profit, x="region", y="profit",
title="Profit by region", text_auto=".2s"),
use_container_width=True,
)
Choose the visual to match the question: scatter plots show relationships between numeric measures; histograms and box plots show distributions; a table is best when readers need exact records. Avoid crowded pie charts and unlabeled axes.
Display and download the filtered records
Place details after the summary and charts. The download below contains the filtered rows, not the original full dataset.
st.subheader("Filtered records")
st.dataframe(
filtered_df.sort_values("order_date", ascending=False),
use_container_width=True,
hide_index=True,
)
csv = filtered_df.to_csv(index=False).encode("utf-8")
st.download_button(
"Download filtered CSV",
data=csv,
file_name="filtered_sales.csv",
mime="text/csv",
)
A download is also a data-access path. Do not offer it for confidential or personal data unless the application’s access controls and data-handling rules permit users to export those records.
Understand reruns, caching, and state
Streamlit reruns the Python script from top to bottom when a user interacts with a widget. That model keeps a simple dashboard easy to write, but repeated file reads, API calls, or expensive transformations can make interaction slow. Streamlit recommends st.cache_data for serializable results such as DataFrames and st.cache_resource for shared resources such as database connections or machine-learning models. See the caching overview.
- Cache data-loading or deterministic transformation functions when repeating the work is costly.
- Use resource caching for reusable connections or models, and take care with shared mutable objects.
- For large datasets, filter or aggregate in the database rather than loading every row into the app.
- Limit the number of rows rendered in a table and simplify overly complex charts.
Caching can also make results stale or use substantial memory; it is not a substitute for a refresh strategy. Use st.session_state when a per-user value, such as a workflow selection, must persist across reruns. It is not durable storage. Streamlit’s advanced concepts guide covers session state and related features.
Run the app locally
From the project directory, with the virtual environment active, start the app:
streamlit run app.py
The command starts a local development server and provides a browser URL. If a browser does not open automatically, copy the local URL printed in the terminal.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Deploy on Streamlit Community Cloud
Community Cloud is Streamlit’s hosted option for creating, deploying, managing, and sharing apps; its documentation describes the service as free. That does not make every part of a project free: connected databases, APIs, or other infrastructure can have their own costs. Most apps launch within a few minutes, according to Streamlit’s deployment guide.
- Push the project to GitHub with
app.py,requirements.txt, and any data files the app needs. - Sign in to Community Cloud with GitHub and start a new app.
- Select the repository, branch, and entry-point file, then deploy.
- If the build or app fails, inspect the deployment logs and check file paths, dependencies, and secrets.
Keep data paths relative to the repository, as in the example. A file on your laptop will not appear in the deployment unless it is included in the repository or retrieved from a service the app can access. Do not rely on a deployed app’s local filesystem as permanent storage; Streamlit’s data connections guidance notes that Community Cloud does not guarantee local-file persistence.
Best Value
Keep credentials out of the repository
Never put passwords or API keys in app.py or commit a secrets file. For local development, add .streamlit/secrets.toml to .gitignore and store credentials there. In Community Cloud, enter them in the app’s secrets settings instead. Read the Community Cloud secrets guide and general secrets guidance. If a credential has already been pushed to GitHub, revoke and replace it; deleting it from the latest version does not undo its exposure.
When a CSV stops being enough
A checked-in CSV works well for a small, static tutorial or portfolio example. For changing or larger datasets, consider an API or database so the app can retrieve only the relevant data. Streamlit supports ordinary Python data-access libraries and documents connections, credentials, and data handling in its guide to connecting to data.
For a database-backed dashboard, use secrets for credentials, parameterized queries, sensible query limits, and caching for expensive work. Decide how often data should refresh and whether the app needs an explicit refresh control. Community Cloud is convenient for public demos, but the hosting choice should also reflect the application’s privacy, access-control, reliability, and networking requirements. For other deployment options, see Streamlit’s deployment overview; Snowflake is one option for teams already using that platform, with billing based on runtime and query-warehouse usage as described in its billing documentation.
Recommended Free Tools
Fix common problems
- File not found: Confirm the CSV is in
data/beside the project and that its name and capitalization match. Avoid absolute paths tied to your computer. - Missing package after deployment: Add the import’s package to
requirements.txt, commit the file, and redeploy. - Date filtering behaves unexpectedly: Parse the date column as datetime before filtering; check invalid values and confirm that the selected end date is included.
- No records appear: Broaden the date range or restore cleared multiselect options. The app should report an empty result rather than display unexplained blank charts.
- The app is slow: Cache repeated loading and transformations, aggregate before charting, reduce rendered rows, or move filtering into the data source.
- Deployment fails: Read the logs, verify the selected entry point and repository contents, and ensure secrets are configured in the deployment environment.
Where to take the dashboard next
Once the single-file app works, move reusable loading, cleaning, or chart code into modules as the project grows. Add clear definitions for each metric and note data freshness so users can interpret the results. The core pattern remains the same: validate inputs, filter once, and make every displayed metric, chart, and download agree about which data it represents.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

