The Visualization Stack
Three libraries cover almost every need. matplotlib is the low-level foundation with total control. seaborn sits on top for beautiful statistical charts in one line. plotly produces interactive charts for dashboards and notebooks.
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import pandas as pd, numpy as np
Figure & Axes
matplotlib has two APIs. Prefer the explicit object-oriented style: create a Figure (the canvas) and one or more Axes (the plots), then call methods on the Axes.
x = np.linspace(0, 10, 100)
fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(x, np.sin(x), label="sin")
ax.set_title("A Simple Line")
ax.set_xlabel("x")
ax.set_ylabel("sin(x)")
ax.legend()
fig.tight_layout()
plt.show()
OO vs pyplot
The stateful plt.plot() style is fine for quick throwaway plots, but the fig, ax style scales to complex, multi-panel figures without surprises.
Core Chart Types
Four plot types cover the majority of everyday needs.
Line
Best for trends over an ordered axis such as time.
fig, ax = plt.subplots()
ax.plot(x, y, color="teal", linewidth=2, linestyle="--")
Scatter
Shows the relationship between two numeric variables.
ax.scatter(df["height"], df["weight"],
c=df["age"], cmap="viridis", alpha=0.7)
Bar
Compares a numeric value across categories.
counts = df["team"].value_counts()
ax.bar(counts.index, counts.values)
ax.barh(counts.index, counts.values) # horizontal
Histogram
Reveals the distribution of a single numeric variable.
ax.hist(df["score"], bins=20, edgecolor="white")
Subplots
Arrange multiple Axes in a grid to compare views side by side. plt.subplots(rows, cols) returns the figure and an array of Axes.
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].plot(x, np.sin(x))
axes[0].set_title("sin")
axes[1].plot(x, np.cos(x))
axes[1].set_title("cos")
fig.suptitle("Trig Functions")
fig.tight_layout()
# 2x2 grid -> index with [row][col]
fig, axes = plt.subplots(2, 2)
axes[0][1].hist(data)
Styling
A few settings dramatically improve readability. Apply a style sheet, set limits and ticks, and always label axes.
plt.style.use("seaborn-v0_8-darkgrid") # global theme
fig, ax = plt.subplots()
ax.plot(x, y, marker="o", markersize=4)
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)
ax.grid(True, alpha=0.3)
ax.set_xticks([0, 5, 10])
ax.annotate("peak", xy=(1.6, 1), xytext=(3, 0.9),
arrowprops=dict(arrowstyle="->"))
fig.savefig("chart.png", dpi=150, bbox_inches="tight")
seaborn: Statistical Plots
seaborn takes a tidy DataFrame and column names, then does the heavy lifting. It shines for distributions, relationships, and correlation matrices.
Distributions
sns.histplot(df, x="score", hue="team", kde=True)
sns.boxplot(df, x="team", y="score")
sns.violinplot(df, x="team", y="score")
Heatmaps
corr = df.select_dtypes("number").corr()
sns.heatmap(corr, annot=True, cmap="coolwarm",
vmin=-1, vmax=1, fmt=".2f")
Pairplot
# scatter matrix of every numeric pair, colored by category
sns.pairplot(df, hue="team", diag_kind="kde")
seaborn returns matplotlib
seaborn draws onto matplotlib Axes, so you can still tweak the result with ax.set_title(...) and other matplotlib methods afterward.
plotly: Interactive Glimpse
plotly express creates zoomable, hoverable charts with the same tidy-DataFrame API. Perfect for exploration and web dashboards.
fig = px.scatter(df, x="height", y="weight",
color="team", size="age",
hover_name="name")
fig.show()
px.line(df, x="date", y="revenue", color="region")
px.bar(df, x="team", y="score")
px.histogram(df, x="score", nbins=20)
Choosing the Right Chart
Match the chart to the question you are answering, not to what looks impressive.
| Goal | Chart | Function |
|---|---|---|
| Trend over time | Line | ax.plot |
| Relationship (2 numeric) | Scatter | ax.scatter |
| Compare categories | Bar | ax.bar |
| Distribution | Histogram / KDE | sns.histplot |
| Spread & outliers | Box / Violin | sns.boxplot |
| Correlation matrix | Heatmap | sns.heatmap |
| Part of a whole | Stacked bar | ax.bar(bottom=) |
Avoid pie charts
Humans compare lengths far better than angles. A bar chart almost always communicates proportions more clearly than a pie chart, especially with more than 3 slices.
Design Principles
Good charts share a few habits: label every axis with units, start bar-chart y-axes at zero, use color intentionally (not decoratively), and remove clutter that does not carry information.
Practice Exercises
- Using the object-oriented API, plot both
sin(x)andcos(x)on the same Axes with a legend and title. - Create a 1x2 subplot figure: a histogram of one column on the left and a scatter plot on the right.
- Load a dataset and draw a seaborn boxplot of a numeric column grouped by a category.
- Compute the correlation matrix of the numeric columns and visualize it as an annotated heatmap.
- Build a plotly express scatter plot with color and size encoding two extra variables, and hover labels.
- For a "revenue by month" question, pick the right chart type and justify your choice in one sentence.