BitBootcampBitBootcamp ← all explainers 🔄 Review ⚙ Settings

DA-01-005-P · Primer — Mean, Median, and Mode: Measures of Center

The idea in one sentence

A measure of center answers "what's a typical value here?" — and the mean, median, and mode each answer it differently, so picking the wrong one can quietly lie to you.

Why it exists

Before you compare groups, spot trends, or headline a number, you have to summarize a whole column into one representative value. The three classic summaries exist because "typical" isn't one idea: the mean is the balance point (add everything up, share it equally), the median is the literal middle value (half above, half below), and the mode is the most common value. On a clean, symmetric column they roughly agree and it barely matters which you use. On a lopsided column — incomes, response times, prices, wait times — they split apart, and that split is itself a finding. Knowing which one to trust is the difference between "the typical customer spends $52" and a number no real customer resembles.

How it works

The mean is sensitive: every value tugs on it, so a single huge outlier drags it far from the crowd. The median just asks "who's standing in the middle of the line?", so extreme values can't move it — it only cares about order, not magnitude. The mode is the value that shows up most, which is the only one of the three that makes sense for categories (most common city, most common plan). The analytical habit is: compute mean and median together and compare them — if they're close, the data is roughly symmetric and either is fine; if the mean sits well above the median, there's a long right tail pulling it up (and below means a long left tail). Here we build a tiny income column with one wealthy household and let the numbers and the picture tell the story.

import pandas as pd
import matplotlib.pyplot as plt

# Household incomes in a small town ($ thousands); one wealthy household
income = pd.Series([38, 42, 45, 47, 50, 52, 55, 58, 61, 420])

mean, median = income.mean(), income.median()
print(mean, median)   # 86.8  vs  51.0  -> they DISAGREE

ax = income.plot(kind="hist", bins=12, edgecolor="white")
ax.axvline(mean,   color="crimson",  label=f"mean {mean:.0f}")
ax.axvline(median, color="seagreen", label=f"median {median:.0f}")
ax.legend()
plt.show()
# Chart shows a long RIGHT tail: the mean (crimson) is dragged toward the
# lone outlier, the median (green) stays at the true middle ~51.
# Nine of ten households earn LESS than the mean -> report the median here.

The one gotcha

The mean feels authoritative because it "uses all the data," but that's exactly why it breaks on skewed columns — the outliers it faithfully includes are the ones distorting it. If mean and median disagree, don't average them or pick your favorite; treat the gap as a signal that the data is skewed and report the median (plus a note about the tail). And never quote a mode for a continuous, all-unique column: .mode() there just returns every value, which means nothing.

See it — do the mean and median agree?

flowchart TD
    A["Column of numbers"] --> B["Compute mean<br/>and median"]
    B --> C{"Are they<br/>close?"}
    C -->|"Yes, roughly equal"| D["Symmetric data<br/>mean is fine"]
    C -->|"Mean much higher"| E["Long right tail<br/>report the median"]
    C -->|"Mean much lower"| F["Long left tail<br/>report the median"]
    D --> G["State typical value<br/>+ note the shape"]
    E --> G
    F --> G

The one-line takeaway

Compute the mean and median together — when they disagree, the data is skewed and the median is usually the honest answer.

Where you'll meet it

Any time someone says "average" — salaries, home prices, page-load times, order values, session lengths, delivery delays. These are almost all right-skewed, so the headline "average" quietly overstates the typical case, and knowing to switch to the median (and to reach for the mode on categorical columns) is a daily judgment call in analytics.

Named concepts: mean vs median, mode, skew and which center to trust