Introduction
Every data project starts the same way: open a CSV, check its shape, peek at rows, check for nulls, eyeball distributions. This app automates that ritual into an instant explorer — upload any CSV and get a full profile, interactive filtering, sorting, and charts with zero configuration. It is the tool you will actually use weekly, built from the exact patterns in the Pandas dashboard tutorial.
The design philosophy is infer everything: column types, filter controls, and chart suggestions all derive from the data itself, so the app works on any file you throw at it.
Features
Prerequisites
pip install streamlit pandasStep 1: Create the Script
Save as csv_explorer.py:
import streamlit as st
import pandas as pd
st.set_page_config(page_title="CSV Explorer", page_icon="🗂️", layout="wide")
st.title("🗂️ CSV Data Explorer")
uploaded = st.file_uploader("Upload a CSV", type=["csv"])
if uploaded:
df = pd.read_csv(uploaded)
c1, c2, c3 = st.columns(3)
c1.metric("Rows", f"{len(df):,}")
c2.metric("Columns", len(df.columns))
c3.metric("Null cells", int(df.isna().sum().sum()))
with st.expander("📋 Column profile"):
profile = pd.DataFrame({
"dtype": df.dtypes.astype(str),
"nulls": df.isna().sum(),
"unique": df.nunique(),
})
st.dataframe(profile, use_container_width=True)
# Sidebar filters inferred from column types
st.sidebar.header("Filters")
filtered = df.copy()
for col in df.columns:
if pd.api.types.is_numeric_dtype(df[col]) and df[col].nunique() > 10:
lo, hi = float(df[col].min()), float(df[col].max())
if lo < hi:
rng = st.sidebar.slider(col, lo, hi, (lo, hi), key=f"f_{col}")
filtered = filtered[filtered[col].between(*rng)]
elif df[col].nunique() <= 25:
choices = st.sidebar.multiselect(col, sorted(df[col].dropna().unique()), key=f"f_{col}")
if choices:
filtered = filtered[filtered[col].isin(choices)]
st.subheader(f"{len(filtered):,} rows match your filters")
st.dataframe(filtered, use_container_width=True, height=350)
st.divider()
col_chart, type_hint = st.columns([0.3, 0.7])
chart_col = col_chart.selectbox("Chart a column", df.columns)
if pd.api.types.is_numeric_dtype(df[chart_col]):
st.bar_chart(filtered[chart_col].value_counts().head(20))
st.caption("Distribution (top 20 values)")
else:
st.bar_chart(filtered[chart_col].value_counts().head(15))
st.caption("Count per category (top 15)")
else:
st.info("Upload a CSV file to start exploring.")Step 2: Run the App
streamlit run csv_explorer.pyTry it on any CSV you have handy — exports from the expense tracker or habit tracker work great.
How It Works
The app is built around one Pandas concept: type introspection. pd.api.types.is_numeric_dtype decides which UI each column gets — sliders for numbers, multiselects for low-cardinality categories. The nunique() > 10 guard prevents absurd sliders on ID-like numeric columns, and nunique() <= 25 keeps category pickers sane on free-text columns.
Filters compose by construction: each one narrows the filtered copy in sequence, so combining "amount between 10–50" and "category is Food" just works. Because every widget has a key derived from the column name, switching files resets cleanly.
The chart section applies the same inference: numeric columns get value distributions, categorical columns get counts. value_counts().head(20) is the workhorse — top-N only, so a column with 10,000 unique values doesn't produce 10,000 bars.
Common Errors & Fixes
pd.read_csv(uploaded, encoding="latin-1") or detect with charset-normalizer.parse_dates="infer" or list date columns explicitly; ISO-format dates usually parse automatically.pd.read_csv(uploaded, usecols=[...]) or sample with nrows=50000 for exploration.Key Concepts
What to Try Next
df.corr() and st.pyplot.to_csv.pd.read_excel — see the Excel report generator for the writing side.FAQ
How large a CSV can this handle?
Comfortably up to a few hundred thousand rows on a laptop — Streamlit serializes the dataframe to the browser, which becomes the bottleneck before Pandas does. Sample beyond that.
Can I save my filters?
They persist while the session lives (widget state). For reusable views, store filter settings in a JSON file keyed by filename.
Why does my ID column get a slider?
It's numeric with many unique values — exactly what the threshold can't distinguish from a measure. Add a name-based exclusion (if "id" in col) for your conventions.