Skip to main content
Streamlit’s primary use case is turning data workflows into interactive apps, which means integrating tightly with Pandas, NumPy, and Matplotlib is central to almost everything you’ll build. This chapter shows you how to load data efficiently with caching, display and filter DataFrames, perform quick NumPy calculations, and render charts — all inside a Streamlit app. By the end you’ll have the core patterns needed to build fully interactive data exploration tools.

Loading CSV Data

The standard approach is to read your CSV with Pandas inside a cached function. The @st.cache_data decorator tells Streamlit to run the function only once, then store the result in memory. On subsequent reruns (triggered by widget interactions), Streamlit returns the cached DataFrame instantly instead of re-reading the file.
Without caching, every widget interaction would re-read and re-parse your CSV from disk — noticeably slow for large files.

Handling Uploaded Files

When you want users to bring their own data, combine st.file_uploader with pd.read_csv:

Displaying DataFrames

Filtering DataFrames with Widgets

Combining Pandas filters with Streamlit widgets is the heart of interactive data apps. User selections from widgets become filter conditions on your DataFrame:

NumPy Calculations

NumPy integrates naturally into Streamlit. You can display NumPy outputs with st.write() or use them to feed charts:

Rendering Matplotlib Charts

Streamlit renders Matplotlib figures with st.pyplot(fig). Always create the figure explicitly using the object-oriented interface (fig, ax = plt.subplots()) to avoid state leakage between reruns.
Always pass the fig object explicitly to st.pyplot(fig). Calling st.pyplot() without an argument (using the global Matplotlib state) is deprecated and can produce unexpected charts as your app grows.

Interactive Data Exploration Pattern

This pattern combines filtering widgets, a filtered DataFrame display, and a chart into a reusable template:
Structure your data apps so that expensive operations (file reading, API calls, database queries) happen inside @st.cache_data functions. Everything that depends on widget values belongs in the main script body where it can react to user input.