DataLens

Showing DataFrames and charts

Pick between Streamlit's quick built-in charts and full Plotly figures, prepare data in the right shape, and let widgets drive the charts.

Level: Intermediate · 6 min read

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

The idea

A Streamlit app is often "a few widgets, then a chart that reacts to them". Streamlit gives you two ways to draw:

Use built-ins while exploring; use Plotly for anything a stakeholder will see.

Built-in charts

They take a DataFrame and draw numeric columns against the index (or against x= if you name one):

import streamlit as st
import pandas as pd

sales = pd.read_csv("sales.csv")
completed = sales[sales["status"] == "completed"]
monthly = (completed.assign(month=completed["order_date"].str[:7])
           .groupby("month")["revenue"].sum())

st.subheader("Completed revenue per month")
st.line_chart(monthly)

monthly is a Series indexed by month, so the months become the x-axis. With a regular DataFrame, name the columns:

import streamlit as st
import pandas as pd

sales = pd.read_csv("sales.csv")
completed = sales[sales["status"] == "completed"]
by_category = completed.groupby("category", as_index=False)["revenue"].sum()

st.bar_chart(by_category, x="category", y="revenue")

Several numeric columns become several series. A wide table (one column per channel) draws one line per channel:

import streamlit as st
import pandas as pd

sales = pd.read_csv("sales.csv")
completed = sales[sales["status"] == "completed"]
wide = (completed.assign(month=completed["order_date"].str[:7])
        .pivot_table(index="month", columns="channel", values="revenue", aggfunc="sum", fill_value=0))
st.line_chart(wide)
st.dataframe(wide)

Note the shapes: Streamlit's built-ins like wide data (a column per series), while Plotly Express likes long data (a channel column plus color="channel"). pivot_table and melt convert between them.

Plotly charts

For control over titles, labels, hover and colours, build a Plotly figure and hand it over:

import streamlit as st
import pandas as pd
import plotly.express as px

sales = pd.read_csv("sales.csv")
completed = sales[sales["status"] == "completed"]
by_category = (completed.groupby("category", as_index=False)["revenue"].sum()
               .sort_values("revenue"))

fig = px.bar(by_category, x="revenue", y="category", orientation="h", text_auto=".2s",
             title="Electronics leads completed revenue",
             labels={"revenue": "Revenue (Rp)", "category": ""})
st.plotly_chart(fig, use_container_width=True)

use_container_width=True stretches the chart to the column or page width, which is what you want in layouts with columns. Hover, zoom and download all work as in a notebook.

Your chart functions from the Plotly track (ranking_bar, trend_line) plug straight in: st.plotly_chart(ranking_bar(...)). That's the payoff of returning figures instead of showing them.

Widgets drive charts

The rerun model makes interactive charts trivial: read a widget, filter, draw.

import streamlit as st
import pandas as pd
import plotly.express as px

sales = pd.read_csv("sales.csv")
completed = sales[sales["status"] == "completed"].assign(month=lambda d: d["order_date"].str[:7])

metric = st.radio("Measure", ["revenue", "quantity"], horizontal=True)
channel = st.selectbox("Channel", ["all", "web", "app"])
rows = completed if channel == "all" else completed[completed["channel"] == channel]
monthly = rows.groupby("month", as_index=False)[metric].sum()

fig = px.line(monthly, x="month", y=metric, markers=True,
              title=f"Monthly {metric} ({channel})")
st.plotly_chart(fig, use_container_width=True)

Every change reruns the script, rebuilding monthly and the figure from the new inputs. No update functions anywhere.

Tables that read well

st.dataframe takes a few options that make tables easier to read:

import streamlit as st
import pandas as pd

sales = pd.read_csv("sales.csv")
completed = sales[sales["status"] == "completed"]
top = (completed.groupby("product", as_index=False)
       .agg(units=("quantity", "sum"), revenue=("revenue", "sum"))
       .sort_values("revenue", ascending=False)
       .head(5))

st.dataframe(top, hide_index=True, use_container_width=True)

Prepare the table in pandas (aggregate, sort, rename columns to friendly names) and let st.dataframe display it. Formatting in pandas (.round(), renaming columns) keeps the display code minimal.

Why this matters for clean code

The same separation as in the Plotly track applies: data prep → figure → display. Keep each step in its own few lines (or functions), so the Streamlit part is just st.plotly_chart(fig). When the chart looks wrong, you can check the DataFrame and the figure separately, without clicking through the app.

Recap

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

Next: session_state: remembering values across reruns →