Python Tutorial
Data Visualisation with matplotlib
A good chart reveals in seconds what a table hides: trends, comparisons, distributions and relationships. matplotlib is Python's foundational plotting library; pandas' .plot() and the statistical library seaborn are built on top of it.
This lesson covers the figure-and-axes model, the essential chart types (line, bar, scatter, histogram, pie and box plots), labelling and styling, multiple charts in one figure, plotting straight from pandas, and saving charts to files. Install with pip install matplotlib.
Figures and Axes
A Figure is the whole image; an Axes is one chart inside it with its own x/y axes. The recommended style is fig, ax = plt.subplots() and then calling methods on ax: ax.plot, ax.bar, ax.set_title, ax.set_xlabel, ax.legend. plt.subplots(2, 2, figsize=(10, 8)) creates a grid of axes. Show the chart with plt.show() (a window, or inline in Jupyter) or save it with fig.savefig("chart.png", dpi=150, bbox_inches="tight").
Choosing a Chart
- Line (
plot) — change over time. - Bar (
bar/barh) — comparing categories. - Scatter (
scatter) — relationship between two numeric variables. - Histogram (
hist) — distribution of one numeric variable. - Box plot (
boxplot) — median, spread and outliers across groups. - Pie (
pie) — parts of a whole; use sparingly, bars are usually easier to read.
Clear Charts
Always give a title, axis labels with units, and a legend when there is more than one series. Start bar charts at zero, avoid 3-D effects, keep colours meaningful, and annotate the key number (ax.bar_label, ax.annotate). pandas can plot directly — df.plot(kind="bar", x="city", y="revenue", ax=ax) — and seaborn (pip install seaborn) adds attractive statistical charts such as sns.barplot, sns.histplot and sns.heatmap with one line each.
Examples
Line and bar charts with labels, legend and annotations
import matplotlib.pyplot as plt
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
online = [120, 135, 150, 170, 165, 190]
store = [100, 98, 110, 105, 120, 118]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 4))
ax1.plot(months, online, marker="o", label="Online")
ax1.plot(months, store, marker="s", linestyle="--", label="Store")
ax1.set_title("Monthly sales (thousand Rs)")
ax1.set_xlabel("Month")
ax1.set_ylabel("Sales")
ax1.legend()
ax1.grid(alpha=0.3)
totals = [o + s for o, s in zip(online, store)]
bars = ax2.bar(months, totals, color="tab:green")
ax2.bar_label(bars) # value on top of each bar
ax2.set_title("Total sales")
ax2.set_ylim(0, max(totals) * 1.15)
fig.tight_layout()
fig.savefig("sales.png", dpi=150)
# plt.show() # opens a window when run locally
print("saved sales.png with", len(fig.axes), "charts")
plt.close(fig)
saved sales.png with 2 charts
Scatter, histogram, box plot and pie in one figure
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(3)
hours = rng.uniform(1, 10, 60)
marks = 30 + 6 * hours + rng.normal(0, 6, 60)
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
axes[0, 0].scatter(hours, marks, alpha=0.7)
axes[0, 0].set(title="Study hours vs marks", xlabel="Hours", ylabel="Marks")
axes[0, 1].hist(marks, bins=10, edgecolor="white")
axes[0, 1].set(title="Distribution of marks", xlabel="Marks", ylabel="Students")
groups = [rng.normal(70, 8, 40), rng.normal(75, 5, 40), rng.normal(65, 12, 40)]
axes[1, 0].boxplot(groups, tick_labels=["Class A", "Class B", "Class C"])
axes[1, 0].set_title("Marks by class")
axes[1, 1].pie([45, 30, 25], labels=["Books", "Electronics", "Other"], autopct="%1.0f%%")
axes[1, 1].set_title("Revenue share")
fig.tight_layout()
fig.savefig("dashboard.png", dpi=120)
print("correlation:", np.corrcoef(hours, marks)[0, 1].round(2))
print("saved dashboard.png:", [ax.get_title() for ax in axes.flat])
plt.close(fig)
correlation: 0.93
saved dashboard.png: ['Study hours vs marks', 'Distribution of marks', 'Marks by class', 'Revenue share']
Plotting directly from pandas
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame({
"city": ["Pune", "Mumbai", "Delhi", "Chennai"],
"online": [420, 610, 380, 290],
"store": [300, 450, 410, 260],
}).set_index("city")
fig, ax = plt.subplots(figsize=(7, 4))
df.plot(kind="bar", ax=ax, rot=0, title="Revenue by city and channel")
ax.set_ylabel("Revenue (thousand Rs)")
fig.tight_layout()
fig.savefig("channels.png")
print(df.sum(axis=1).sort_values(ascending=False).to_dict())
print("legend:", [text.get_text() for text in ax.get_legend().get_texts()])
plt.close(fig)
{'Mumbai': 1060, 'Delhi': 790, 'Pune': 720, 'Chennai': 550}
legend: ['online', 'store']
Common Mistakes
- Charts without a title, axis labels or units.
- Using a line chart for unordered categories, or a pie chart with many slices.
- Bar charts whose y-axis does not start at zero, exaggerating differences.
- Mixing the plt.* state-machine style and the ax.* object style in the same chart.
- Forgetting plt.close() when creating many figures in a loop, which uses more and more memory.
Key Points to Remember
- fig, ax = plt.subplots() then ax.plot/bar/scatter/hist/boxplot/pie.
- Pick the chart for the question: trend, comparison, relationship, distribution or share.
- Label everything; add legends and value labels where helpful.
- subplots creates grids; savefig writes PNG/SVG/PDF files.
- pandas .plot() and seaborn build on matplotlib for quick charts.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Use your local project environment for these examples. Codelab currently runs Python and HTML/CSS/JavaScript; framework examples may need project dependencies.