Lesson 33 – Matplotlib Histograms, Pie Charts & Machine Learning Statistics
Lesson 33 – Matplotlib Histograms, Pie Charts & Machine Learning Statistics
Lesson Introduction
In this lesson you will learn how to visualise data using two powerful chart types — histograms and pie charts — and then step into the exciting world of Machine Learning (ML) statistics by mastering mean, median, mode, standard deviation, and percentiles.
These are not just academic ideas. Every time a business analyses customer ages, a scientist measures temperature variation, or a developer builds a recommendation engine, they use the exact tools you will learn here.
By the end of this lesson you will be able to:
- Draw and customise histograms with Matplotlib
- Draw and customise pie charts with Matplotlib
- Understand what Machine Learning is and why statistics power it
- Calculate mean, median, and mode
- Calculate standard deviation and variance
- Use NumPy and SciPy to perform all these calculations in Python
No prior statistics knowledge is needed. Every concept is taught from scratch with plain English explanations.
Prerequisite Concepts
Before we begin, here is a quick checkpoint of the tools we will use.
What is Matplotlib?
Matplotlib is a Python library that lets you draw charts and graphs. Think of it as graph paper + coloured pens, but controlled entirely by code.
Install it if needed:
pip install matplotlib
What is NumPy?
NumPy is a Python library for fast number crunching. It can store lists of numbers in special structures called arrays and calculate things like averages instantly.
pip install numpy
What is SciPy?
SciPy builds on NumPy and adds advanced scientific tools — including the ability to find the mode of a dataset.
pip install scipy
What is a Dataset?
A dataset is simply a collection of values — for example, the ages of 100 people, or the scores of 50 students.
Part 1 — Matplotlib Histograms
What Is a Histogram?
A histogram is a type of bar chart that shows how frequently values appear within ranges (called bins).
Analogy: Imagine you asked 100 classmates how many hours they sleep per night. Some said 5, some said 7, some said 9. A histogram groups those answers into ranges — “5–6 hours”, “7–8 hours”, “9–10 hours” — and draws a bar showing how many people fall in each range.
Why do we need histograms?
- To understand the distribution (spread) of data
- To spot patterns: Is data bunched together? Spread out? Skewed to one side?
- Used constantly in data science, quality control, medical research, and finance
A histogram is different from a regular bar chart:
| Bar Chart | Histogram |
|---|---|
| Compares separate categories (e.g., apples vs oranges) | Shows frequency of a range of continuous values |
| Bars have gaps between them | Bars are touching (no gaps) — showing continuity |
Your First Histogram
import matplotlib.pyplot as plt
import numpy as np
# Create a list of 250 random ages between 0 and 130
x = np.random.normal(170, 10, 250)
plt.hist(x)
plt.show()
What does each line do?
| Line | Explanation |
|---|---|
import matplotlib.pyplot as plt |
Loads the chart-drawing tools, nicknamed plt |
import numpy as np |
Loads NumPy number tools, nicknamed np |
np.random.normal(170, 10, 250) |
Generates 250 numbers clustered around 170, with a spread of 10 |
plt.hist(x) |
Draws a histogram of those numbers |
plt.show() |
Opens and displays the chart window |
Expected output: A bell-shaped histogram centred around 170. Most bars will be near 170, with fewer bars further away on either side.
💡 Thinking prompt: What would happen if you changed
10(the spread) to30? Try it! The histogram would become much wider because values would be more spread out.
Understanding np.random.normal()
This function creates normally distributed (bell-curve) data. Three arguments control it:
np.random.normal(mean, standard_deviation, count)
| Argument | Meaning | Example |
|---|---|---|
mean |
The central value — where data clusters | 170 (heights cluster around 170 cm) |
standard_deviation |
How spread out the data is | 10 (most values are within 10 cm of 170) |
count |
How many numbers to generate | 250 data points |
Quick demo — narrow vs wide spread:
import matplotlib.pyplot as plt
import numpy as np
narrow = np.random.normal(50, 2, 200) # tightly packed around 50
wide = np.random.normal(50, 20, 200) # spread far around 50
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.hist(narrow, color='steelblue')
plt.title("Narrow Spread (std=2)")
plt.subplot(1, 2, 2)
plt.hist(wide, color='tomato')
plt.title("Wide Spread (std=20)")
plt.tight_layout()
plt.show()
Expected output: Two side-by-side histograms. The left one has a tall, narrow peak. The right one is short and wide.
💡 Thinking prompt: Which spread better represents a consistent factory product? The narrow one — meaning almost all items are very similar in size.
Controlling the Number of Bins
Bins are the ranges that your data is grouped into. More bins = more detail. Fewer bins = simpler picture.
import matplotlib.pyplot as plt
import numpy as np
x = np.random.normal(170, 10, 250)
plt.hist(x, bins=5) # only 5 groups
plt.title("5 Bins")
plt.show()
plt.hist(x, bins=30) # 30 groups — much more detail
plt.title("30 Bins")
plt.show()
Expected output (5 bins): Just 5 wide bars — you can see the general shape but not the fine detail.
Expected output (30 bins): 30 narrow bars — the bell curve shape is very visible.
💡 Rule of thumb: A good starting point for bin count is roughly the square root of your data count. For 250 data points → about 15–16 bins.
Common Beginner Mistake: Forgetting plt.show()
# WRONG — chart never appears
import matplotlib.pyplot as plt
import numpy as np
x = np.random.normal(100, 15, 100)
plt.hist(x)
# Missing plt.show()!
# CORRECT
import matplotlib.pyplot as plt
import numpy as np
x = np.random.normal(100, 15, 100)
plt.hist(x)
plt.show() # ✅ This line opens the chart window
Real-World Use of Histograms
| Field | Example |
|---|---|
| Medicine | Distribution of patient blood pressure readings |
| Education | Distribution of exam scores across a class |
| Manufacturing | Distribution of product weights to check quality |
| Finance | Distribution of daily stock price changes |
| Machine Learning | Checking if your dataset’s values are evenly spread |
Part 2 — Matplotlib Pie Charts
What Is a Pie Chart?
A pie chart is a circle divided into slices. Each slice represents a proportion (share) of the whole.
Analogy: Imagine a pizza cut into 4 slices — one person gets 2 slices, another gets 1, and two others get half a slice each. A pie chart shows exactly that kind of proportional split.
Why use pie charts?
- Perfect for showing part-to-whole relationships (e.g., what percentage of sales came from each product)
- Easy to understand at a glance
- Used in business reports, surveys, budgets, and research
Your First Pie Chart
import matplotlib.pyplot as plt
y = [35, 25, 25, 15]
plt.pie(y)
plt.show()
What does each line do?
| Line | Explanation |
|---|---|
y = [35, 25, 25, 15] |
The 4 values that will become 4 slices. They add up to 100. |
plt.pie(y) |
Draws the pie chart from those values |
plt.show() |
Displays the chart |
Expected output: A circle divided into 4 coloured slices — the largest slice takes up 35% of the circle.
💡 Note: The values don’t have to add up to 100. Matplotlib automatically converts them into percentages. If you wrote
[1, 1, 1, 1], each slice would be 25%.
Adding Labels to a Pie Chart
Without labels, a pie chart is hard to interpret. Let’s add them:
import matplotlib.pyplot as plt
y = [35, 25, 25, 15]
labels = ["Apples", "Bananas", "Cherries", "Dates"]
plt.pie(y, labels=labels)
plt.show()
Expected output: A pie chart where each slice is labelled with the fruit name.
Starting Angle: startangle
By default, the first slice starts at the 3 o’clock position (right side). You can rotate the start:
import matplotlib.pyplot as plt
y = [35, 25, 25, 15]
labels = ["Apples", "Bananas", "Cherries", "Dates"]
plt.pie(y, labels=labels, startangle=90)
plt.show()
Expected output: The first slice (“Apples”) now starts at the 12 o’clock position (top of the circle).
💡
startangle=90rotates the chart 90° counter-clockwise from the default starting position.
Exploding a Slice
You can “pull out” one slice to highlight it:
import matplotlib.pyplot as plt
y = [35, 25, 25, 15]
labels = ["Apples", "Bananas", "Cherries", "Dates"]
explode = [0.2, 0, 0, 0] # Pull the first slice (Apples) 0.2 outward
plt.pie(y, labels=labels, explode=explode, startangle=90)
plt.show()
Expected output: The “Apples” slice is visually separated from the rest of the pie, drawing the viewer’s attention to it.
Understanding the explode list:
explode = [0.2, 0, 0, 0]
# ^ ^ ^ ^
# Apples Bananas Cherries Dates
# 0.2 = pull out, 0 = stay in place
💡 Try
explode = [0.2, 0.1, 0, 0]— both Apples and Bananas will be pulled out!
Adding Shadows
import matplotlib.pyplot as plt
y = [35, 25, 25, 15]
labels = ["Apples", "Bananas", "Cherries", "Dates"]
explode = [0.2, 0, 0, 0]
plt.pie(y, labels=labels, explode=explode, startangle=90, shadow=True)
plt.show()
Expected output: The pie chart now has a subtle shadow beneath it, giving it a 3D-like appearance.
Custom Colors
import matplotlib.pyplot as plt
y = [35, 25, 25, 15]
labels = ["Apples", "Bananas", "Cherries", "Dates"]
colors = ["red", "yellow", "pink", "orange"]
plt.pie(y, labels=labels, colors=colors, startangle=90)
plt.show()
Expected output: Each slice now uses your specified colours: red for Apples, yellow for Bananas, etc.
Showing Percentages: autopct
import matplotlib.pyplot as plt
y = [35, 25, 25, 15]
labels = ["Apples", "Bananas", "Cherries", "Dates"]
plt.pie(y, labels=labels, autopct="%1.1f%%")
plt.show()
What is autopct="%1.1f%%"?
| Part | Meaning |
|---|---|
%1.1f |
Display a number with 1 decimal place |
%% |
Display a literal % sign |
Expected output: Each slice now shows its percentage, e.g., 35.0%, 25.0%, 25.0%, 15.0%.
Adding a Legend
import matplotlib.pyplot as plt
y = [35, 25, 25, 15]
labels = ["Apples", "Bananas", "Cherries", "Dates"]
plt.pie(y, labels=labels, autopct="%1.1f%%", startangle=90)
plt.legend(title="Fruits")
plt.show()
Expected output: A coloured legend box appears beside the chart, labelled “Fruits”.
Full Polished Pie Chart Example
import matplotlib.pyplot as plt
y = [35, 25, 25, 15]
labels = ["Apples", "Bananas", "Cherries", "Dates"]
explode = [0.2, 0, 0, 0]
colors = ["#e74c3c", "#f1c40f", "#e91e63", "#ff9800"]
plt.pie(
y,
labels=labels,
explode=explode,
colors=colors,
autopct="%1.1f%%",
shadow=True,
startangle=90
)
plt.legend(title="Fruits", loc="lower right")
plt.title("Fruit Sales Distribution")
plt.show()
Expected output: A professional-looking pie chart with:
- Highlighted “Apples” slice (pulled out)
- Custom colours
- Percentage labels on each slice
- Drop shadow
- Legend in the lower right
- Chart title at the top
Real-World Use of Pie Charts
| Field | Example |
|---|---|
| Business | What percentage of revenue comes from each product |
| Surveys | How respondents are split across answer options |
| Budgeting | What percentage of a budget is spent on each category |
| Education | Grade distribution in a class |
| Health | Breakdown of causes of hospital admissions |
Part 3 — Getting Started with Machine Learning
What Is Machine Learning?
Machine Learning (ML) is a branch of Artificial Intelligence where computers learn patterns from data — without being explicitly programmed for every situation.
Analogy: Imagine teaching a child to recognise cats. You don’t write rules like “cats have pointy ears, whiskers, and fur.” Instead, you show the child hundreds of cat pictures, and they learn the pattern. ML works exactly the same way — it learns from data examples.
Why does ML matter?
| Application | Example |
|---|---|
| Email filters | Detecting spam automatically |
| Recommendations | Netflix suggesting shows you’ll like |
| Medical diagnosis | Detecting tumours in X-rays |
| Self-driving cars | Recognising road signs |
| Fraud detection | Flagging unusual bank transactions |
The Core of ML: Data and Statistics
Before a machine can learn, it needs to understand the data it’s working with. This is where statistics become essential.
The key statistical concepts used in ML are:
| Concept | What it tells us |
|---|---|
| Mean | The average value |
| Median | The middle value |
| Mode | The most common value |
| Standard Deviation | How spread out values are |
| Percentile | Where a value sits relative to all others |
Let’s learn each one deeply.
Part 4 — Mean, Median, and Mode
What Is the Mean?
The mean (also called the average) is calculated by:
- Adding up all values
- Dividing by how many values there are
Formula:
Mean = (Sum of all values) ÷ (Number of values)
Simple example:
Values: 10, 20, 30
Sum = 10 + 20 + 30 = 60
Count = 3
Mean = 60 ÷ 3 = 20
In Python with NumPy:
import numpy as np
speed = [99, 86, 87, 88, 111, 86, 103, 87, 94, 78, 77, 85, 86]
x = np.mean(speed)
print(x)
Expected output:
89.77
💡 This means the average speed in this dataset is about 89.77 km/h.
Why is mean useful in ML?
The mean is used to fill in missing data, normalise features, and understand the central tendency of a dataset.
What Is the Median?
The median is the middle value when all values are sorted in order.
Why not always use the mean?
The mean is pulled by extreme values. Consider this scenario:
Salaries: 30000, 32000, 35000, 38000, 1000000
Mean = (30000 + 32000 + 35000 + 38000 + 1000000) ÷ 5 = 227000
A mean of £227,000 is misleading because 4 out of 5 people earn between £30,000–£38,000. The billionaire distorts the mean.
Median = 35000 ← the actual middle value
The median is more honest here.
How to find the median manually:
Odd count of values:
Values: 77, 86, 86, 86, 87, 87, 88, 94, 99, 103, 111
Count: 11 values (odd)
Middle position: (11+1) ÷ 2 = position 6
6th value = 87
Median = 87
Even count of values:
Values: 77, 86, 86, 87, 87, 88, 94, 99, 103, 111
Count: 10 values (even)
Two middle values: position 5 and 6 → 87 and 88
Median = (87 + 88) ÷ 2 = 87.5
In Python with NumPy:
import numpy as np
speed = [99, 86, 87, 88, 111, 86, 103, 87, 94, 78, 77, 85, 86]
x = np.median(speed)
print(x)
Expected output:
87.0
What Is the Mode?
The mode is the value that appears most often in a dataset.
Simple example:
Values: 4, 7, 7, 7, 9, 11
Mode = 7 (appears 3 times — more than any other value)
Why is mode useful?
| Scenario | How mode helps |
|---|---|
| Shoe sizes | Find the most common size to stock the most of |
| Survey answers | Find the most popular response |
| Defect tracking | Find the most common type of fault |
In Python with SciPy:
from scipy import stats
speed = [99, 86, 87, 88, 111, 86, 103, 87, 94, 78, 77, 85, 86]
x = stats.mode(speed)
print(x)
Expected output:
ModeResult(mode=86, count=3)
This tells us: 86 is the most common value, and it appears 3 times.
Comparing Mean, Median, and Mode
Let’s compare all three on the same dataset:
import numpy as np
from scipy import stats
data = [99, 86, 87, 88, 111, 86, 103, 87, 94, 78, 77, 85, 86]
print("Mean: ", np.mean(data))
print("Median:", np.median(data))
print("Mode: ", stats.mode(data).mode)
Expected output:
Mean: 89.77
Median: 87.0
Mode: 86
💡 Thinking prompt: The mean (89.77) is higher than the median (87). Why? Because the value
111pulls the mean upward. The median ignores this.
Common Beginner Mistake: Using Mean When Outliers Exist
# WRONG approach for skewed data with outliers
salaries = [30000, 32000, 35000, 38000, 2000000]
average = sum(salaries) / len(salaries)
print("Average salary:", average)
# Output: 427000 ← misleading!
import numpy as np
# BETTER — use median for skewed data
salaries = [30000, 32000, 35000, 38000, 2000000]
print("Median salary:", np.median(salaries))
# Output: 35000.0 ← realistic picture
Rule: Use mean when data is evenly spread. Use median when there are outliers or skew.
Part 5 — Standard Deviation and Variance
What Is Standard Deviation?
Standard deviation measures how spread out the values in a dataset are from the mean.
Analogy: Imagine two archery teams both hitting an average score of 80. Team A’s scores are 79, 80, 81, 80, 80. Team B’s scores are 50, 95, 100, 65, 90. Both have the same mean — but Team B is wildly inconsistent. Standard deviation captures that inconsistency.
- Low standard deviation → values are close to the mean → consistent, predictable
- High standard deviation → values are spread far from the mean → varied, unpredictable
Two Example Datasets Side by Side
import numpy as np
speed1 = [86, 87, 88, 86, 87, 85, 86] # similar values — low spread
speed2 = [32, 111, 138, 28, 59, 77, 97] # very different values — high spread
print("Speed1 Mean:", np.mean(speed1))
print("Speed2 Mean:", np.mean(speed2))
print()
print("Speed1 Std Dev:", np.std(speed1))
print("Speed2 Std Dev:", np.std(speed2))
Expected output:
Speed1 Mean: 85.57
Speed2 Mean: 77.43
Speed1 Std Dev: 0.90
Speed2 Std Dev: 37.85
💡 Both datasets have similar means (~85 vs ~77), but Speed1 has a tiny standard deviation (0.90) while Speed2 has a huge one (37.85). Speed1 values barely deviate from their mean; Speed2 values vary wildly.
Calculating Standard Deviation Step by Step
Let’s manually understand how it’s calculated with a tiny dataset: [4, 8, 6, 5, 3]
Step 1 — Find the mean:
Mean = (4 + 8 + 6 + 5 + 3) ÷ 5 = 26 ÷ 5 = 5.2
Step 2 — Find each value’s distance from the mean (deviation):
4 - 5.2 = -1.2
8 - 5.2 = 2.8
6 - 5.2 = 0.8
5 - 5.2 = -0.2
3 - 5.2 = -2.2
Step 3 — Square each deviation (to remove negatives):
(-1.2)² = 1.44
( 2.8)² = 7.84
( 0.8)² = 0.64
(-0.2)² = 0.04
(-2.2)² = 4.84
Step 4 — Find the mean of those squares (this is the VARIANCE):
Variance = (1.44 + 7.84 + 0.64 + 0.04 + 4.84) ÷ 5
= 14.80 ÷ 5
= 2.96
Step 5 — Square root of the variance = Standard Deviation:
Standard Deviation = √2.96 ≈ 1.72
In Python with NumPy (does all 5 steps instantly):
import numpy as np
data = [4, 8, 6, 5, 3]
variance = np.var(data)
std_dev = np.std(data)
print("Variance: ", variance)
print("Standard Deviation:", std_dev)
Expected output:
Variance: 2.96
Standard Deviation: 1.7204650534085253
✅ NumPy’s result matches our manual calculation exactly.
What Is Variance?
Variance is simply the square of the standard deviation — or equivalently, the mean of the squared deviations. It’s an intermediate step in calculating standard deviation.
Variance = (Standard Deviation)²
Standard Deviation = √Variance
| Concept | Formula | Unit |
|---|---|---|
| Variance | Mean of squared deviations | km² (if data is in km) |
| Standard Deviation | √Variance | km (same unit as the data) |
Standard deviation is more practical because it’s in the same unit as your data.
What Are Percentiles?
A percentile tells you what percentage of values fall below a specific value.
Analogy: If you scored in the 90th percentile on an exam, it means 90% of students scored lower than you.
import numpy as np
ages = [5, 31, 43, 48, 50, 41, 7, 11, 15, 39, 80, 82, 32, 2, 8, 6, 25, 36, 27, 61, 31]
p75 = np.percentile(ages, 75)
print("75th percentile:", p75)
Expected output:
75th percentile: 43.0
This means: 75% of people in this dataset are younger than 43 years old.
Percentiles in Action
import numpy as np
ages = [5, 31, 43, 48, 50, 41, 7, 11, 15, 39, 80, 82, 32, 2, 8, 6, 25, 36, 27, 61, 31]
for p in [25, 50, 75, 90]:
val = np.percentile(ages, p)
print(f"{p}th percentile: {val}")
Expected output:
25th percentile: 11.0
50th percentile: 31.0
75th percentile: 43.0
90th percentile: 61.0
💡 The 50th percentile is always equal to the median. Notice above: 50th percentile = 31, and
np.median(ages)would also give 31.
Why Standard Deviation and Percentiles Matter in ML
| ML Use Case | Why it matters |
|---|---|
| Feature normalisation | Ensures all features (inputs) are on the same scale |
| Outlier detection | Values more than 2–3 standard deviations from the mean are flagged as unusual |
| Model performance | Understanding how consistent predictions are |
| Data quality checks | Verifying your dataset is realistic and not corrupted |
Guided Practice Exercises
Exercise 1 — Histogram of Exam Scores
Objective: Draw a histogram showing the distribution of 200 student exam scores.
Scenario: A teacher wants to see how their students performed.
Steps:
- Use
np.random.normal(65, 12, 200)to simulate scores centred around 65 with spread of 12 - Draw a histogram with 15 bins
- Add a title:
"Student Exam Score Distribution" - Add x-label:
"Score"and y-label:"Number of Students"
Solution:
import matplotlib.pyplot as plt
import numpy as np
scores = np.random.normal(65, 12, 200)
plt.hist(scores, bins=15, color='steelblue', edgecolor='white')
plt.title("Student Exam Score Distribution")
plt.xlabel("Score")
plt.ylabel("Number of Students")
plt.show()
Expected output: A bell-shaped histogram centred around 65, with bars showing how many students scored in each 5-point range.
Self-check questions:
- Which score range contains the most students?
- What happens if you change the standard deviation from 12 to 3?
- What does a very tall, narrow histogram tell you about the class?
Exercise 2 — Pie Chart of Monthly Budget
Objective: Create a pie chart showing a personal monthly budget breakdown.
Scenario: A young professional wants to visualise where their money goes.
Steps:
- Create values:
[800, 300, 200, 150, 100, 450] - Create labels:
["Rent", "Food", "Transport", "Entertainment", "Savings", "Other"] - Explode the “Rent” slice (the biggest expense)
- Show percentages with
autopct - Add a legend
Solution:
import matplotlib.pyplot as plt
amounts = [800, 300, 200, 150, 100, 450]
labels = ["Rent", "Food", "Transport", "Entertainment", "Savings", "Other"]
explode = [0.1, 0, 0, 0, 0, 0]
colors = ["#e74c3c", "#3498db", "#2ecc71", "#f39c12", "#9b59b6", "#95a5a6"]
plt.pie(
amounts,
labels=labels,
explode=explode,
colors=colors,
autopct="%1.1f%%",
shadow=True,
startangle=90
)
plt.legend(title="Categories", loc="lower left")
plt.title("Monthly Budget Breakdown")
plt.show()
Expected output: A colourful pie chart where Rent is the largest slice (pulled out), with all percentages labelled.
Exercise 3 — Mean, Median, Mode of Temperature Data
Objective: Analyse a week of temperature readings.
Scenario: A weather station recorded daily temperatures.
import numpy as np
from scipy import stats
temperatures = [22, 21, 23, 22, 25, 28, 22, 20, 19, 22, 24, 22, 21]
mean_temp = np.mean(temperatures)
median_temp = np.median(temperatures)
mode_temp = stats.mode(temperatures)
std_temp = np.std(temperatures)
print(f"Mean temperature: {mean_temp:.2f}°C")
print(f"Median temperature: {median_temp:.2f}°C")
print(f"Mode temperature: {mode_temp.mode}°C (appeared {mode_temp.count} times)")
print(f"Standard deviation: {std_temp:.2f}°C")
Expected output:
Mean temperature: 22.38°C
Median temperature: 22.00°C
Mode temperature: 22°C (appeared 5 times)
Standard deviation: 2.04°C
Self-check questions:
- The mean and median are very close — what does this suggest about the data?
- The standard deviation is about 2°C — is that high or low variability for daily temperature?
Mini-Project — Student Performance Analyser
Project Overview
You will build a complete Python program that:
- Stores student test scores
- Visualises the distribution with a histogram
- Visualises grade categories with a pie chart
- Calculates all key statistics
- Prints a full report
Stage 1 — Setup and Data
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# Simulate 50 student scores (mean 70, std dev 15, min 0, max 100)
np.random.seed(42) # seed ensures same results every run
raw_scores = np.random.normal(70, 15, 50)
scores = np.clip(raw_scores, 0, 100) # clamp between 0 and 100
print("First 10 scores:", scores[:10].round(1))
print("Total students:", len(scores))
Expected output:
First 10 scores: [79.3 57.9 76.4 84.3 76.2 72.5 80.3 73.3 54.2 64.4]
Total students: 50
Stage 2 — Statistics Report
mean = np.mean(scores)
median = np.median(scores)
mode = stats.mode(scores.round())
std = np.std(scores)
p25 = np.percentile(scores, 25)
p75 = np.percentile(scores, 75)
print("=" * 40)
print(" STUDENT PERFORMANCE REPORT")
print("=" * 40)
print(f"Mean Score: {mean:.2f}")
print(f"Median Score: {median:.2f}")
print(f"Mode Score: {mode.mode:.1f}")
print(f"Standard Deviation: {std:.2f}")
print(f"25th Percentile: {p25:.2f}")
print(f"75th Percentile: {p75:.2f}")
print(f"Highest Score: {scores.max():.2f}")
print(f"Lowest Score: {scores.min():.2f}")
print("=" * 40)
Expected output:
========================================
STUDENT PERFORMANCE REPORT
========================================
Mean Score: 70.22
Median Score: 70.69
Mode Score: 70.0
Standard Deviation: 14.82
25th Percentile: 60.14
75th Percentile: 81.23
Highest Score: 98.44
Lowest Score: 31.03
========================================
Stage 3 — Histogram
plt.figure(figsize=(8, 5))
plt.hist(scores, bins=10, color='steelblue', edgecolor='white')
plt.axvline(mean, color='red', linestyle='dashed', linewidth=2, label=f'Mean: {mean:.1f}')
plt.axvline(median, color='green', linestyle='dashed', linewidth=2, label=f'Median: {median:.1f}')
plt.title("Student Score Distribution")
plt.xlabel("Score")
plt.ylabel("Number of Students")
plt.legend()
plt.tight_layout()
plt.show()
Expected output: A histogram with a red dashed line for the mean and a green dashed line for the median. Both lines will be close together (indicating roughly symmetrical data).
Stage 4 — Grade Pie Chart
# Categorise scores into grade bands
def grade(score):
if score >= 70: return "Pass (≥70)"
elif score >= 50: return "Near Pass (50–69)"
else: return "Fail (<50)"
grade_counts = {"Pass (≥70)": 0, "Near Pass (50–69)": 0, "Fail (<50)": 0}
for s in scores:
grade_counts[grade(s)] += 1
labels = list(grade_counts.keys())
values = list(grade_counts.values())
colors = ["#2ecc71", "#f39c12", "#e74c3c"]
plt.figure(figsize=(7, 7))
plt.pie(values, labels=labels, colors=colors, autopct="%1.1f%%", startangle=90, shadow=True)
plt.title("Grade Distribution")
plt.show()
print("Grade Breakdown:")
for label, count in grade_counts.items():
print(f" {label}: {count} students ({count/50*100:.1f}%)")
Expected output (chart): A 3-slice pie chart showing percentages of students in each grade band.
Expected output (text):
Grade Breakdown:
Pass (≥70): 28 students (56.0%)
Near Pass (50–69): 17 students (34.0%)
Fail (<50): 5 students (10.0%)
(Exact numbers may vary slightly due to random generation.)
Stage 5 — Reflection
After completing the project, ask yourself:
- If the standard deviation were 5 instead of 15, how would the histogram look different?
- If 40% of students failed, what would the pie chart’s red slice look like?
- How would you use the mean and median to advise the teacher on whether the exam was too hard?
Optional extension challenges:
- Add a second dataset (a second class) and compare their histograms side by side
- Colour the histogram bars differently for pass/fail zones
- Export the statistics report to a
.txtfile using Python’sopen()function
Common Beginner Mistakes
Mistake 1 — Confusing Histogram with Bar Chart
# WRONG — using bar() for continuous numerical distribution
categories = ["60-70", "70-80", "80-90"]
counts = [10, 25, 15]
plt.bar(categories, counts) # ← this works but is semantically wrong for distributions
# CORRECT — use hist() for continuous numerical data
data = [65, 72, 74, 78, 81, 85, 88, 63, 71]
plt.hist(data) # ← Matplotlib handles the binning automatically
Mistake 2 — Forgetting to Import NumPy or SciPy
# WRONG
x = mean([1, 2, 3]) # mean() is not a built-in Python function!
# CORRECT
import numpy as np
x = np.mean([1, 2, 3])
Mistake 3 — Using Mean for Skewed Data
import numpy as np
house_prices = [200000, 210000, 195000, 205000, 1500000] # one outlier
print("Mean: ", np.mean(house_prices)) # 462000 — misleading!
print("Median:", np.median(house_prices)) # 205000 — realistic
Output:
Mean: 462000.0
Median: 205000.0
Mistake 4 — Misreading stats.mode() Output
from scipy import stats
data = [4, 4, 7, 9]
result = stats.mode(data)
# WRONG — printing the whole object
print(result) # Prints: ModeResult(mode=4, count=2)
# CORRECT — access .mode and .count attributes
print("Mode:", result.mode) # 4
print("Count:", result.count) # 2
Mistake 5 — Not Setting bins Intentionally
import matplotlib.pyplot as plt
import numpy as np
data = np.random.normal(50, 10, 1000)
# Default bins might hide patterns
plt.hist(data) # Uses 10 bins by default — may not be ideal
# Better — set bins deliberately
plt.hist(data, bins=30) # More detail visible
plt.show()
Reflection Questions
-
A dataset has
mean = 50andmedian = 35. What does this tell you about the shape of the data? -
You have two groups of factory parts. Group A has
std = 0.2 mm, Group B hasstd = 5 mm. Which group’s parts are more consistent? -
A pie chart has one slice at 75%. Does that mean it’s the mode of the data? Explain the difference.
-
If you calculate the 90th percentile of student heights and get 185 cm, what does that mean in plain English?
-
You’re building an ML model to predict house prices. Should you use mean or median to represent a “typical” house price in a city? Why?
-
Why do histograms have touching bars while regular bar charts have gaps?
-
A histogram is very flat (all bars are roughly the same height). What does this tell you about the data?
Completion Checklist
Before moving on, confirm you can do each of the following:
- Draw a histogram using
plt.hist()with customised bins - Draw a pie chart using
plt.pie()with labels, colours, explode, shadow, and percentages - Add a legend to a pie chart with
plt.legend() - Explain the difference between mean, median, and mode in plain English
- Calculate mean using
np.mean() - Calculate median using
np.median() - Calculate mode using
stats.mode() - Calculate standard deviation using
np.std() - Calculate variance using
np.var() - Calculate percentiles using
np.percentile() - Decide whether to use mean or median based on data shape
- Combine histogram + pie chart + statistics into a working mini-project
Lesson Summary
What You Learned
Histograms:
- A histogram groups numerical data into bins and shows how many values fall in each range
plt.hist(data, bins=n)draws it- Use
np.random.normal(mean, std, count)to generate realistic test data - More bins = more detail; fewer bins = simpler overview
Pie Charts:
- A pie chart shows each value as a proportion (slice) of the whole
- Key parameters:
labels,explode,colors,autopct,shadow,startangle - Add a legend with
plt.legend()
Machine Learning Foundation:
- ML learns patterns from data — statistics help us understand that data
- The three measures of central tendency: mean (average), median (middle), mode (most common)
- Use mean for symmetric data; use median when outliers exist
Spread and Variation:
- Standard deviation = how far values typically stray from the mean
- Variance = standard deviation squared (intermediate calculation)
- Percentiles = what % of values fall below a given point
- Low std dev = consistent; high std dev = variable
Quick Reference
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
data = [10, 20, 20, 30, 40, 50]
# Central tendency
np.mean(data) # Average
np.median(data) # Middle value
stats.mode(data).mode # Most common value
# Spread
np.std(data) # Standard deviation
np.var(data) # Variance
np.percentile(data, 75) # 75th percentile
# Histogram
plt.hist(data, bins=10)
plt.show()
# Pie chart
plt.pie([35, 25, 25, 15], labels=["A","B","C","D"], autopct="%1.1f%%")
plt.show()
End of Lesson 33