Plotting and quick exploratory charts

df.plot, the chart kinds worth knowing, subplots for comparisons, styling, and when to hand the figure over to Matplotlib or Seaborn.

One line to a chart

import matplotlib.pyplot as plt

df.plot(x="date", y="sales", kind="line", figsize=(9, 4), title="Daily sales")

df.groupby("team")["score"].mean().sort_values().plot(kind="barh")
df.plot.scatter(x="price", y="units", alpha=0.6, s=12)
df["score"].plot.hist(bins=20, edgecolor="white")
df.set_index("date")["sales"].resample("ME").sum().plot.area(stacked=False)

df[["score", "hours"]].plot(subplots=True, layout=(1, 2), sharex=False, figsize=(11, 4))
plt.tight_layout()
plt.show()
  • The index is the x-axis by default; when a date column exists, set_index first or pass x= explicitly.
  • A DatetimeIndex gets sensible date ticks automatically - one of the few places where the index being special is a help.
  • Plot from an aggregated frame, not from raw rows: groupby or resample first, or the chart shows noise at full resolution.
  • Matplotlib must be installed; pandas only dispatches to it.

Choosing a chart kind

kindShows
lineA trend over an ordered axis, usually time
bar / barhComparison between categories; horizontal when labels are long
histThe distribution of one numeric column
boxSpread and outliers per group
scatterRelationship between two numeric columns
areaComposition over time, stacked
kdeA smoothed version of the histogram
pieParts of a whole - readable with at most four slices
💡
A pandas plot is a Matplotlib figure: ax = df.plot(...) returns the axes, so you can keep styling with ax.set_ylabel(), ax.legend() and ax.axhline(). There is no separate charting language to learn.

When to leave pandas

fig, axes = plt.subplots(1, 2, figsize=(11, 4))

long = df.melt(id_vars=["date"], value_vars=["sales", "returns"], var_name="metric")
for name, part in long.groupby("metric"):
    axes[0].plot(part["date"], part["value"], label=name)
axes[0].legend(loc="upper left")
axes[0].set_title("Sales and returns")

df.boxplot(column="score", by="team", ax=axes[1])
plt.suptitle("")
plt.tight_layout()
  • Long format plus a groupby loop is the general pattern for several series with a legend - the same melt you use for modelling.
  • Move to Seaborn when you need faceting, regression lines or statistical summaries: sns.relplot(data=df, x=..., y=..., col=...).
  • Move to Matplotlib when you need exact control of axes, annotations or a publication layout.
  • Never build a chart by calling plot() repeatedly on the same axes without checking which object owns the state; collect the axes explicitly.

FAQ

How do I save a chart to a file?
Keep the plot call and call plt.savefig("chart.png", dpi=150, bbox_inches="tight") before plt.show(). Saving after showing can produce an empty file in some backends.
Why is my chart empty?
Usually the data was not aggregated or the axis is text: a line plot needs a sorted numeric or datetime x-axis. Inspect the aggregated frame you plotted - the bug is almost always there, not in the plotting call.

Dates, times and time series Pivot tables and crosstab

Last refreshed 2026-09-18.