# Data Visualisation with matplotlib — Bar, Line, Histogram, Scatter and Telling a Story

Source: https://skills101.in/learn/data-science-ml/matplotlib-data-visualization — Skills101 free tutorial (Python for Data Science & Machine Learning). Updated 2026-11-09.

A good chart answers a question in five seconds. Every chart on this page was drawn by the exact code above it.

## 1. Which chart?

| Question | Chart |
|---|---|
| Compare categories (branches, cities) | **bar** |
| Change over time | **line** |
| How values are spread | **histogram** (or box plot) |
| Relationship between two numbers | **scatter** |
| Parts of a whole | stacked bar (pie charts only for 2–3 parts) |

## 2. Bar chart: compare categories

```python
import matplotlib.pyplot as plt

branches = ["CSE", "IT", "ECE", "ME", "Civil"]
rate = [92, 88, 74, 61, 48]                       # placement rate %

fig, ax = plt.subplots(figsize=(7, 3.6))
bars = ax.bar(branches, rate, color=["#c94f0f" if r == max(rate) else "#94a3b8" for r in rate])
ax.bar_label(bars, fmt="%d%%")                     # value on each bar
ax.set_title("Placement rate by branch, 2026 batch (practice data)")
ax.set_ylabel("Placed (%)")
ax.set_ylim(0, 100)
ax.spines[["top", "right"]].set_visible(False)     # less clutter
plt.savefig("placement-bar.svg")
```

![Bar chart of placement rate by branch: CSE 92%, IT 88%, ECE 74%, ME 61%, Civil 48%, with CSE highlighted](https://skills101.in/learn-img/data-science-ml/placement-bar.svg)

Highlight the one bar that matters, label values directly, remove chart junk.

## 3. Line chart: trends over time

```python
import matplotlib.pyplot as plt

months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
signups = [120, 135, 160, 210, 340, 310]
paid = [12, 15, 18, 25, 44, 41]

fig, ax = plt.subplots(figsize=(7, 3.6))
ax.plot(months, signups, marker="o", label="Sign-ups")
ax.plot(months, paid, marker="o", label="Paid")
ax.annotate("Exam season", xy=(4, 340), xytext=(1.6, 300), arrowprops={"arrowstyle": "->"})
ax.set_title("Monthly sign-ups (practice data)")
ax.legend()
ax.spines[["top", "right"]].set_visible(False)
plt.savefig("signups-line.svg")
```

![Line chart of monthly sign-ups rising from 120 in January to a peak of 340 in May, annotated Exam season, with paid users rising from 12 to 44](https://skills101.in/learn-img/data-science-ml/signups-line.svg)

An **annotation** tells the reader *why* the line moves — that's the story.

## 4. Histogram: how values are spread

```python
import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(1)
cgpa = rng.normal(7.4, 0.8, 500).clip(4, 10)

fig, ax = plt.subplots(figsize=(7, 3.6))
ax.hist(cgpa, bins=20, color="#2563eb", edgecolor="white")
ax.axvline(cgpa.mean(), color="#c94f0f", linestyle="--", label=f"mean {cgpa.mean():.2f}")
ax.set_title("CGPA of 500 students (simulated)")
ax.set_xlabel("CGPA")
ax.set_ylabel("Students")
ax.legend()
plt.savefig("cgpa-hist.svg")
```

![Histogram of 500 simulated CGPAs forming a bell shape centred near 7.4, with a dashed line at the mean](https://skills101.in/learn-img/data-science-ml/cgpa-hist.svg)

## 5. Scatter plot: relationships

```python
import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(3)
cgpa = rng.uniform(6, 10, 120)
package = 2 + 1.1 * (cgpa - 6) + rng.normal(0, 0.8, 120)

fig, ax = plt.subplots(figsize=(7, 3.6))
ax.scatter(cgpa, package, alpha=0.6)
slope, intercept = np.polyfit(cgpa, package, 1)        # best-fit straight line
xs = np.array([6, 10])
ax.plot(xs, slope * xs + intercept, color="#c94f0f", label=f"trend: +{slope:.2f} LPA per CGPA point")
ax.set_xlabel("CGPA")
ax.set_ylabel("Package (LPA)")
ax.set_title("Higher CGPA, higher package — on average (simulated)")
ax.legend()
plt.savefig("cgpa-package-scatter.svg")
```

![Scatter plot of 120 simulated students showing package rising with CGPA, with a red trend line](https://skills101.in/learn-img/data-science-ml/cgpa-package-scatter.svg)

Remember: a trend is not a guarantee for each person, and **correlation is not causation** (lesson 7).

## 6. Several charts together

```python
import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(5)
aptitude = rng.normal(62, 12, 300).clip(0, 100)

fig, (left, right) = plt.subplots(1, 2, figsize=(8, 3.4))
left.hist(aptitude, bins=15, color="#7c3aed", edgecolor="white")
left.set_title("Aptitude scores")
right.boxplot(aptitude, orientation="horizontal")
right.set_title("Same data as a box plot")
fig.tight_layout()
plt.savefig("aptitude-subplots.svg")
```

![Two charts side by side: a histogram of aptitude scores and a horizontal box plot of the same data showing the median, quartiles and outliers](https://skills101.in/learn-img/data-science-ml/aptitude-subplots.svg)

The **box plot** shows median, middle 50% (the box) and outliers (dots) — compact for comparing many groups.

## Chart rules that impress managers

1. A **title that states the finding**, not just the topic.
2. Label axes with units; label values directly where possible.
3. One highlight colour; everything else grey.
4. Start bar charts at zero.
5. Say where the data comes from and whether it's a sample.

(For interactive dashboards look at **Plotly**, **Power BI** or **Tableau**; **seaborn** gives prettier statistical charts with less code.)

## Common mistakes

- Pie charts with 8 slices; 3-D charts.
- Bar charts not starting at zero (exaggerates differences).
- Rainbow colours with no meaning.
- No title or units.

## Quick recap

- Bar → compare, line → trend, histogram/box → spread, scatter → relationship.
- `fig, ax = plt.subplots()`, then `ax.bar / plot / hist / scatter`, labels, `savefig`.
- Highlight, annotate, simplify.


Practice quiz and free certificate: https://skills101.in/learn/data-science-ml/matplotlib-data-visualization