Mean, Median & Mode
"What is the average?" is the most common question in statistics, and it has more than one answer. The mean, the median and the mode each describe the centre of the data in a different way, and they can give very different numbers for the same list. Choosing the right one is often more important than calculating it.
In this lesson you will compute all three by hand and with Python, see how one extreme value changes them, and learn three useful variations: the weighted mean (used for your CGPA), the trimmed mean (used in judged sports) and the mean and median of grouped data.
The three measures
These are the formulas in plain text. Σ (sigma) means "add up".
mean = (x1 + x2 + ... + xn) / n = Σx / n
median = the middle value after sorting
n odd: the value in position (n + 1) / 2
n even: the average of the two middle values
mode = the value that appears most oftenWork through a batter's scores from nine innings. First sort them: 0, 7, 12, 21, 33, 45, 45, 78, 104.
- Mean: the total is 345, and 345 / 9 = 38.33 runs.
- Median: n = 9 is odd, so the median is the value in position (9 + 1) / 2 = 5, which is 33.
- Mode: 45 appears twice and every other score once, so the mode is 45.
The same steps in plain Python, without any statistics library:
# Scores of a batter in 9 innings (invented)
scores = [12, 45, 0, 78, 33, 104, 7, 45, 21]
mean = sum(scores) / len(scores)
ordered = sorted(scores)
n = len(ordered)
mid = n // 2
if n % 2 == 1:
median = ordered[mid]
else:
median = (ordered[mid - 1] + ordered[mid]) / 2
counts = {}
for s in scores:
counts[s] = counts.get(s, 0) + 1
top = max(counts.values())
modes = [value for value, c in counts.items() if c == top]
print("Sorted:", ordered)
print("Mean:", round(mean, 2), " Median:", median, " Mode:", modes)Sorted: [0, 7, 12, 21, 33, 45, 45, 78, 104] Mean: 38.33 Median: 33 Mode: [45]
Python lists start at index 0, so the fifth value is ordered[4], which is ordered[n // 2] when n is 9. The mode code collects every value that ties for the top count, because a dataset can have more than one mode.
The same thing with libraries
Python's statistics module, NumPy, SciPy and pandas all compute these measures. They agree on the values but return slightly different types.
import statistics
import numpy as np
import pandas as pd
from scipy import stats
scores = [12, 45, 0, 78, 33, 104, 7, 45, 21]
print("statistics:", statistics.mean(scores), statistics.median(scores), statistics.mode(scores))
print("NumPy: ", np.mean(scores), np.median(scores))
res = stats.mode(scores)
print("SciPy: ", res.mode, "appears", res.count, "times")
s = pd.Series(scores)
print("pandas: ", s.mean(), s.median(), s.mode().tolist())
print("Median of an even count:", statistics.median([12, 45, 0, 78, 33, 104, 7, 45, 21, 60]))
print("Two modes:", statistics.multimode([2, 3, 3, 5, 5, 8]))statistics: 38.333333333333336 33 45 NumPy: 38.333333333333336 33.0 SciPy: 45 appears 2 times pandas: 38.333333333333336 33.0 [45] Median of an even count: 39.0 Two modes: [3, 5]
NumPy has no mode function, so use scipy.stats.mode() (which returns the smallest mode if there is a tie) or pandas mode() (which returns all of them). With ten scores, the median is the average of the 5th and 6th sorted values, 33 and 45, which gives 39.0. statistics.multimode() returns every mode, which matters for data such as shoe sizes where two values can be equally common.
Outliers pull the mean, not the median
The mean uses every value, so one extreme value can drag it a long way. The median only cares about the middle position, so it barely moves.
import numpy as np
# Monthly salaries (₹) at a small Jaipur office (invented)
staff = np.array([18_000, 21_000, 22_000, 24_000, 25_000, 27_000, 30_000])
print(f"Staff only: mean ₹{staff.mean():,.0f} median ₹{np.median(staff):,.0f}")
with_owner = np.append(staff, 3_50_000)
print(f"Owner included: mean ₹{with_owner.mean():,.0f} median ₹{np.median(with_owner):,.0f}")Staff only: mean ₹23,857 median ₹24,000 Owner included: mean ₹64,625 median ₹24,500
Adding the owner's ₹3,50,000 nearly triples the mean, to a salary that nobody in the office earns. The median moves by only ₹500. Python allows underscores inside numbers, so you can write 3_50_000 in the Indian lakh style. This is why reports on incomes and house prices usually give the median: those data have a long tail of very large values.
Which average should you use?
| Situation | Best measure | Why |
|---|---|---|
| Roughly symmetric numbers (marks, heights) | Mean | Uses all the data; mean and median are close anyway |
| Skewed numbers or outliers (salaries, rainfall, prices) | Median | Not pulled by extreme values |
| Categories (city, favourite subject) | Mode | The only one that works for names |
| Ordinal data (ratings, grades) | Median | Order is meaningful, exact gaps are not |
When in doubt, report both the mean and the median. A big gap between them is itself useful information: it signals skewed data or outliers.
Weighted and trimmed means
A weighted mean gives some values more importance than others. Your semester grade point average is one: a 4-credit subject counts four times as much as a 1-credit subject.
weighted mean = Σ(w x x) / Σw (w = weight of each value)A trimmed mean removes a fixed share of the smallest and largest values and averages the rest. Some judged sports use this idea, dropping the highest and lowest judges' scores to reduce the effect of one unfair judge.
import numpy as np
from scipy import stats
# Meera's semester: grade points and credits per subject (invented)
grade_points = np.array([9, 8, 10, 7, 9])
credits = np.array([4, 4, 2, 3, 1])
by_hand = (grade_points * credits).sum() / credits.sum()
print("Weighted mean by hand:", round(by_hand, 2))
print("np.average(weights=): ", round(np.average(grade_points, weights=credits), 2))
print("Plain mean (wrong): ", grade_points.mean())
judges = np.array([8.5, 9.0, 8.8, 9.1, 4.0, 8.9, 9.9])
print("Judges, mean: ", round(judges.mean(), 2))
print("Judges, trimmed mean:", round(stats.trim_mean(judges, 0.15), 2))Weighted mean by hand: 8.43 np.average(weights=): 8.43 Plain mean (wrong): 8.6 Judges, mean: 8.31 Judges, trimmed mean: 8.86
By hand: 9x4 + 8x4 + 10x2 + 7x3 + 9x1 = 118 grade-point credits, divided by 14 credits, gives 8.43. The plain mean of 8.6 is wrong because it treats the 1-credit subject like the 4-credit ones. stats.trim_mean(judges, 0.15) cuts 15% of the values from each end; with 7 scores, 15% is 1.05, which is rounded down to one score from each end. That removes the unfair 4.0 and the 9.9, and the remaining five average 8.86.
Advanced: mean and median of grouped data
Sometimes you only have a frequency table with class intervals, not the raw values, as in many school textbook problems. You then estimate the centre:
grouped mean = Σ(f x m) / n m = midpoint of each class, f = its frequency
grouped median = L + ((n/2 - cf) / f) x h
L = lower limit of the median class (the class where the running total passes n/2)
cf = cumulative frequency before the median class
f = frequency of the median class, h = class width# Marks of 50 students in a class test, grouped into classes (invented)
classes = [(0, 10), (10, 20), (20, 30), (30, 40), (40, 50)]
freq = [4, 9, 16, 13, 8]
n = sum(freq)
mids = [(lo + hi) / 2 for lo, hi in classes]
mean = sum(m * f for m, f in zip(mids, freq)) / n
print("n =", n, " grouped mean =", mean)
cum = 0
for (lo, hi), f in zip(classes, freq):
if cum + f >= n / 2: # the median class
h = hi - lo
median = lo + (n / 2 - cum) / f * h
print(f"Median class {lo}-{hi}: {lo} + ({n / 2} - {cum}) / {f} x {h} = {median}")
break
cum += fn = 50 grouped mean = 27.4 Median class 20-30: 20 + (25.0 - 13) / 16 x 10 = 27.5
The midpoints are 5, 15, 25, 35 and 45, so the mean is (4x5 + 9x15 + 16x25 + 13x35 + 8x45) / 50 = 1370 / 50 = 27.4. For the median, n/2 = 25 and the running total is 4, 13, then 29, so the 25th student is in the 20-30 class. The formula assumes the 16 students in that class are spread evenly across it. Both answers are estimates, because the exact marks inside each class are unknown.
Common mistakes
- Forgetting to sort before taking the median. The middle of an unsorted list means nothing.
- Using the mean for skewed data. One huge value can make the "average" describe nobody.
- Averaging averages. The mean of two class averages is only the overall average if both classes have the same number of students. Otherwise use a weighted mean.
- Expecting NumPy to have a mode. Use
scipy.stats.mode(), pandasmode()orstatistics.multimode().
Key points
- Mean = Σx / n; median = middle of the sorted values; mode = most frequent value.
- The mean is pulled by outliers and long tails; the median resists them.
- Use the mode for categories and the median for ordinal or skewed data.
- A weighted mean, Σ(w x x) / Σw, is needed when values count unequally, such as credits in a CGPA.
stats.trim_mean()drops a share of values from each end before averaging.
Exercise
In cricket, a batting average is total runs divided by the number of times the batter was out, not by the number of innings. Kabir's last 8 innings were 45, 12*, 67, 0, 23*, 88, 31, 9, where * means not out. Compute his batting average this way, the plain mean runs per innings, and the median runs per innings.
Show solution
import numpy as np
runs = np.array([45, 12, 67, 0, 23, 88, 31, 9])
not_out = np.array([False, True, False, False, True, False, False, False])
dismissals = (~not_out).sum()
print("Times out:", dismissals)
print("Batting average:", round(runs.sum() / dismissals, 2))
print("Mean runs per innings:", runs.mean())
print("Median runs per innings:", np.median(runs))Times out: 6 Batting average: 45.83 Mean runs per innings: 34.375 Median runs per innings: 27.0
The total is 275 runs. Dividing by 6 dismissals gives 45.83, while dividing by 8 innings gives 34.375. ~not_out flips True and False, so summing it counts the innings in which he was out.
Quick Quiz
1. What is the median of 4, 9, 1, 7, 3?
Sorted: 1, 3, 4, 7, 9. The middle (third) value is 4.
2. A dataset of house prices has a mean of ₹85 lakh and a median of ₹60 lakh. What is the most likely reason?
A long right tail of expensive houses raises the mean but not the median.
3. Which measure of centre can you use for a column of favourite sports?
Favourite sport is nominal data, so only the mode makes sense.
4. Subjects with 4, 3 and 1 credits have grade points 8, 6 and 10. What is the weighted mean?
(4x8 + 3x6 + 1x10) / (4 + 3 + 1) = 60 / 8 = 7.5.
Learn Statistics with a mentor
Free lessons get you started. Our Data Science & Machine Learning training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
