Mean, median and skew

What is the typical posted pay for an analyst role, and which number should we put on the slide?

Lesson 14 min readFree, runs offline
On this page
  1. The problem
  2. What you will build from scratch
  3. The idea
  4. Walk through
  5. Your turn
  6. Check yourself
  7. In real job postings
  8. Next

Get the lesson files

The dataset, exercise.py, the solution and the test live in the open-source repo. You need only Python 3.9 or later.

git clone https://github.com/Ani19arya/analytics-from-scratch
cd analytics-from-scratch/phases/02-statistics-and-experiments/01-mean-median-and-skew
python3 test_lesson.py
Open this lesson on GitHub

The problem

Brightline Talent, a fictional recruiting firm, publishes a short pay report for its clients every quarter. Priya, the head of client services, needs the headline number for 300 analyst postings by Friday. Last quarter someone reported the average, and a client pointed out that most of their offers were well below it. Your job is to compute both the mean and the median by hand, explain why they differ, and recommend which one goes on the slide.

The dataset in data/postings.csv is synthetic. It is generated by make_data.py with a fixed seed, so everyone gets the same 300 rows. No row is a real job posting.

What you will build from scratch

  • mean(values): the sum divided by the count, with no statistics module.
  • median(values): the middle of the sorted list, handling odd and even counts.
  • skew_label(values): says whether the data is pulled to the right, the left, or neither, by comparing mean and median.
  • read_pay(path) and summarise(path): read the pay column and report n, mean, median, the gap and the skew.

The idea

The mean of n values x1, x2, ..., xn is

mean = (x1 + x2 + ... + xn) / n

Every value pulls on the mean in proportion to its size. One very large value can move it a long way.

The median is the value in the middle once you sort the list:

sorted list of n values, positions 0 to n - 1
n odd:  median = value at position n // 2
n even: median = (value at n // 2 - 1 + value at n // 2) / 2

The median only cares about order. If the top value doubles, the median does not move at all.

Pay data has a floor (nobody posts a negative salary) and a long tail on the high side (a few senior and director roles). That shape is called right skew. In right-skewed data the tail drags the mean above the median, so the mean describes a pay level that most postings do not reach. That is why pay ranges and salary surveys are usually summarised with the median.

A quick rule of thumb used in this lesson:

  • mean clearly above median: right skew (long high tail)
  • mean clearly below median: left skew (long low tail)
  • about equal: roughly symmetric

"Clearly" here means a gap bigger than 1% of the median. It is a simple screen, not a formal skewness statistic.

Walk through

Start with a tiny list and an outlier:

pay = [60000, 70000, 80000, 90000, 100000]
print(sum(pay) / len(pay))           # mean 80000.0
pay[-1] = 400000
print(sum(pay) / len(pay))           # mean 140000.0, pulled up by one value
print(sorted(pay)[len(pay) // 2])    # median still 80000

Now the even case for the median:

values = sorted([10, 1, 4, 3])       # [1, 3, 4, 10]
mid = len(values) // 2               # 2
print((values[mid - 1] + values[mid]) / 2)   # 3.5

Reading the column from the CSV:

import csv
with open("data/postings.csv", newline="") as f:
    pay = [float(row["pay_mid"]) for row in csv.DictReader(f)]
print(len(pay), min(pay), max(pay))   # 300 43000.0 315000.0

On this dataset the solution reports a mean of 89,280 and a median of 83,000. The gap is 6,280, and 195 of the 300 postings sit below the mean. Eight rows are director roles paying 230,000 or more. Drop them and the mean falls to about 84,264 while the median stays at 83,000. The median barely notices the tail; the mean is mostly measuring it.

Recommendation for Priya: lead with the median, and show the range or the share of postings above a threshold if clients want to see the tail.

Your turn

Open exercise.py and fill in the five functions. Do not import statistics; the point is to write the arithmetic yourself. Then run, from this folder:

python3 test_lesson.py

The tests compare your numbers with Python's statistics module on the same data. To see the reference solution pass, run CHECK_SOLUTION=1 python3 test_lesson.py.

Check yourself

  1. A hiring manager says the average salary for the team is 150,000 but most people earn less than that. How is that possible?
  2. When would you report the mean instead of the median?
  3. How do you compute the median of an even number of values?
Answers
  1. A few very high salaries pull the mean up. With right-skewed pay, most people can sit below the mean. The median would show the typical salary.
  2. When you need a total or a rate that adds up, for example revenue per user times users equals revenue, or when the data is roughly symmetric with no heavy tail.
  3. Sort the values and average the two in the middle.

In real job postings

In the Real Analyst Jobs live feed on 25 October 2026, 45% of the 2,848 live US analyst postings stated pay, so pay summaries like this one are built from a subset of roles, which is one more reason to say exactly which number you report. See the full breakdown in the US analyst job market report or browse roles at Real Analyst Jobs.

Next

02: Confidence interval by hand

Put the skill to work

Real Analyst Jobs lists analyst roles read daily from company career pages, with the skills each posting asks for.

Browse analyst jobs

All lessons in Analytics from Scratch

Raj