Seaborn, Plotly & Polars

Data Science · Lesson 31 of 32 7 min read Updated

NumPy, pandas and matplotlib can do almost everything in this course, and they are what most jobs expect. But three other libraries come up constantly in data science work: seaborn for statistical charts in one line, Plotly for interactive charts you can hover and zoom, and Polars, a newer and very fast DataFrame library.

They are not available in this site's browser editor, so their examples are shown as code to run on your own computer, with the chart or result described in words. Each section ends with what the same task looks like in the tools you already know.

Installing them

All three install like any other package (see Tools & Setup):

Terminal
uv add seaborn plotly polars

At the time of writing, seaborn's latest release is 0.13.2 (it builds on matplotlib and pandas), Plotly's Python library is at version 7, and Polars reached version 2 in 2026. Check each project's documentation for the current version before you rely on a newer feature.

seaborn: statistical charts in one line

seaborn sits on top of matplotlib. You pass a DataFrame and column names, and it handles grouping, colours, legends and statistical extras such as confidence bands.

Python
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

students = pd.read_csv("students.csv", na_values=["AB"]).drop_duplicates()
sns.set_theme(style="whitegrid")

sns.boxplot(data=students, x="city", y="maths")
plt.show()

sns.histplot(data=students, x="maths", hue="tuition", kde=True)
plt.show()

sns.regplot(data=students, x="study_hours", y="maths", scatter_kws={"alpha": 0.4})
plt.show()

The first call draws one box of maths marks per city with no loops and no list of arrays. The second draws two overlapping histograms, one per tuition group, each with a smooth density curve. The third draws a scatter plot of study hours against maths marks with a fitted straight line and a shaded band showing the uncertainty of that line. Because seaborn returns ordinary matplotlib axes, everything from Subplots & Styling still works on its charts.

seaborn also has a newer objects interface, import seaborn.objects as so, which builds charts layer by layer, for example so.Plot(students, x="study_hours", y="maths").add(so.Dots()). Most code you will find online still uses the function style above.

For comparison, here is the first seaborn chart written with matplotlib alone. You can run it here.

PythonRun it yourself
import pandas as pd
import matplotlib.pyplot as plt

students = pd.read_csv("students.csv", na_values=["AB"]).drop_duplicates()
cities = sorted(students["city"].unique())
data = [students.loc[students["city"] == c, "maths"].dropna() for c in cities]

fig, ax = plt.subplots(figsize=(7, 4))
ax.boxplot(data, tick_labels=cities)
ax.set_xlabel("city")
ax.set_ylabel("maths")
ax.grid(axis="y", alpha=0.3)
plt.show()

print(students.groupby("city")["maths"].median())
Output
city
Jaipur     57.0
Kochi      59.0
Lucknow    57.5
Pune       58.0
Rohtak     61.0
Name: maths, dtype: float64

The chart shows five boxes, one per city, with similar medians in the high 50s and early 60s. matplotlib needed you to build the list of groups by hand; seaborn does it from the column names. That convenience is seaborn's main selling point.

Plotly: interactive charts

Plotly draws charts in a web page: readers can hover over a point to see its values, zoom into a period, and click legend entries to hide series. Plotly Express (plotly.express, imported as px) is its quick, one-function-per-chart interface.

Python
import pandas as pd
import plotly.express as px

sales = pd.read_csv("sales.csv", parse_dates=["order_date"]).drop_duplicates()
sales["revenue"] = sales["quantity"] * sales["unit_price"] * (1 - sales["discount"] / 100)
weekly = sales.set_index("order_date")["revenue"].resample("W").sum().reset_index()

fig = px.line(weekly, x="order_date", y="revenue", title="Weekly revenue, 2025")
fig.show()
fig.write_html("weekly_revenue.html")

students = pd.read_csv("students.csv", na_values=["AB"]).drop_duplicates()
fig = px.scatter(students, x="study_hours", y="maths", color="tuition", hover_data=["name", "city"])
fig.show()

The first chart is the weekly revenue line from earlier lessons, but hovering shows the exact week and amount, and dragging zooms into October. write_html() saves a single HTML file you can email or put on a website; it opens in any browser. In the scatter plot, hovering over a dot shows that student's name and city. Plotly suits dashboards and reports read on screen; for printed reports and papers, matplotlib's static images are usually better. Recent Plotly versions work with pandas and Polars DataFrames alike.

Polars: a fast DataFrame library

Polars is a DataFrame library written in Rust. It is designed to use all CPU cores and to optimise whole queries before running them, so it is often much faster than pandas on large data. Its API is different from pandas: there is no row index, and you build expressions with pl.col().

Python
import polars as pl

sales = pl.read_csv("sales.csv")

