Plotting basics

pandas · Practical · 10 min

A quick chart reveals trends, outliers and distribution shapes that summary tables hide.

Every Series and DataFrame has a `.plot` accessor that draws with Matplotlib (which must be installed). It's built for fast exploratory charts with sensible defaults: the index becomes the x-axis and each column becomes a series.

Chart kinds via kind= or the accessor methods:

  • df.plot() / .plot.line(): trends over an ordered index such as dates
  • .plot.bar() / .plot.barh(): compare categories, typically after a groupby
  • .plot.hist(bins=30): distribution of one column; .plot.box() compares distributions
  • .plot.scatter(x="a", y="b"): relationship between two columns (DataFrame only)
  • .plot.area(), .plot.pie() for the occasional special case

Most plotting is the last step of a chain: aggregate first, then plot. df.groupby("region")["sales"].sum().sort_values().plot.barh() is a complete chart. For a time series, set a DatetimeIndex, maybe resample, then plot.

Useful arguments: title=, figsize=(8, 4), logy=True, subplots=True (one panel per column), ax= to draw into an existing Matplotlib axes, and color=. Each call returns a Matplotlib Axes, so you can keep customising with the Matplotlib API: ax.set_ylabel("EUR"), ax.axhline(0), and save with ax.figure.savefig("chart.png", dpi=150).

In a script, call matplotlib.pyplot.show() to open a window; in Jupyter charts appear automatically.

pandas plotting is for exploration and simple reports. For publication styling or interactive charts, pass the tidy (long) data to Seaborn, Plotly or Altair. pandas can also switch its backend with pd.options.plotting.backend = "plotly" if that package is installed.

Example

import matplotlib
matplotlib.use("Agg")                   # headless: just write files
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

rng = np.random.default_rng(1)
days = pd.date_range("2024-01-01", periods=90, freq="D")
df = pd.DataFrame({"visits": rng.poisson(200, 90).cumsum(),
                   "region": rng.choice(["N", "S", "E"], 90),
                   "spend": rng.gamma(2, 20, 90)}, index=days)

ax = df["visits"].plot(title="Cumulative visits", figsize=(8, 3))
ax.set_ylabel("visits")
ax.figure.savefig("visits.png", dpi=120)

fig, (left, right) = plt.subplots(1, 2, figsize=(9, 3))   # new figure
df.groupby("region")["spend"].sum().sort_values().plot.barh(ax=left)
df["spend"].plot.hist(bins=20, alpha=0.7, ax=right, title="Spend")
fig.savefig("spend.png")
print(type(left).__name__)              # Axes

Remember

  • .plot on a Series/DataFrame draws with Matplotlib; the index is the x-axis
  • Aggregate first, then plot: groupby result plus .plot.bar() is a full chart
  • Each call returns a Matplotlib Axes for further tweaks and savefig
  • Use Seaborn, Plotly or Altair for polished or interactive charts

Watch out for

  • Plotting unsorted, unaggregated raw rows as a line chart and getting a meaningless zigzag.
  • Calling .plot repeatedly in a script without ax= or a new figure, so charts are drawn on top of each other in the current axes.

Check yourself

  1. What does `df.groupby('city')['sales'].sum().plot.bar()` put on the x-axis?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: pandas.pydata.org/docs/user_guide/visualization.html

Builds on

Unlocks

Nothing builds on this yet.