summary = (
    sales
    .unique()
    .with_columns(
        (pl.col("quantity") * pl.col("unit_price") * (1 - pl.col("discount") / 100)).alias("revenue")
    )
    .group_by("category")
    .agg(pl.len().alias("orders"), pl.col("revenue").sum().round(0))
    .sort("revenue", descending=True)
)
print(summary)
Output
shape: (6, 3)
┌────────────────┬────────┬──────────┐
│ category       ┆ orders ┆ revenue  │
│ ---            ┆ ---    ┆ ---      │
│ str            ┆ u32    ┆ f64      │
╞════════════════╪════════╪══════════╡
│ Fashion        ┆ 272    ┆ 529526.0 │
│ Electronics    ┆ 301    ┆ 502862.0 │
│ Home & Kitchen ┆ 215    ┆ 450092.0 │
│ Groceries      ┆ 187    ┆ 178462.0 │
│ Sports         ┆ 122    ┆ 134330.0 │
│ Books          ┆ 112    ┆ 111569.0 │
└────────────────┴────────┴──────────┘

unique() drops duplicate rows, with_columns() adds a column from an expression, and group_by().agg() aggregates. Polars prints the shape and each column's type above the table (str, u32, f64). pl.len() counts rows per group. Note that Polars does not keep the group order unless you sort, which is why the query ends with sort().

Polars also has a lazy mode. pl.scan_csv() does not read the file immediately; it builds a plan, and only collect() runs it. Polars can then read only the columns and rows the query needs:

Python
import polars as pl

pune = (
    pl.scan_csv("sales.csv")
    .filter(pl.col("city") == "Pune")
    .select(["order_id", "product", "quantity"])
    .collect()
)
print(pune.shape)
Output
(106, 3)

Here is the first Polars query in pandas, runnable here. Compare the two styles line by line.

PythonRun it yourself
import pandas as pd

sales = pd.read_csv("sales.csv")

summary = (
    sales
    .drop_duplicates()
    .assign(revenue=pd.col("quantity") * pd.col("unit_price") * (1 - pd.col("discount") / 100))
    .groupby("category")
    .agg(orders=("order_id", "size"), revenue=("revenue", "sum"))
    .round(0)
    .sort_values("revenue", ascending=False)
)
print(summary)
Output
                orders   revenue
category                        
Fashion            272  529526.0
Electronics        301  502862.0
Home & Kitchen     215  450092.0
Groceries          187  178462.0
Sports             122  134330.0
Books              112  111569.0

The two libraries give the same numbers, and pandas 3.0's pd.col() makes the pandas version look much like Polars. pandas keeps category as the index, while Polars keeps it as an ordinary column.

Which tool when?

ToolChoose it whenStay with the core stack when
seabornYou want quick statistical charts from a DataFrameYou need fine control over every detail
PlotlyReaders will explore charts on screen or in a web appThe output is a printed report or paper
PolarsData is large (millions of rows) or speed mattersYour team, tutorials and libraries expect pandas

Other names you will hear: DuckDB runs SQL queries directly on CSV and Parquet files and on DataFrames; statsmodels provides regression and classical statistical models with detailed reports; and scikit-learn, used in the Machine Learning course, builds predictive models.

Common mistakes

  • Learning every library at once. Master pandas and matplotlib first; the others then take days, not months.
  • Mixing pandas habits into Polars. There is no index and no .loc; think in expressions with pl.col().
  • Interactive charts in print. A Plotly chart in a PDF loses everything that made it useful.
  • Copying old seaborn code. Functions such as distplot were deprecated in favour of histplot and displot.

Key points

  • seaborn builds statistical charts from DataFrame column names and returns matplotlib axes.
  • Plotly Express (px) makes interactive charts; write_html() saves them as a standalone web page.
  • Polars is a fast, index-free DataFrame library built on expressions and a lazy query mode.
  • pandas 3.0's pd.col() brings an expression style close to Polars.
  • Install them with uv add or pip install; they do not run in this site's browser editor.

Exercise

Rewrite this Polars query in pandas and run it: read weather.csv, keep rows where temp_max is below 60, group by city, compute the mean temp_max (rounded to 1 decimal) as avg_max and the maximum rainfall_mm as wettest_day, and sort by avg_max from highest to lowest. In Polars it would be pl.read_csv("weather.csv").filter(pl.col("temp_max") < 60).group_by("city").agg(pl.col("temp_max").mean().round(1).alias("avg_max"), pl.col("rainfall_mm").max().alias("wettest_day")).sort("avg_max", descending=True).

Show solution
Python
import pandas as pd

weather = pd.read_csv("weather.csv")
result = (
    weather[weather["temp_max"] < 60]
    .groupby("city")
    .agg(avg_max=("temp_max", "mean"), wettest_day=("rainfall_mm", "max"))
    .round({"avg_max": 1})
    .sort_values("avg_max", ascending=False)
)
print(result)
Output
         avg_max  wettest_day
city                         
Chennai     32.6        106.7
Jaipur      32.2         36.8
Mumbai      31.5         77.6
Delhi       30.9         46.9
Kochi       30.1        108.9

Polars' filter is a boolean selection in pandas, group_by().agg() maps to named aggregation, and sort(descending=True) becomes sort_values(ascending=False).

Quick Quiz

1. Which library is built on matplotlib and makes statistical charts from DataFrame column names?

2. What does fig.write_html("chart.html") do in Plotly?

3. What does collect() do in a lazy Polars query?

4. Which statement about Polars is true?

Learn Data Science with a mentor

Free lessons get you started. Our Data Science & Machine Learning training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call