Z-Score Calculator
Turn any value into a z-score and read off its p-value, percentile and outlier status. Works from raw data or from a mean and standard deviation you already have, handles single values, whole datasets and sample means, and includes a robust modified z-score plus a full z-table.
⚡ 0. Quick Answer
A z-score tells you how many standard deviations a value sits above or below the mean.
Subtract the mean from your value, then divide by the standard deviation. A z of 0 means the value is exactly average. A z of +2 means it is two standard deviations above the mean, which puts it around the 98th percentile. Negative z-scores sit below the mean. Because the result has no units, z-scores let you compare measurements taken on completely different scales, such as a test score against a height.
Rule of thumb: about 68% of values fall within z = ±1, 95% within ±2 and 99.7% within ±3. A |z| above 3 is conventionally treated as an outlier.
Key takeaways
- z = (x − mean) / SD, the number of standard deviations a value lies from the mean.
- Z-scores are unit-free, so they let you compare values measured on completely different scales.
- The empirical rule: roughly 68% of data within ±1, 95% within ±2, 99.7% within ±3 standard deviations.
- Converting z to a p-value or percentile assumes the data are normally distributed. The z-score itself does not, but every probability read from it does.
- Using z-scores to find outliers is self-defeating on small samples, because the outlier inflates the very standard deviation used to detect it. Use the modified z-score instead.
📚 1. What Is a Z-Score?
A z-score, also called a standard score, rewrites a measurement in terms of standard deviations from the mean. It answers a question the raw number cannot: is this value ordinary or unusual, given how spread out the data are? A height of 190 cm means nothing on its own. Told that adult male height averages 175 cm with a standard deviation of 7 cm, that becomes a z of +2.14, and now you know it sits above roughly 98% of men.
What this calculator reports:
- Z-score, the standard score itself, for a single value or for every value in a dataset.
- P-value, one-tailed and two-tailed, the probability of a value this extreme or more.
- Percentile rank, the percentage of a normal distribution falling below your value.
- Modified z-score, built from the median and MAD, which outliers cannot distort.
- Outlier flags at |z| > 3 and modified |z| > 3.5, the Iglewicz and Hoaglin threshold.
- Reverse lookup, giving the raw value that corresponds to any z or percentile you name.
- Z for a sample mean, using the standard error rather than the standard deviation.
A worked one-liner: a student scores 85 on a test where the class averaged 70 with a standard deviation of 10. Their z-score is (85 − 70) ÷ 10 = 1.5, placing them at about the 93rd percentile.
Who uses them: educators convert raw test marks to standard scores so results from different exams can be compared. Clinicians use z-scores for growth charts, bone density (the T-score is a z-score against a young-adult reference) and lab results. Finance uses them in the Altman Z-score for bankruptcy risk. Quality control flags parts beyond ±3 standard deviations. Machine learning standardises features to z-scores so that variables on different scales contribute equally.
| Score type | Formula | Use it when |
|---|---|---|
| Z-score (population) | (x − μ) ÷ σ | You know the true population mean and SD |
| Z-score (sample) | (x − x̄) ÷ s | You are working from sample data, the usual case |
| Z for a sample mean | (x̄ − μ) ÷ (σ ÷ √n) | Testing whether a sample mean differs from a known value |
| Modified z-score | 0.6745(x − Mdn) ÷ MAD | Detecting outliers, especially in small samples |
🧮 2. Set Up Your Calculation
📁 Or upload a CSV / Excel file
The classic textbook case. Enter the value you are scoring plus the mean and standard deviation you already know.
Work backwards: given a z-score or a percentile, what raw value does it correspond to?
For testing whether a sample mean differs from a known population mean. This uses the standard error, not the standard deviation, so the z-score is larger for the same gap.
📊 3. Results
🧠 4. Interpretation of Results, In Detail
4.1 Reading the z-score itself
The sign tells you the direction and the magnitude tells you the distance. A z of +1.5 means the value sits one and a half standard deviations above the mean; a z of −0.4 means it sits slightly below. Zero means exactly average. That is the entire interpretation, and it holds regardless of what you measured or what units you used.
The magnitude is what makes z-scores useful. Because the standard deviation sets the scale, a z of 2 means the same thing whether you are measuring reaction times in milliseconds or annual rainfall in millimetres: the value is unusually high for that particular distribution. This is what allows a student to compare their performance on a maths exam against their performance on an essay, even though the two marking schemes share nothing.
4.2 What the percentile actually claims
The percentile converts your z-score into a statement about position: the percentage of the distribution falling below your value. A z of 1.5 corresponds to roughly the 93rd percentile, meaning about 93% of a normal distribution lies below it.
That word normal is doing a great deal of work. The z-score itself is a pure arithmetic operation that requires no assumptions at all. The moment you convert it to a percentile or a p-value, you are assuming the data follow a normal distribution, because you are reading off the normal curve. If your data are skewed, the z-score remains a correct statement about standard deviations, but the percentile attached to it can be badly wrong. For income data, a z of 2 might correspond to the 95th percentile in reality rather than the 97.7th the normal curve predicts.
4.3 The p-value, and choosing a tail
The p-value is the probability of observing a value at least this extreme if the data really are normal with the mean and standard deviation you supplied. A two-tailed p-value counts both extremes: it answers "how likely is a value this far from the mean in either direction". A one-tailed p-value counts only one side.
Choose the tail before you see the data, not after. If your question is genuinely directional, such as "is this batch heavier than specification", a right-tailed test is appropriate. If you simply want to know whether the value is unusual, use two-tailed. Switching to a one-tailed test after seeing which direction the result went halves your p-value without any statistical justification, and it is one of the most common forms of unintentional p-hacking.
4.4 Why z-scores fail at outlier detection on small samples
This is the most important limitation on the page, and most calculators ignore it entirely. The standard advice is to flag any value with |z| > 3. The problem is circular: an extreme value inflates the standard deviation used to compute its own z-score, so it disguises itself.
The mathematics is unforgiving. With n observations, the largest possible z-score is (n − 1) ÷ √n. For n = 10 that ceiling is 2.85, so no value in a sample of ten can ever reach z = 3, however extreme it is. With n = 5 the maximum is 1.79. You could have a value a thousand times larger than the rest and the z-score rule would never flag it. This is why the calculator reports the theoretical maximum alongside your results whenever the sample is small.
4.5 The modified z-score, and when to use it
The fix is to replace the mean with the median and the standard deviation with the median absolute deviation, neither of which an outlier can distort. The modified z-score is 0.6745 times the deviation from the median, divided by the MAD. The constant 0.6745 is the 75th percentile of the standard normal, which rescales the MAD so the modified z is comparable to an ordinary z for normal data.
Iglewicz and Hoaglin recommend flagging values with a modified |z| above 3.5. Because it is built from robust components, it has no artificial ceiling and works on samples where the ordinary z-score is useless. When the two disagree, trust the modified version: a value flagged by the modified z but not by the ordinary z is almost always a genuine outlier that inflated the standard deviation enough to hide itself.
4.6 Sample versus population, and why it matters less than you think
Textbooks write the z-score with the Greek letters μ and σ, implying you know the true population values. In practice you almost never do, so you substitute the sample mean and sample standard deviation. Strictly this produces a slightly different quantity, and with small samples the resulting score follows a t-distribution rather than a normal one.
For most practical purposes the distinction matters less than the normality assumption does. If n is above about 30, the difference between using s and σ is small. If n is below 30 and you are computing a p-value for a sample mean, you should be using a t-test rather than a z-test. The calculator lets you choose the denominator so you can match whatever convention your course or software uses.
4.7 The difference between z for a value and z for a sample mean
These are two different calculations and confusing them is a common error. When you score an individual observation you divide by the standard deviation, because you are asking how unusual that one value is among individuals. When you score a sample mean you divide by the standard error, σ divided by the square root of n, because sample means vary far less than individual values do.
The practical consequence is that the same gap produces a much larger z-score for a mean. A sample mean two units above the population mean might give z = 0.2 as an individual value but z = 1.0 as a mean of 25 observations. Using the standard deviation when you should be using the standard error will make a real effect look unremarkable.
4.8 Reading the four charts
Chart 1 draws the standard normal curve with your z marked and the p-value region shaded, which is the figure most people picture when they think of a z-score. Chart 2 plots every observation as a z-score with reference lines at ±2 and ±3, so outliers are immediately visible. Chart 3 shows the raw distribution so you can judge whether the normality assumption behind the percentile is reasonable. Chart 4 is the cumulative normal curve, which is where the percentile is read from.
Chart 3 deserves particular attention. If the histogram is visibly skewed or has two peaks, the percentile and p-value from charts 1 and 4 are unreliable even though the z-score itself is still arithmetically correct.
4.9 What a z-score cannot tell you
It cannot tell you whether a value is wrong, only whether it is unusual. An extreme z-score might indicate a data-entry error, a faulty instrument, or the single most scientifically interesting observation in your dataset. The statistic cannot distinguish between them and neither should you without going back to the raw record.
It also carries no information about sample size, no measure of its own uncertainty, and no protection against a biased sample. And because it is computed from the mean and standard deviation, it inherits every weakness of those two statistics, including their sensitivity to skew.
4.10 Practical judgement
Compute the z-score, then ask two questions before you use the percentile. First, are the data plausibly normal? Check chart 3, and if the answer is no, report the z-score as a distance in standard deviations but not as a percentile. Second, is the sample large enough for the outlier rule to work? If n is under about 30, use the modified z-score instead. Report the mean, standard deviation and n alongside any z-score, because the same z means completely different things depending on the distribution it came from.
✍ 5. How to Write Your Results in Research
▶ Run the analysis above to auto-fill all five examples with your results.
📌 Key conventions for this style
- Italicise z and p. Report p to three decimals, or as p < .001.
- State whether the p-value is one or two tailed.
- Give the mean, SD and n that the z-score was computed from.
- Do not write p = .000; write p < .001.
📌 Key conventions for this style
- State the threshold used and cite a source for it.
- For small samples, use and report the modified z-score instead, citing Iglewicz and Hoaglin (1993).
- Say how many values were flagged and whether they were removed or retained.
- Never remove outliers silently; reviewers will ask.
📌 Key conventions for this style
- Never write z, sigma or p. Say "standard deviations above average" at most.
- Percentiles are far more intuitive to a general reader than z-scores.
- Give units and context for the raw value.
- Avoid implying a percentile is exact when the data may not be normal.
📌 Key conventions for this style
- State whether you standardised using sample or population statistics.
- In machine learning, fit the scaler on training data only, never on the full dataset.
- Report the original means and SDs so readers can back-transform.
- Note that standardising does not make skewed data normal.
📌 Key conventions for this style
- A z-test requires a known population standard deviation. If you estimated it, use a t-test instead.
- Report n, the population mean tested against, and the standard error.
- State the tail and the alpha level chosen in advance.
- Give an effect size or confidence interval, not just the p-value.
∑ 6. Formulas Used
📝 7. How to Use This Calculator
- Paste your numbers into the data column. Raw data mode opens first because it is what most people need. Enter your values comma-separated, exactly as the placeholder shows. The calculator works out the mean and standard deviation for you, then scores every observation. Leave the highlight box blank to standardise the whole dataset, or type a value to score just that one.
- Or switch mode if you already have the summary statistics. I know the mean and SD is the classic textbook case where you enter the value, the mean and the standard deviation directly. Reverse works backwards from a z-score or percentile to a raw value. Z for a sample mean uses the standard error instead of the standard deviation. In every mode the standard deviation must be greater than zero, because dividing by zero leaves the z-score undefined.
- Load a sample dataset to see how it behaves. Eight are built in, including a heart-rate set containing a genuine outlier and a ten-value set that demonstrates the small-sample ceiling. Selecting one replaces the data column immediately.
- Add clusters to compare groups. Each gets its own mean, standard deviation and set of z-scores. Every column has its own Clear and Remove button, and Remove is disabled on the last remaining column.
- Or upload a CSV. Open the upload panel inside the raw data tab and click the column names you want. Every column you click becomes its own cluster.
- Choose your standard deviation type. Sample (n − 1) is correct unless you genuinely measured the whole population. This is the setting that makes Excel's
STDEV.SandSTDEV.Pdisagree. - Choose the p-value tail before you look at the result. Two-tailed asks whether the value is unusual in either direction; one-tailed commits to a direction. Deciding after seeing the data halves your p-value without justification.
- Press Calculate Z-Score. Nothing is computed until you do, and changing any input clears the results so you never read stale numbers.
- Read the colour bar and the warnings first. The bar shows instantly where your z sits against the empirical rule bands, and the warnings will tell you if your sample is too small for the |z| > 3 rule to be capable of firing at all.
- Check chart 3 before trusting the percentile. The z-score is always arithmetically correct, but the percentile and p-value assume normality. If the histogram is visibly skewed, report the z-score as a distance and drop the percentile.
📊 8. How to Calculate a Z-Score in Excel
Excel can do every part of this: the mean, the standard deviation, the z-score itself, the percentile, the p-value, and the reverse lookup. There are two ways to write the z-score and three functions you need for the normal curve. Below is the whole workflow in ten steps, each with a picture of what your sheet should look like.
AVERAGE, STDEV.S or STDEV.P, STANDARDIZE, NORM.S.DIST and NORM.S.INV. Everything else on this page is built from those five.Step 1. Put your data in one column with a header in A1, and set aside a small block for the summary statistics.
Step 2. AVERAGE gives the mean, 53.1667. Select the whole data range, not just the rows you can see.
Step 3. STDEV.S uses n minus 1 and is the right choice for a sample. STDEV.P uses n and gives the smaller 5.4288. Pick one and stay with it.
Step 4. The manual formula. The dollar signs lock the mean and SD cells so you can drag the formula down the whole column without the references sliding.
Step 5. STANDARDIZE(x, mean, sd) does exactly the same thing and is easier to read. Both give -0.9112 for the value 48.
Step 6. NORM.S.DIST with TRUE returns the cumulative area, 0.9164, so the value sits at about the 92nd percentile. The FALSE argument returns the curve height instead, which is not what you want.
Step 7. The two-tailed p-value is 0.1671. The right-tail version drops the 2 and gives 0.0836. Decide which you need before you look at the number.
Step 8. Working backwards. NORM.S.INV turns a percentile into a z-score, then mean plus z times SD recovers the raw value, 62.4938.
Step 9. A flag column. Note that with n = 12 the largest possible z-score is only 3.18, so this rule can barely fire at all. Below n = 11 it can never fire.
Step 10. The robust alternative. Median with =MEDIAN(A2:A13), MAD with =MEDIAN(ABS(A2:A13-MEDIAN(A2:A13))) entered as an array formula, then flag anything above 3.5 in absolute value.
The formula cheat sheet
| What you want | Excel formula | Notes |
|---|---|---|
| Mean | =AVERAGE(A2:A13) | Ignores blanks and text |
| Sample SD | =STDEV.S(A2:A13) | Divides by n minus 1. The usual choice |
| Population SD | =STDEV.P(A2:A13) | Divides by n. Only if you measured everyone |
| Z-score, manual | =(A2-$E$2)/$E$3 | Dollar signs lock the mean and SD cells |
| Z-score, built in | =STANDARDIZE(A2,$E$2,$E$3) | Identical result, clearer to read |
| Percentile from z | =NORM.S.DIST(B2,TRUE) | TRUE gives cumulative area. Multiply by 100 for a percentage |
| Two-tailed p | =2*(1-NORM.S.DIST(ABS(B2),TRUE)) | ABS handles negative z-scores |
| Right-tail p | =1-NORM.S.DIST(B2,TRUE) | Only if the direction was decided in advance |
| Left-tail p | =NORM.S.DIST(B2,TRUE) | Same as the percentile |
| Z from percentile | =NORM.S.INV(0.95) | Returns 1.6449 |
| Value from z | =$E$2+B2*$E$3 | Rearranged z formula |
| Z for a sample mean | =(xbar-mu)/(sd/SQRT(n)) | Standard error in the denominator, not SD |
| Median | =MEDIAN(A2:A13) | For the modified z-score |
| MAD | =MEDIAN(ABS(A2:A13-MEDIAN(A2:A13))) | Array formula. Ctrl+Shift+Enter in older Excel |
| Outlier flag | =IF(ABS(B2)>3,"Outlier","OK") | Use 3.5 with the modified z-score |
Five mistakes that catch people out
- Forgetting the dollar signs. Drag
=(A2-E2)/E3down and row 3 will divide by the wrong cells. Write$E$2and$E$3. - Using NORM.DIST instead of NORM.S.DIST. The S version is the standard normal and takes a z-score. The plain version needs the mean and SD as extra arguments.
- Passing FALSE to NORM.S.DIST. That returns the height of the curve, not the area, and the number will look plausible but be meaningless as a percentile.
- Mixing STDEV.S and STDEV.P across a workbook. The z-scores will not be comparable. Decide once.
- Applying the |z| > 3 rule to a short column. With 12 rows the largest attainable z is 3.18. With 8 rows it is 2.47. The rule silently never fires.
NORMSDIST and NORMSINV names. The MAD array formula needs ARRAYFORMULA(...) wrapped around it.📈 9. How to Calculate a Z-Score in R
Base R does all of this with no packages at all. scale() standardises a vector, pnorm() converts a z-score to a cumulative probability, and qnorm() goes the other way. The script below computes every quantity on this page and draws one figure. Copy it whole and it will run.
sd() always uses n minus 1. There is no population option, so if you need the population SD you must write sd(x) * sqrt((n - 1) / n) yourself. NumPy has the opposite default, which is why R and Python can disagree on the same data.# Z-Score Calculator in R (base R, no packages)
# ---- 1. Your data -----------------------------------------------------
x <- c(52, 48, 55, 61, 47, 58, 50, 63, 45, 56, 54, 49)
highlight <- 61 # the value you want to score
# ---- 2. Mean and standard deviation -----------------------------------
n <- length(x)
mean_x <- mean(x)
sd_x <- sd(x) # sample SD, divides by n - 1
# population SD if you need it:
# sd_pop <- sd(x) * sqrt((n - 1) / n)
z <- (x - mean_x) / sd_x # or simply: z <- as.vector(scale(x))
# ---- 3. Percentile and p-value ----------------------------------------
z_hi <- (highlight - mean_x) / sd_x
pct <- pnorm(z_hi) * 100
p_two <- 2 * (1 - pnorm(abs(z_hi)))
p_rt <- 1 - pnorm(z_hi) # right tail only
# ---- 4. Robust modified z-score (Iglewicz & Hoaglin 1993) -------------
med <- median(x)
mad_x <- median(abs(x - med)) # raw MAD, not R's mad() which rescales
mod_z <- if (mad_x > 0) 0.6745 * (x - med) / mad_x else rep(0, n)
z_ceiling <- (n - 1) / sqrt(n) # largest |z| any value can reach
cat(sprintf("n = %d mean = %.4f SD = %.4f\n", n, mean_x, sd_x))
cat(sprintf("z for %g = %.4f\n", highlight, z_hi))
cat(sprintf("percentile = %.2f%% two-tailed p = %.4f\n", pct, p_two))
cat(sprintf("max possible |z| at n=%d is %.2f\n", n, z_ceiling))
cat("outliers |z|>3 :", x[abs(z) > 3], "\n")
cat("outliers |mod z|>3.5 :", x[abs(mod_z) > 3.5], "\n")
# ---- 5. One figure ----------------------------------------------------
grid <- seq(-4, 4, length.out = 600)
plot(grid, dnorm(grid), type = "l", lwd = 2, col = "#0f766e",
xlab = "z-score (standard deviations from the mean)",
ylab = "density", bty = "n",
main = sprintf("Z-Score Summary n=%d, mean=%.2f, SD=%.2f",
n, mean_x, sd_x))
# shade the two tails beyond |z_hi|
tail_r <- grid[grid >= abs(z_hi)]
tail_l <- grid[grid <= -abs(z_hi)]
polygon(c(tail_r, rev(tail_r)), c(dnorm(tail_r), rep(0, length(tail_r))),
col = adjustcolor("#f59e0b", 0.45), border = NA)
polygon(c(tail_l, rev(tail_l)), c(dnorm(tail_l), rep(0, length(tail_l))),
col = adjustcolor("#f59e0b", 0.45), border = NA)
abline(v = z_hi, col = "#7c3aed", lwd = 2.2)
abline(v = c(-3, -2, 2, 3), col = "#94a3b8", lty = 3)
points(z, rep(-0.012, n), pch = 19, col = "#0ea5e9", xpd = NA)
legend("topright", bty = "n", cex = 0.85,
legend = c(sprintf("z = %.3f (%.1fth pct)", z_hi, pct),
sprintf("two-tailed p = %.4f", p_two),
"your data as z-scores"),
col = c("#7c3aed", "#f59e0b", "#0ea5e9"),
lwd = c(2.2, 6, NA), pch = c(NA, NA, 19))
What the script prints
| Line | Output | Meaning |
|---|---|---|
| 1 | n = 12 mean = 53.1667 SD = 5.6702 | The summary statistics the z-scores are built from. Always report these |
| 2 | z for 61 = 1.3815 | The value 61 sits 1.38 sample standard deviations above the mean |
| 3 | percentile = 91.64% two-tailed p = 0.1671 | About 92% of a normal distribution lies below it, and a value this far from the mean in either direction is not unusual |
| 4 | max possible |z| at n=12 is 3.18 | The ceiling. With only twelve observations the |z| > 3 rule barely has room to fire |
| 5 and 6 | Two empty outlier lists | Neither the ordinary nor the modified rule flags anything, which agrees with the figure |
Line-by-line explanation
- Block 1 holds the only two things you change: your vector and the value you want to score. Everything below is generic.
- Block 2 computes the mean and the sample standard deviation. The commented line shows the population conversion, because R will not do it for you.
scale(x)is the idiomatic shortcut but returns a matrix, soas.vector()is needed if you want a plain numeric vector back. - Block 3 converts the highlighted z to a percentile with
pnorm()and to p-values. Note thatpnorm()is the direct equivalent of Excel'sNORM.S.DIST(z, TRUE), andqnorm()isNORM.S.INV. - Block 4 builds the robust version. Be careful here: R's built-in
mad()already multiplies by 1.4826, so feeding it into the 0.6745 formula would double-scale the result. The script computes the raw median absolute deviation directly to avoid that. - Block 5 draws a single figure containing everything: the standard normal curve, the shaded p-value region, a vertical line at your z, dotted guides at ±2 and ±3, and every observation plotted along the bottom as a z-score.
xpd = NAlets those points sit below the axis without being clipped.
Useful one-liners
| Task | R |
|---|---|
| Standardise a vector | as.vector(scale(x)) |
| Standardise every numeric column of a data frame | as.data.frame(scale(df)) |
| Percentile from z | pnorm(z) |
| Z from percentile | qnorm(0.95) gives 1.644854 |
| Two-tailed p | 2 * pnorm(-abs(z)) |
| Raw value from z | mean_x + z * sd_x |
| Test normality first | shapiro.test(x) |
| Q-Q plot | qqnorm(x); qqline(x) |
🐍 10. How to Calculate a Z-Score in Python
NumPy computes the z-scores, SciPy supplies the normal distribution, and Matplotlib draws the figure. The script below was run before being published, so the printed output shown underneath is real, not typed by hand.
np.std() defaults to ddof=0, the population version. R's sd() defaults to n minus 1. If your R and Python answers differ slightly, this is almost always why. Write ddof=1 explicitly whenever your data are a sample. Note that scipy.stats.zscore() also defaults to ddof=0.# Z-Score Calculator in Python
# Computes z-scores, percentiles and p-values, and draws one summary figure.
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# ---- 1. Your data -----------------------------------------------------
x = np.array([52, 48, 55, 61, 47, 58, 50, 63, 45, 56, 54, 49], dtype=float)
highlight = 61.0 # the value you want to score
# ---- 2. Mean and standard deviation -----------------------------------
n = x.size
mean = x.mean()
sd = x.std(ddof=1) # ddof=1 = sample SD. Use ddof=0 for population
z = (x - mean) / sd
# ---- 3. Percentile and p-value for the highlighted value --------------
z_hi = (highlight - mean) / sd
pct = stats.norm.cdf(z_hi) * 100
p_two = 2 * (1 - stats.norm.cdf(abs(z_hi)))
# ---- 4. Robust modified z-score (Iglewicz and Hoaglin 1993) -----------
med = np.median(x)
mad = np.median(np.abs(x - med))
mod_z = 0.6745 * (x - med) / mad if mad > 0 else np.zeros_like(x)
z_ceiling = (n - 1) / np.sqrt(n) # largest |z| any value can reach
print(f"n = {n} mean = {mean:.4f} SD = {sd:.4f}")
print(f"z for {highlight:g} = {z_hi:.4f}")
print(f"percentile = {pct:.2f}% two-tailed p = {p_two:.4f}")
print(f"max possible |z| at n={n} is {z_ceiling:.2f}")
print("outliers |z|>3 :", x[np.abs(z) > 3].tolist())
print("outliers |mod z|>3.5:", x[np.abs(mod_z) > 3.5].tolist())
# ---- 5. One figure ----------------------------------------------------
fig, ax = plt.subplots(figsize=(9, 5))
grid = np.linspace(-4, 4, 600)
ax.plot(grid, stats.norm.pdf(grid), color="#0f766e", lw=2)
ax.fill_between(grid, stats.norm.pdf(grid), where=np.abs(grid) >= abs(z_hi),
color="#f59e0b", alpha=.45,
label=f"two-tailed p = {p_two:.4f}")
ax.axvline(z_hi, color="#7c3aed", lw=2.2,
label=f"z = {z_hi:.3f} ({pct:.1f}th pct)")
ax.scatter(z, np.full_like(z, -0.012), s=46, color="#0ea5e9",
zorder=3, clip_on=False, label="your data as z-scores")
for b in (-3, -2, 2, 3):
ax.axvline(b, color="#94a3b8", ls=":", lw=1)
ax.set_xlabel("z-score (standard deviations from the mean)")
ax.set_ylabel("density")
ax.set_title(f"Z-Score Summary n={n}, mean={mean:.2f}, SD={sd:.2f}")
ax.legend(frameon=False, fontsize=9)
ax.spines[["top", "right"]].set_visible(False)
fig.tight_layout()
fig.savefig("z_score_summary.png", dpi=150)
plt.show()
Actual output from running the script
n = 12 mean = 53.1667 SD = 5.6702
z for 61 = 1.3815
percentile = 91.64% two-tailed p = 0.1671
max possible |z| at n=12 is 3.18
outliers |z|>3 : []
outliers |mod z|>3.5: []
These are the same numbers the R script produces and the same numbers the calculator at the top of this page produces, which is a useful sanity check whenever you port an analysis between tools.
Line-by-line explanation
- Block 1 is the only part you edit. Setting
dtype=floatavoids integer division surprises if you later divide the array. - Block 2 computes the mean and the sample standard deviation, then vectorises the z-score across the whole array in one line. NumPy broadcasting means no loop is needed.
- Block 3 uses
stats.norm.cdf, the direct equivalent of R'spnorm()and Excel'sNORM.S.DIST(z, TRUE). For the reverse direction usestats.norm.ppf(0.95), which returns 1.6449. - Block 4 builds the robust modified z-score. The guard on
mad > 0matters: if more than half your values are identical the MAD is zero and the formula would divide by zero. - Block 5 draws the single summary figure.
fill_betweenwith awheremask shades both tails in one call,clip_on=Falselets the data points sit just below the axis, and hiding the top and right spines keeps the plot publication-ready.
Useful one-liners
| Task | Python |
|---|---|
| Z-scores, sample SD | stats.zscore(x, ddof=1) |
| Z-scores, population SD | stats.zscore(x) |
| Standardise a DataFrame | (df - df.mean()) / df.std() (pandas uses ddof=1) |
| Scikit-learn scaler | StandardScaler().fit_transform(X) (uses ddof=0) |
| Percentile from z | stats.norm.cdf(z) |
| Z from percentile | stats.norm.ppf(0.95) |
| Two-tailed p | 2 * stats.norm.sf(abs(z)) |
| Raw value from z | mean + z * sd |
| Test normality first | stats.shapiro(x) |
| Q-Q plot | stats.probplot(x, plot=plt) |
📋 11. Reference Tables
11.1 Standard normal table, negative z-scores
Find your z-score to one decimal place down the left column, then read across to the second decimal place. The number in the cell is the area to the left of that z, which is the percentile expressed as a proportion.
| z | 0.00 | 0.01 | 0.02 | 0.03 | 0.04 | 0.05 | 0.06 | 0.07 | 0.08 | 0.09 |
|---|---|---|---|---|---|---|---|---|---|---|
| -0.0 | 0.5000 | 0.4960 | 0.4920 | 0.4880 | 0.4840 | 0.4801 | 0.4761 | 0.4721 | 0.4681 | 0.4641 |
| -0.1 | 0.4602 | 0.4562 | 0.4522 | 0.4483 | 0.4443 | 0.4404 | 0.4364 | 0.4325 | 0.4286 | 0.4247 |
| -0.2 | 0.4207 | 0.4168 | 0.4129 | 0.4090 | 0.4052 | 0.4013 | 0.3974 | 0.3936 | 0.3897 | 0.3859 |
| -0.3 | 0.3821 | 0.3783 | 0.3745 | 0.3707 | 0.3669 | 0.3632 | 0.3594 | 0.3557 | 0.3520 | 0.3483 |
| -0.4 | 0.3446 | 0.3409 | 0.3372 | 0.3336 | 0.3300 | 0.3264 | 0.3228 | 0.3192 | 0.3156 | 0.3121 |
| -0.5 | 0.3085 | 0.3050 | 0.3015 | 0.2981 | 0.2946 | 0.2912 | 0.2877 | 0.2843 | 0.2810 | 0.2776 |
| -0.6 | 0.2743 | 0.2709 | 0.2676 | 0.2643 | 0.2611 | 0.2578 | 0.2546 | 0.2514 | 0.2483 | 0.2451 |
| -0.7 | 0.2420 | 0.2389 | 0.2358 | 0.2327 | 0.2296 | 0.2266 | 0.2236 | 0.2206 | 0.2177 | 0.2148 |
| -0.8 | 0.2119 | 0.2090 | 0.2061 | 0.2033 | 0.2005 | 0.1977 | 0.1949 | 0.1922 | 0.1894 | 0.1867 |
| -0.9 | 0.1841 | 0.1814 | 0.1788 | 0.1762 | 0.1736 | 0.1711 | 0.1685 | 0.1660 | 0.1635 | 0.1611 |
| -1.0 | 0.1587 | 0.1562 | 0.1539 | 0.1515 | 0.1492 | 0.1469 | 0.1446 | 0.1423 | 0.1401 | 0.1379 |
| -1.1 | 0.1357 | 0.1335 | 0.1314 | 0.1292 | 0.1271 | 0.1251 | 0.1230 | 0.1210 | 0.1190 | 0.1170 |
| -1.2 | 0.1151 | 0.1131 | 0.1112 | 0.1093 | 0.1075 | 0.1056 | 0.1038 | 0.1020 | 0.1003 | 0.0985 |
| -1.3 | 0.0968 | 0.0951 | 0.0934 | 0.0918 | 0.0901 | 0.0885 | 0.0869 | 0.0853 | 0.0838 | 0.0823 |
| -1.4 | 0.0808 | 0.0793 | 0.0778 | 0.0764 | 0.0749 | 0.0735 | 0.0721 | 0.0708 | 0.0694 | 0.0681 |
| -1.5 | 0.0668 | 0.0655 | 0.0643 | 0.0630 | 0.0618 | 0.0606 | 0.0594 | 0.0582 | 0.0571 | 0.0559 |
| -1.6 | 0.0548 | 0.0537 | 0.0526 | 0.0516 | 0.0505 | 0.0495 | 0.0485 | 0.0475 | 0.0465 | 0.0455 |
| -1.7 | 0.0446 | 0.0436 | 0.0427 | 0.0418 | 0.0409 | 0.0401 | 0.0392 | 0.0384 | 0.0375 | 0.0367 |
| -1.8 | 0.0359 | 0.0351 | 0.0344 | 0.0336 | 0.0329 | 0.0322 | 0.0314 | 0.0307 | 0.0301 | 0.0294 |
| -1.9 | 0.0287 | 0.0281 | 0.0274 | 0.0268 | 0.0262 | 0.0256 | 0.0250 | 0.0244 | 0.0239 | 0.0233 |
| -2.0 | 0.0228 | 0.0222 | 0.0217 | 0.0212 | 0.0207 | 0.0202 | 0.0197 | 0.0192 | 0.0188 | 0.0183 |
| -2.1 | 0.0179 | 0.0174 | 0.0170 | 0.0166 | 0.0162 | 0.0158 | 0.0154 | 0.0150 | 0.0146 | 0.0143 |
| -2.2 | 0.0139 | 0.0136 | 0.0132 | 0.0129 | 0.0125 | 0.0122 | 0.0119 | 0.0116 | 0.0113 | 0.0110 |
| -2.3 | 0.0107 | 0.0104 | 0.0102 | 0.0099 | 0.0096 | 0.0094 | 0.0091 | 0.0089 | 0.0087 | 0.0084 |
| -2.4 | 0.0082 | 0.0080 | 0.0078 | 0.0075 | 0.0073 | 0.0071 | 0.0069 | 0.0068 | 0.0066 | 0.0064 |
| -2.5 | 0.0062 | 0.0060 | 0.0059 | 0.0057 | 0.0055 | 0.0054 | 0.0052 | 0.0051 | 0.0049 | 0.0048 |
| -2.6 | 0.0047 | 0.0045 | 0.0044 | 0.0043 | 0.0041 | 0.0040 | 0.0039 | 0.0038 | 0.0037 | 0.0036 |
| -2.7 | 0.0035 | 0.0034 | 0.0033 | 0.0032 | 0.0031 | 0.0030 | 0.0029 | 0.0028 | 0.0027 | 0.0026 |
| -2.8 | 0.0026 | 0.0025 | 0.0024 | 0.0023 | 0.0023 | 0.0022 | 0.0021 | 0.0021 | 0.0020 | 0.0019 |
| -2.9 | 0.0019 | 0.0018 | 0.0018 | 0.0017 | 0.0016 | 0.0016 | 0.0015 | 0.0015 | 0.0014 | 0.0014 |
| -3.0 | 0.0013 | 0.0013 | 0.0013 | 0.0012 | 0.0012 | 0.0011 | 0.0011 | 0.0011 | 0.0010 | 0.0010 |
| -3.1 | 0.0010 | 0.0009 | 0.0009 | 0.0009 | 0.0008 | 0.0008 | 0.0008 | 0.0008 | 0.0007 | 0.0007 |
| -3.2 | 0.0007 | 0.0007 | 0.0006 | 0.0006 | 0.0006 | 0.0006 | 0.0006 | 0.0005 | 0.0005 | 0.0005 |
| -3.3 | 0.0005 | 0.0005 | 0.0005 | 0.0004 | 0.0004 | 0.0004 | 0.0004 | 0.0004 | 0.0004 | 0.0003 |
| -3.4 | 0.0003 | 0.0003 | 0.0003 | 0.0003 | 0.0003 | 0.0003 | 0.0003 | 0.0003 | 0.0003 | 0.0002 |
11.2 Standard normal table, positive z-scores
Same reading method. For example z = 1.38 gives 0.9162, so about 91.6% of the distribution lies below that value.
| z | 0.00 | 0.01 | 0.02 | 0.03 | 0.04 | 0.05 | 0.06 | 0.07 | 0.08 | 0.09 |
|---|---|---|---|---|---|---|---|---|---|---|
| 0.0 | 0.5000 | 0.5040 | 0.5080 | 0.5120 | 0.5160 | 0.5199 | 0.5239 | 0.5279 | 0.5319 | 0.5359 |
| 0.1 | 0.5398 | 0.5438 | 0.5478 | 0.5517 | 0.5557 | 0.5596 | 0.5636 | 0.5675 | 0.5714 | 0.5753 |
| 0.2 | 0.5793 | 0.5832 | 0.5871 | 0.5910 | 0.5948 | 0.5987 | 0.6026 | 0.6064 | 0.6103 | 0.6141 |
| 0.3 | 0.6179 | 0.6217 | 0.6255 | 0.6293 | 0.6331 | 0.6368 | 0.6406 | 0.6443 | 0.6480 | 0.6517 |
| 0.4 | 0.6554 | 0.6591 | 0.6628 | 0.6664 | 0.6700 | 0.6736 | 0.6772 | 0.6808 | 0.6844 | 0.6879 |
| 0.5 | 0.6915 | 0.6950 | 0.6985 | 0.7019 | 0.7054 | 0.7088 | 0.7123 | 0.7157 | 0.7190 | 0.7224 |
| 0.6 | 0.7257 | 0.7291 | 0.7324 | 0.7357 | 0.7389 | 0.7422 | 0.7454 | 0.7486 | 0.7517 | 0.7549 |
| 0.7 | 0.7580 | 0.7611 | 0.7642 | 0.7673 | 0.7704 | 0.7734 | 0.7764 | 0.7794 | 0.7823 | 0.7852 |
| 0.8 | 0.7881 | 0.7910 | 0.7939 | 0.7967 | 0.7995 | 0.8023 | 0.8051 | 0.8078 | 0.8106 | 0.8133 |
| 0.9 | 0.8159 | 0.8186 | 0.8212 | 0.8238 | 0.8264 | 0.8289 | 0.8315 | 0.8340 | 0.8365 | 0.8389 |
| 1.0 | 0.8413 | 0.8438 | 0.8461 | 0.8485 | 0.8508 | 0.8531 | 0.8554 | 0.8577 | 0.8599 | 0.8621 |
| 1.1 | 0.8643 | 0.8665 | 0.8686 | 0.8708 | 0.8729 | 0.8749 | 0.8770 | 0.8790 | 0.8810 | 0.8830 |
| 1.2 | 0.8849 | 0.8869 | 0.8888 | 0.8907 | 0.8925 | 0.8944 | 0.8962 | 0.8980 | 0.8997 | 0.9015 |
| 1.3 | 0.9032 | 0.9049 | 0.9066 | 0.9082 | 0.9099 | 0.9115 | 0.9131 | 0.9147 | 0.9162 | 0.9177 |
| 1.4 | 0.9192 | 0.9207 | 0.9222 | 0.9236 | 0.9251 | 0.9265 | 0.9279 | 0.9292 | 0.9306 | 0.9319 |
| 1.5 | 0.9332 | 0.9345 | 0.9357 | 0.9370 | 0.9382 | 0.9394 | 0.9406 | 0.9418 | 0.9429 | 0.9441 |
| 1.6 | 0.9452 | 0.9463 | 0.9474 | 0.9484 | 0.9495 | 0.9505 | 0.9515 | 0.9525 | 0.9535 | 0.9545 |
| 1.7 | 0.9554 | 0.9564 | 0.9573 | 0.9582 | 0.9591 | 0.9599 | 0.9608 | 0.9616 | 0.9625 | 0.9633 |
| 1.8 | 0.9641 | 0.9649 | 0.9656 | 0.9664 | 0.9671 | 0.9678 | 0.9686 | 0.9693 | 0.9699 | 0.9706 |
| 1.9 | 0.9713 | 0.9719 | 0.9726 | 0.9732 | 0.9738 | 0.9744 | 0.9750 | 0.9756 | 0.9761 | 0.9767 |
| 2.0 | 0.9772 | 0.9778 | 0.9783 | 0.9788 | 0.9793 | 0.9798 | 0.9803 | 0.9808 | 0.9812 | 0.9817 |
| 2.1 | 0.9821 | 0.9826 | 0.9830 | 0.9834 | 0.9838 | 0.9842 | 0.9846 | 0.9850 | 0.9854 | 0.9857 |
| 2.2 | 0.9861 | 0.9864 | 0.9868 | 0.9871 | 0.9875 | 0.9878 | 0.9881 | 0.9884 | 0.9887 | 0.9890 |
| 2.3 | 0.9893 | 0.9896 | 0.9898 | 0.9901 | 0.9904 | 0.9906 | 0.9909 | 0.9911 | 0.9913 | 0.9916 |
| 2.4 | 0.9918 | 0.9920 | 0.9922 | 0.9925 | 0.9927 | 0.9929 | 0.9931 | 0.9932 | 0.9934 | 0.9936 |
| 2.5 | 0.9938 | 0.9940 | 0.9941 | 0.9943 | 0.9945 | 0.9946 | 0.9948 | 0.9949 | 0.9951 | 0.9952 |
| 2.6 | 0.9953 | 0.9955 | 0.9956 | 0.9957 | 0.9959 | 0.9960 | 0.9961 | 0.9962 | 0.9963 | 0.9964 |
| 2.7 | 0.9965 | 0.9966 | 0.9967 | 0.9968 | 0.9969 | 0.9970 | 0.9971 | 0.9972 | 0.9973 | 0.9974 |
| 2.8 | 0.9974 | 0.9975 | 0.9976 | 0.9977 | 0.9977 | 0.9978 | 0.9979 | 0.9979 | 0.9980 | 0.9981 |
| 2.9 | 0.9981 | 0.9982 | 0.9982 | 0.9983 | 0.9984 | 0.9984 | 0.9985 | 0.9985 | 0.9986 | 0.9986 |
| 3.0 | 0.9987 | 0.9987 | 0.9987 | 0.9988 | 0.9988 | 0.9989 | 0.9989 | 0.9989 | 0.9990 | 0.9990 |
| 3.1 | 0.9990 | 0.9991 | 0.9991 | 0.9991 | 0.9992 | 0.9992 | 0.9992 | 0.9992 | 0.9993 | 0.9993 |
| 3.2 | 0.9993 | 0.9993 | 0.9994 | 0.9994 | 0.9994 | 0.9994 | 0.9994 | 0.9995 | 0.9995 | 0.9995 |
| 3.3 | 0.9995 | 0.9995 | 0.9995 | 0.9996 | 0.9996 | 0.9996 | 0.9996 | 0.9996 | 0.9996 | 0.9997 |
| 3.4 | 0.9997 | 0.9997 | 0.9997 | 0.9997 | 0.9997 | 0.9997 | 0.9997 | 0.9997 | 0.9997 | 0.9998 |
11.3 The empirical rule
| Within z of | Area inside | Area in the tails |
|---|---|---|
| ±1 | 68.27% | 31.73% |
| ±1.645 | 90.00% | 10.00% |
| ±1.96 | 95.00% | 5.00% |
| ±2 | 95.45% | 4.55% |
| ±2.576 | 99.00% | 1.00% |
| ±3 | 99.73% | 0.27% |
11.4 Common critical values
| Confidence | Alpha | Two-tailed critical z | One-tailed critical z |
|---|---|---|---|
| 80% | 0.2 | 1.2816 | 0.8416 |
| 90% | 0.1 | 1.6449 | 1.2816 |
| 95% | 0.05 | 1.96 | 1.6449 |
| 98% | 0.02 | 2.3263 | 2.0537 |
| 99% | 0.01 | 2.5758 | 2.3263 |
| 99.9% | 0.001 | 3.2905 | 3.0902 |
11.5 The maximum possible z-score by sample size
This is the table nobody prints, and it is the reason the |z| > 3 outlier rule fails so often. The largest z-score attainable in a sample of n is (n − 1) divided by the square root of n, no matter how extreme the value is.
| n | Maximum possible |z| | Can |z| > 3 ever fire? |
|---|---|---|
| 4 | 1.50 | No |
| 5 | 1.79 | No |
| 6 | 2.04 | No |
| 8 | 2.47 | No |
| 10 | 2.85 | No |
| 11 | 3.02 | Yes |
| 12 | 3.18 | Yes |
| 15 | 3.61 | Yes |
| 20 | 4.25 | Yes |
| 25 | 4.80 | Yes |
| 30 | 5.29 | Yes |
| 50 | 6.93 | Yes |
| 100 | 9.90 | Yes |
Below n = 11 the ordinary z-score rule is mathematically incapable of flagging anything. Use the modified z-score with a threshold of 3.5 instead.
11.6 Z-scores, percentiles and everyday equivalents
| Z-score | Percentile | IQ (mean 100, SD 15) | Interpretation |
|---|---|---|---|
| -3.0 | 0.13 | 55 | Extremely low |
| -2.0 | 2.28 | 70 | Well below average |
| -1.0 | 15.87 | 85 | Below average |
| -0.5 | 30.85 | 92.5 | Slightly below average |
| 0.0 | 50.00 | 100 | Exactly average |
| +0.5 | 69.15 | 107.5 | Slightly above average |
| +1.0 | 84.13 | 115 | Above average |
| +1.645 | 95.00 | 124.7 | Top 5% |
| +1.96 | 97.50 | 129.4 | Top 2.5% |
| +2.0 | 97.72 | 130 | Well above average |
| +2.576 | 99.50 | 138.6 | Top 0.5% |
| +3.0 | 99.87 | 145 | Extremely high |
11.7 Z-score to p-value quick reference
| |z| | Two-tailed p | One-tailed p | Significant at 0.05? |
|---|---|---|---|
| 0.50 | 0.6171 | 0.3085 | No |
| 1.00 | 0.3173 | 0.1587 | No |
| 1.50 | 0.1336 | 0.0668 | No |
| 1.645 | 0.1000 | 0.0500 | One-tailed only |
| 1.96 | 0.0500 | 0.0250 | Yes, exactly at the boundary |
| 2.00 | 0.0455 | 0.0228 | Yes |
| 2.576 | 0.0100 | 0.0050 | Yes |
| 3.00 | 0.0027 | 0.0013 | Yes |
| 3.29 | 0.0010 | 0.0005 | Yes |
11.8 Area between the mean and z
Some textbooks print the table this way instead. Each cell gives the area between 0 and z, which is simply the cumulative area minus 0.5. If your course uses this style, the value for z = 1.96 is 0.4750 rather than 0.9750.
| z | 0.00 | 0.01 | 0.02 | 0.03 | 0.04 | 0.05 | 0.06 | 0.07 | 0.08 | 0.09 |
|---|---|---|---|---|---|---|---|---|---|---|
| 0.0 | 0.0000 | 0.0040 | 0.0080 | 0.0120 | 0.0160 | 0.0199 | 0.0239 | 0.0279 | 0.0319 | 0.0359 |
| 0.1 | 0.0398 | 0.0438 | 0.0478 | 0.0517 | 0.0557 | 0.0596 | 0.0636 | 0.0675 | 0.0714 | 0.0753 |
| 0.2 | 0.0793 | 0.0832 | 0.0871 | 0.0910 | 0.0948 | 0.0987 | 0.1026 | 0.1064 | 0.1103 | 0.1141 |
| 0.3 | 0.1179 | 0.1217 | 0.1255 | 0.1293 | 0.1331 | 0.1368 | 0.1406 | 0.1443 | 0.1480 | 0.1517 |
| 0.4 | 0.1554 | 0.1591 | 0.1628 | 0.1664 | 0.1700 | 0.1736 | 0.1772 | 0.1808 | 0.1844 | 0.1879 |
| 0.5 | 0.1915 | 0.1950 | 0.1985 | 0.2019 | 0.2054 | 0.2088 | 0.2123 | 0.2157 | 0.2190 | 0.2224 |
| 0.6 | 0.2257 | 0.2291 | 0.2324 | 0.2357 | 0.2389 | 0.2422 | 0.2454 | 0.2486 | 0.2517 | 0.2549 |
| 0.7 | 0.2580 | 0.2611 | 0.2642 | 0.2673 | 0.2704 | 0.2734 | 0.2764 | 0.2794 | 0.2823 | 0.2852 |
| 0.8 | 0.2881 | 0.2910 | 0.2939 | 0.2967 | 0.2995 | 0.3023 | 0.3051 | 0.3078 | 0.3106 | 0.3133 |
| 0.9 | 0.3159 | 0.3186 | 0.3212 | 0.3238 | 0.3264 | 0.3289 | 0.3315 | 0.3340 | 0.3365 | 0.3389 |
| 1.0 | 0.3413 | 0.3438 | 0.3461 | 0.3485 | 0.3508 | 0.3531 | 0.3554 | 0.3577 | 0.3599 | 0.3621 |
| 1.1 | 0.3643 | 0.3665 | 0.3686 | 0.3708 | 0.3729 | 0.3749 | 0.3770 | 0.3790 | 0.3810 | 0.3830 |
| 1.2 | 0.3849 | 0.3869 | 0.3888 | 0.3907 | 0.3925 | 0.3944 | 0.3962 | 0.3980 | 0.3997 | 0.4015 |
| 1.3 | 0.4032 | 0.4049 | 0.4066 | 0.4082 | 0.4099 | 0.4115 | 0.4131 | 0.4147 | 0.4162 | 0.4177 |
| 1.4 | 0.4192 | 0.4207 | 0.4222 | 0.4236 | 0.4251 | 0.4265 | 0.4279 | 0.4292 | 0.4306 | 0.4319 |
| 1.5 | 0.4332 | 0.4345 | 0.4357 | 0.4370 | 0.4382 | 0.4394 | 0.4406 | 0.4418 | 0.4429 | 0.4441 |
| 1.6 | 0.4452 | 0.4463 | 0.4474 | 0.4484 | 0.4495 | 0.4505 | 0.4515 | 0.4525 | 0.4535 | 0.4545 |
| 1.7 | 0.4554 | 0.4564 | 0.4573 | 0.4582 | 0.4591 | 0.4599 | 0.4608 | 0.4616 | 0.4625 | 0.4633 |
| 1.8 | 0.4641 | 0.4649 | 0.4656 | 0.4664 | 0.4671 | 0.4678 | 0.4686 | 0.4693 | 0.4699 | 0.4706 |
| 1.9 | 0.4713 | 0.4719 | 0.4726 | 0.4732 | 0.4738 | 0.4744 | 0.4750 | 0.4756 | 0.4761 | 0.4767 |
| 2.0 | 0.4772 | 0.4778 | 0.4783 | 0.4788 | 0.4793 | 0.4798 | 0.4803 | 0.4808 | 0.4812 | 0.4817 |
| 2.1 | 0.4821 | 0.4826 | 0.4830 | 0.4834 | 0.4838 | 0.4842 | 0.4846 | 0.4850 | 0.4854 | 0.4857 |
| 2.2 | 0.4861 | 0.4864 | 0.4868 | 0.4871 | 0.4875 | 0.4878 | 0.4881 | 0.4884 | 0.4887 | 0.4890 |
| 2.3 | 0.4893 | 0.4896 | 0.4898 | 0.4901 | 0.4904 | 0.4906 | 0.4909 | 0.4911 | 0.4913 | 0.4916 |
| 2.4 | 0.4918 | 0.4920 | 0.4922 | 0.4925 | 0.4927 | 0.4929 | 0.4931 | 0.4932 | 0.4934 | 0.4936 |
| 2.5 | 0.4938 | 0.4940 | 0.4941 | 0.4943 | 0.4945 | 0.4946 | 0.4948 | 0.4949 | 0.4951 | 0.4952 |
| 2.6 | 0.4953 | 0.4955 | 0.4956 | 0.4957 | 0.4959 | 0.4960 | 0.4961 | 0.4962 | 0.4963 | 0.4964 |
| 2.7 | 0.4965 | 0.4966 | 0.4967 | 0.4968 | 0.4969 | 0.4970 | 0.4971 | 0.4972 | 0.4973 | 0.4974 |
| 2.8 | 0.4974 | 0.4975 | 0.4976 | 0.4977 | 0.4977 | 0.4978 | 0.4979 | 0.4979 | 0.4980 | 0.4981 |
| 2.9 | 0.4981 | 0.4982 | 0.4982 | 0.4983 | 0.4984 | 0.4984 | 0.4985 | 0.4985 | 0.4986 | 0.4986 |
| 3.0 | 0.4987 | 0.4987 | 0.4987 | 0.4988 | 0.4988 | 0.4989 | 0.4989 | 0.4989 | 0.4990 | 0.4990 |
11.9 Right-tail areas, the one-tailed p-value table
Each cell is the area to the right of z, which is exactly the one-tailed p-value. Double it for a two-tailed test. This is the table to use when you are testing a directional hypothesis.
| z | 0.00 | 0.01 | 0.02 | 0.03 | 0.04 | 0.05 | 0.06 | 0.07 | 0.08 | 0.09 |
|---|---|---|---|---|---|---|---|---|---|---|
| 0.0 | 0.5000 | 0.4960 | 0.4920 | 0.4880 | 0.4840 | 0.4801 | 0.4761 | 0.4721 | 0.4681 | 0.4641 |
| 0.1 | 0.4602 | 0.4562 | 0.4522 | 0.4483 | 0.4443 | 0.4404 | 0.4364 | 0.4325 | 0.4286 | 0.4247 |
| 0.2 | 0.4207 | 0.4168 | 0.4129 | 0.4090 | 0.4052 | 0.4013 | 0.3974 | 0.3936 | 0.3897 | 0.3859 |
| 0.3 | 0.3821 | 0.3783 | 0.3745 | 0.3707 | 0.3669 | 0.3632 | 0.3594 | 0.3557 | 0.3520 | 0.3483 |
| 0.4 | 0.3446 | 0.3409 | 0.3372 | 0.3336 | 0.3300 | 0.3264 | 0.3228 | 0.3192 | 0.3156 | 0.3121 |
| 0.5 | 0.3085 | 0.3050 | 0.3015 | 0.2981 | 0.2946 | 0.2912 | 0.2877 | 0.2843 | 0.2810 | 0.2776 |
| 0.6 | 0.2743 | 0.2709 | 0.2676 | 0.2643 | 0.2611 | 0.2578 | 0.2546 | 0.2514 | 0.2483 | 0.2451 |
| 0.7 | 0.2420 | 0.2389 | 0.2358 | 0.2327 | 0.2296 | 0.2266 | 0.2236 | 0.2206 | 0.2177 | 0.2148 |
| 0.8 | 0.2119 | 0.2090 | 0.2061 | 0.2033 | 0.2005 | 0.1977 | 0.1949 | 0.1922 | 0.1894 | 0.1867 |
| 0.9 | 0.1841 | 0.1814 | 0.1788 | 0.1762 | 0.1736 | 0.1711 | 0.1685 | 0.1660 | 0.1635 | 0.1611 |
| 1.0 | 0.1587 | 0.1562 | 0.1539 | 0.1515 | 0.1492 | 0.1469 | 0.1446 | 0.1423 | 0.1401 | 0.1379 |
| 1.1 | 0.1357 | 0.1335 | 0.1314 | 0.1292 | 0.1271 | 0.1251 | 0.1230 | 0.1210 | 0.1190 | 0.1170 |
| 1.2 | 0.1151 | 0.1131 | 0.1112 | 0.1093 | 0.1075 | 0.1056 | 0.1038 | 0.1020 | 0.1003 | 0.0985 |
| 1.3 | 0.0968 | 0.0951 | 0.0934 | 0.0918 | 0.0901 | 0.0885 | 0.0869 | 0.0853 | 0.0838 | 0.0823 |
| 1.4 | 0.0808 | 0.0793 | 0.0778 | 0.0764 | 0.0749 | 0.0735 | 0.0721 | 0.0708 | 0.0694 | 0.0681 |
| 1.5 | 0.0668 | 0.0655 | 0.0643 | 0.0630 | 0.0618 | 0.0606 | 0.0594 | 0.0582 | 0.0571 | 0.0559 |
| 1.6 | 0.0548 | 0.0537 | 0.0526 | 0.0516 | 0.0505 | 0.0495 | 0.0485 | 0.0475 | 0.0465 | 0.0455 |
| 1.7 | 0.0446 | 0.0436 | 0.0427 | 0.0418 | 0.0409 | 0.0401 | 0.0392 | 0.0384 | 0.0375 | 0.0367 |
| 1.8 | 0.0359 | 0.0351 | 0.0344 | 0.0336 | 0.0329 | 0.0322 | 0.0314 | 0.0307 | 0.0301 | 0.0294 |
| 1.9 | 0.0287 | 0.0281 | 0.0274 | 0.0268 | 0.0262 | 0.0256 | 0.0250 | 0.0244 | 0.0239 | 0.0233 |
| 2.0 | 0.0228 | 0.0222 | 0.0217 | 0.0212 | 0.0207 | 0.0202 | 0.0197 | 0.0192 | 0.0188 | 0.0183 |
| 2.1 | 0.0179 | 0.0174 | 0.0170 | 0.0166 | 0.0162 | 0.0158 | 0.0154 | 0.0150 | 0.0146 | 0.0143 |
| 2.2 | 0.0139 | 0.0136 | 0.0132 | 0.0129 | 0.0125 | 0.0122 | 0.0119 | 0.0116 | 0.0113 | 0.0110 |
| 2.3 | 0.0107 | 0.0104 | 0.0102 | 0.0099 | 0.0096 | 0.0094 | 0.0091 | 0.0089 | 0.0087 | 0.0084 |
| 2.4 | 0.0082 | 0.0080 | 0.0078 | 0.0075 | 0.0073 | 0.0071 | 0.0069 | 0.0068 | 0.0066 | 0.0064 |
| 2.5 | 0.0062 | 0.0060 | 0.0059 | 0.0057 | 0.0055 | 0.0054 | 0.0052 | 0.0051 | 0.0049 | 0.0048 |
| 2.6 | 0.0047 | 0.0045 | 0.0044 | 0.0043 | 0.0041 | 0.0040 | 0.0039 | 0.0038 | 0.0037 | 0.0036 |
| 2.7 | 0.0035 | 0.0034 | 0.0033 | 0.0032 | 0.0031 | 0.0030 | 0.0029 | 0.0028 | 0.0027 | 0.0026 |
| 2.8 | 0.0026 | 0.0025 | 0.0024 | 0.0023 | 0.0023 | 0.0022 | 0.0021 | 0.0021 | 0.0020 | 0.0019 |
| 2.9 | 0.0019 | 0.0018 | 0.0018 | 0.0017 | 0.0016 | 0.0016 | 0.0015 | 0.0015 | 0.0014 | 0.0014 |
| 3.0 | 0.0013 | 0.0013 | 0.0013 | 0.0012 | 0.0012 | 0.0011 | 0.0011 | 0.0011 | 0.0010 | 0.0010 |
11.10 Z-scores converted to other standardised scales
Almost every named score system is a z-score in disguise, rescaled to avoid decimals and negative numbers. This table shows the conversions, so you can move between them without recomputing anything.
| Z-score | Percentile | T-score M 50, SD 10 | IQ M 100, SD 15 | SAT section M 500, SD 100 | GRE section M 150, SD 8.75 | Stanine | Sten |
|---|---|---|---|---|---|---|---|
| -3.0 | 0.13 | 20 | 55.0 | 200 | 123.8 | 1 | 1 |
| -2.5 | 0.62 | 25 | 62.5 | 250 | 128.1 | 1 | 1 |
| -2.0 | 2.28 | 30 | 70.0 | 300 | 132.5 | 1 | 2 |
| -1.5 | 6.68 | 35 | 77.5 | 350 | 136.9 | 2 | 2 |
| -1.0 | 15.87 | 40 | 85.0 | 400 | 141.2 | 3 | 4 |
| -0.5 | 30.85 | 45 | 92.5 | 450 | 145.6 | 4 | 4 |
| 0.0 | 50.00 | 50 | 100.0 | 500 | 150.0 | 5 | 6 |
| +0.5 | 69.15 | 55 | 107.5 | 550 | 154.4 | 6 | 6 |
| +1.0 | 84.13 | 60 | 115.0 | 600 | 158.8 | 7 | 8 |
| +1.5 | 93.32 | 65 | 122.5 | 650 | 163.1 | 8 | 8 |
| +2.0 | 97.72 | 70 | 130.0 | 700 | 167.5 | 9 | 10 |
| +2.5 | 99.38 | 75 | 137.5 | 750 | 171.9 | 9 | 10 |
| +3.0 | 99.87 | 80 | 145.0 | 800 | 176.2 | 9 | 10 |
Conversion formulas: T = 50 + 10z, IQ = 100 + 15z, SAT section = 500 + 100z, GRE section = 150 + 8.75z, stanine = 5 + 2z rounded and clipped to 1-9, sten = 5.5 + 2z rounded and clipped to 1-10.
11.11 How rare is that z-score?
Percentages are hard to feel. This table restates the same probabilities as "one in how many", which is usually more intuitive when you are deciding whether an observation is worth investigating.
| |z| | Rarity, one side | Rarity, both sides | One-tailed p | Two-tailed p |
|---|---|---|---|---|
| 1 | 1 in 6 | 1 in 3 | 1.587e-01 | 3.173e-01 |
| 1.5 | 1 in 15 | 1 in 7 | 6.681e-02 | 1.336e-01 |
| 1.96 | 1 in 40 | 1 in 20 | 2.500e-02 | 5.000e-02 |
| 2 | 1 in 44 | 1 in 22 | 2.275e-02 | 4.550e-02 |
| 2.5 | 1 in 161 | 1 in 81 | 6.210e-03 | 1.242e-02 |
| 2.576 | 1 in 200 | 1 in 100 | 4.998e-03 | 9.995e-03 |
| 3 | 1 in 741 | 1 in 370 | 1.350e-03 | 2.700e-03 |
| 3.29 | 1 in 1,996 | 1 in 998 | 5.009e-04 | 1.002e-03 |
| 3.5 | 1 in 4,299 | 1 in 2,149 | 2.326e-04 | 4.653e-04 |
| 4 | 1 in 31,574 | 1 in 15,787 | 3.167e-05 | 6.334e-05 |
| 4.5 | 1 in 294,319 | 1 in 147,160 | 3.398e-06 | 6.795e-06 |
| 5 | 1 in 3,488,556 | 1 in 1,744,278 | 2.867e-07 | 5.733e-07 |
| 6 | 1 in 1,013,594,635 | 1 in 506,797,317 | 9.866e-10 | 1.973e-09 |
The five sigma standard used in particle physics corresponds to a one-sided probability of about 1 in 3.5 million. Six sigma manufacturing allows for a 1.5 sigma process drift, so in practice it targets about 3.4 defects per million rather than the 1 in a billion the raw table implies.
11.12 Sample size and the standard error
When you score a sample mean rather than a single value, the denominator is the standard error. This table shows how quickly it shrinks, and why quadrupling your sample only halves your uncertainty.
| n | √n | SE as a fraction of SD | 95% margin of error in SD units |
|---|---|---|---|
| 4 | 2.00 | 0.5000 | 0.9800 |
| 9 | 3.00 | 0.3333 | 0.6533 |
| 16 | 4.00 | 0.2500 | 0.4900 |
| 25 | 5.00 | 0.2000 | 0.3920 |
| 36 | 6.00 | 0.1667 | 0.3267 |
| 49 | 7.00 | 0.1429 | 0.2800 |
| 64 | 8.00 | 0.1250 | 0.2450 |
| 100 | 10.00 | 0.1000 | 0.1960 |
| 144 | 12.00 | 0.0833 | 0.1633 |
| 225 | 15.00 | 0.0667 | 0.1307 |
| 400 | 20.00 | 0.0500 | 0.0980 |
| 1000 | 31.62 | 0.0316 | 0.0620 |
Read the last column as: with n = 25 a 95% interval reaches 0.392 standard deviations either side of the sample mean. Going from n = 25 to n = 100 halves that to 0.196, which is why sample size has diminishing returns.
11.13 The same calculation in five packages
| Task | Excel | R | Python | SPSS | Stata |
|---|---|---|---|---|---|
| Mean | AVERAGE(r) | mean(x) | x.mean() | DESCRIPTIVES | summarize x |
| Sample SD | STDEV.S(r) | sd(x) | x.std(ddof=1) | DESCRIPTIVES | summarize x |
| Population SD | STDEV.P(r) | sd(x)*sqrt((n-1)/n) | x.std() | not direct | summarize x |
| Standardise a variable | STANDARDIZE(x,m,s) | scale(x) | stats.zscore(x,ddof=1) | DESCRIPTIVES /SAVE | egen z=std(x) |
| Percentile from z | NORM.S.DIST(z,TRUE) | pnorm(z) | stats.norm.cdf(z) | CDF.NORMAL(z,0,1) | normal(z) |
| Z from percentile | NORM.S.INV(p) | qnorm(p) | stats.norm.ppf(p) | IDF.NORMAL(p,0,1) | invnormal(p) |
| Two-tailed p | 2*(1-NORM.S.DIST(ABS(z),TRUE)) | 2*pnorm(-abs(z)) | 2*stats.norm.sf(abs(z)) | 2*(1-CDF.NORMAL(ABS(z),0,1)) | 2*normal(-abs(z)) |
| Median | MEDIAN(r) | median(x) | np.median(x) | FREQUENCIES | summarize x, detail |
| Default SD divisor | you choose | n − 1 | n | n − 1 | n − 1 |
scipy.stats.zscore and scikit-learn's StandardScaler all default to n. That single inconsistency is the most common reason two analysts get different z-scores from identical data.11.14 Choosing an outlier method
| Method | Formula | Threshold | Robust? | Minimum useful n | Best for |
|---|---|---|---|---|---|
| Ordinary z-score | (x − mean) / SD | |z| > 3 | No | About 30 | Large, roughly normal samples |
| Modified z-score | 0.6745(x − median) / MAD | |M| > 3.5 | Yes | About 10 | Small samples, suspected masking |
| Tukey IQR rule | outside Q1 − 1.5×IQR to Q3 + 1.5×IQR | fixed | Yes | About 8 | Exploratory work, boxplots |
| Grubbs test | max|x − mean| / SD | table of critical values | No | 7 | Testing one suspected outlier formally |
| Percentile trimming | drop below 1st or above 99th | chosen in advance | Yes | About 100 | Large datasets, automated pipelines |
| Isolation forest | tree-based anomaly score | contamination rate | Yes | Several hundred | Many variables at once |
11.15 Reading the sign and size at a glance
| Z-score range | Plain-English description | Roughly how often in normal data | What to do |
|---|---|---|---|
| |z| below 0.5 | Essentially average | About 38% of values | Nothing. This is the middle of the distribution |
| 0.5 to 1.0 | Slightly above or below average | About 30% of values | Nothing. Still entirely ordinary |
| 1.0 to 2.0 | Noticeably high or low | About 27% of values | Worth mentioning, not worth investigating |
| 2.0 to 2.5 | Uncommon | About 4% of values | Note it. Check the measurement if it matters |
| 2.5 to 3.0 | Rare | About 1% of values | Inspect the original record |
| Above 3.0 | Very rare | About 0.27% of values | Inspect, and check the modified z-score too |
| Above 5.0 | Almost certainly not chance | About 1 in 1.7 million | Suspect a data-entry or unit error first |
These frequencies apply to a normal distribution only. On skewed data, values with |z| above 3 can be entirely common, which is why chart 3 in the results section matters before you use this table.
💡 12. Eight Worked Examples
Every number below was computed with the calculator on this page, using the sample standard deviation and a two-tailed p-value unless stated otherwise. Each example has its own colour and its own figure showing where the value falls on the normal curve. Paste any dataset into the tool and you will reproduce the result exactly.
Data: 52, 48, 55, 61, 47, 58, 50, 63, 45, 56, 54, 49
Value scored: 61
| n | 12 |
|---|---|
| Mean | 53.1667 |
| SD (sample) | 5.6702 |
| Z-score | 1.3815 |
| Percentile | 91.64 |
| Two-tailed p | 0.1671 |
| Median | 53.0 |
| MAD | 4.5 |
| Modified z | 1.1991 |
Reading it: A mark of 61 sits 1.38 standard deviations above the class mean, roughly the 92nd percentile. The two-tailed p of 0.167 says a mark this far from the mean is not unusual. Note the ceiling: with n = 12 the largest z any mark could reach is 3.18, so the |z| > 3 rule has almost no room to work here.
Data: mean 100, SD 15 (published norms, so population values)
Value scored: 148
| μ | 100 |
|---|---|
| σ | 15 |
| x | 148 |
| Z-score | 3.2000 |
| Percentile | 99.93 |
| Two-tailed p | 0.00137 |
| Rarity | about 1 in 1455 |
Reading it: This is the cleanest possible case, because the mean and SD are genuinely known rather than estimated. A z of 3.2 puts the score above roughly 99.93% of the population, or about 1 person in 1,450. The percentile is trustworthy here precisely because IQ scores are constructed to be normally distributed.
Data: 12.1, 11.8, 12.4, 12.0, 11.9, 12.3, 12.2, 11.7, 12.5, 12.0, 11.6, 12.6, 12.1, 11.95, 12.35
Value scored: 12.6
| n | 15 |
|---|---|
| Mean | 12.1000 |
| SD (sample) | 0.2922 |
| Z-score | 1.7114 |
| Percentile | 95.65 |
| Two-tailed p | 0.0870 |
Reading it: The largest part is 1.71 SD above the mean, comfortably inside a typical ±3 SD control limit. In process control the z-score is the natural language: specification limits are usually expressed as a number of standard deviations, and a six sigma process is simply one where the nearest limit sits at z = 6.
Data: 120, 118, 125, 119, 122, 121, 117, 124, 123, 119, 126, 116, 121, 120, 118, 180
Value scored: 180
| n | 16 |
|---|---|
| Mean | 124.3125 |
| SD (sample) | 15.1205 |
| SD without the 180 | 2.9472 |
| Z-score | 3.6829 |
| Max possible |z| | 3.75 |
| Median | 120.5 |
| MAD | 2.5 |
| Modified z | 16.05 |
Reading it: This is the single most instructive example on the page. The ordinary z is 3.68, only just past the usual threshold, because the outlier inflated the standard deviation from 2.95 to 15.12 and thereby disguised itself. The modified z-score, built from the median and MAD, returns 16.05, an unmistakable flag. Also notice the ceiling for n = 16 is 3.75, marked as the dashed line in the figure, so this value came within a whisker of being uncatchable by the ordinary rule no matter how extreme it was.
Data: 3.2, 3.5, 3.1, 3.8, 3.4, 3.6, 3.3, 3.7, 3.45, 3.55
Value scored: 3.8
| n | 10 |
|---|---|
| Mean | 3.4600 |
| SD (sample) | 0.2183 |
| Z-score | 1.5573 |
| Percentile | 94.03 |
| Max possible |z| | 2.85 |
Reading it: With ten observations the largest z-score arithmetically possible is 2.85, shown as the dashed ceiling line well to the left of z = 3. No value in this dataset can ever be flagged, so screening it with the |z| > 3 rule guarantees zero detections regardless of the data. Use the modified z-score at any sample size below about thirty.
Data: population mean 500, population SD 100, sample mean 512 from n = 64
Value scored: 512 (a mean, not one value)
| Standard error | 100 ÷ √64 = 12.5 |
|---|---|
| Z-score | 0.9600 |
| Percentile | 83.15 |
| Two-tailed p | 0.3371 |
| If you wrongly used SD | z = 0.12 |
Reading it: The denominator is the standard error, 12.5, not the standard deviation of 100. Had you wrongly divided by 100 you would have got z = 0.12 and concluded the sample was utterly ordinary. Even done correctly, p = 0.337 gives no evidence the sample came from a population with a different mean.
Data: 88, 92, 79, 85, 95, 73, 90, 84, 81, 87, 94, 76, 89, 83, 91, 80, 86, 93, 77, 82
Value scored: 95
| n | 20 |
|---|---|
| Mean | 85.2500 |
| SD (sample) | 6.3401 |
| Z-score | 1.5378 |
| Percentile | 93.80 |
| Two-tailed p | 0.1241 |
| Same student, harder paper | 68 with M 55, SD 9 gives z = 1.4444 |
Reading it: The top scorer is 1.54 SD above the class mean. The point of standardising is comparison: the same student scoring 68 on a harder paper with mean 55 and SD 9 gives z = 1.44, so the two performances are almost identical despite the raw marks differing by 27 points.
Data: mean 500, SD 100. Question: what value marks the 90th percentile?
Value scored: the cut-off is what we are solving for
| Percentile wanted | 90% |
|---|---|
| Z for that percentile | 1.2816 |
| Raw value | 500 + 1.2816 × 100 = 628.16 |
| Excel | =NORM.S.INV(0.9) |
| R | qnorm(0.9) |
| Python | stats.norm.ppf(0.9) |
Reading it: The reverse mode answers cut-off questions: the pass mark for the top decile, the reorder level that covers 90% of demand, the threshold that captures the top 10% of applicants. The shaded region in the figure is the 90% of the distribution that falls below the cut-off.
📋 13. Data Collection Protocol
A z-score is only as good as the mean and standard deviation behind it, and both are easy to compromise before you ever open a calculator. This is the protocol that keeps them honest.
- Define the reference population before you collect anything. A z-score answers "unusual compared with what". Compared with this year's cohort, or all cohorts? Compared with this machine, or the whole production line? The answer determines which mean and SD you should be using, and it cannot be decided afterwards.
- Decide whether you have a population or a sample. If you measured every unit that exists in your defined population, use the population SD. In almost every real study you have a sample, so use n minus 1.
- Use a single measurement protocol throughout. Same instrument, same units, same operator instructions, same time of day where it matters. Mixing two measurement methods inflates the SD and shrinks every z-score, which quietly hides genuine outliers.
- Record raw values, never rounded ones. Rounding to the nearest whole number before computing an SD can shift z-scores in the second decimal place. Round at the reporting stage only.
- Log every exclusion at the moment it happens. Instrument failure, protocol violation, participant withdrawal. Write down what and why. Reviewers will compare your stated n against your reported one.
- Aim for n of at least 30 if outlier screening is a goal. Below that the |z| > 3 rule is mathematically hobbled by the ceiling. If a small sample is unavoidable, plan to use the modified z-score and say so in your methods.
- Check for the units trap. Z-scores are unitless, which is their strength, but the mean and SD must come from data in one consistent unit. One row in grams among a column of kilograms will not throw an error, it will just produce a spectacular false outlier.
- Screen for normality before you rely on percentiles. A histogram and a Q-Q plot take thirty seconds. If the data are clearly skewed, plan to report the z-score as a distance in standard deviations and drop the percentile claim.
- Keep the raw data file untouched. Do all cleaning in a script or a copy so that any z-score can be traced back to the original record. When a value is flagged, you will want to look at the original entry.
- Store the mean, SD and n alongside the z-scores. A column of z-scores with no record of what standardised them is uninterpretable and cannot be back-transformed.
🎯 14. When to Use a Z-Score, and When Not To
Use a z-score when
- You need to compare measurements on different scales. This is the original purpose. Height in centimetres against weight in kilograms, an essay mark against a maths mark, revenue in one currency against another.
- You want a percentile from a roughly normal distribution. Test scores, measurement error, biological measurements within a defined group.
- You are standardising features before modelling. Regularised regression, k-nearest neighbours, principal component analysis, neural networks and anything using gradient descent all behave badly when features have wildly different scales.
- You are screening a reasonably large sample for outliers. Above about n = 30 the ordinary rule becomes usable, though the modified version is still safer.
- The population mean and SD are genuinely known. Standardised tests, published growth charts, calibrated instruments. This is the case textbooks assume and the case where the percentile is most trustworthy.
- You need a cut-off from a percentile. The reverse direction: what value marks the top 5%, what stock level covers 95% of demand.
Do not use a z-score when
- The data are strongly skewed and you want a percentile. Income, waiting times, species abundance, insurance claims. The z-score is still a valid distance but the normal percentile attached to it will be wrong. Use the empirical percentile rank instead.
- The sample is small and you are hunting outliers. Below n = 11 the |z| > 3 rule cannot fire at all. Use the modified z-score.
- The data are ordinal. Likert responses, rankings, categories with an order. The mean and SD of an ordinal scale are not well defined, so neither is the z-score.
- The standard deviation is zero or near zero. If every value is identical the z-score is undefined. If they are nearly identical, tiny differences become enormous z-scores.
- You are testing a sample mean with an estimated SD and a small n. That is a t-test, not a z-test. The t-distribution has heavier tails and gives more honest p-values below about n = 30.
- The distribution has two peaks. A bimodal dataset has a mean sitting in the empty valley between the groups, so every z-score is measured from a value that no observation resembles.
- You want to say something about the reliability of the estimate. A z-score carries no uncertainty of its own and no information about sample size. Use a confidence interval for that.
What to use instead
| Situation | Better choice | Why |
|---|---|---|
| Skewed data, need a percentile | Empirical percentile rank | Makes no distributional assumption |
| Small sample outlier screening | Modified z-score, threshold 3.5 | Median and MAD cannot be inflated by the outlier |
| Outliers in a boxplot context | 1.5 × IQR rule | Also robust, and standard in exploratory work |
| Testing a mean, SD estimated, n small | One-sample t-test | Accounts for the extra uncertainty in the estimated SD |
| Comparing spread across different scales | Coefficient of variation | Relative rather than absolute variability |
| Heavy skew before any analysis | Log or Box-Cox transform, then standardise | Restores approximate normality first |
| Robust scaling for machine learning | RobustScaler (median and IQR) | Outliers do not distort the scaling |
🔧 15. Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Z-score is undefined or the tool refuses to run | Standard deviation is zero, so every value equals the mean | There is no z-score to compute. Check that your data actually vary |
| Every z-score is enormous | Near-zero SD, or one value entered in the wrong unit | Look at the raw data. A single gram among kilograms does this |
| Your answer differs from a colleague's in the third decimal | One of you used n and the other n minus 1 | Agree on sample or population and state it in the methods |
| R and Python disagree | sd() uses n minus 1, np.std() defaults to n | Write ddof=1 in NumPy |
| No outliers found, but the data clearly contain one | The outlier inflated the SD and hid itself, or n is below the ceiling | Use the modified z-score with a 3.5 threshold |
| Percentile looks wrong for your data | The data are not normal, so the normal curve mapping fails | Report the z-score as a distance, or use an empirical percentile |
| P-value is half what you expected | One-tailed instead of two-tailed | Choose the tail before seeing the data, and state which you used |
| Excel returns #VALUE! | Text or blanks inside the range, or FALSE passed to NORM.S.DIST | Clean the range and pass TRUE for cumulative probability |
| Excel z-scores drift as you drag down the column | Missing dollar signs on the mean and SD references | Use $E$2 and $E$3 |
| Modified z-score is infinite | MAD is zero because more than half the values are identical | The data are too discrete for this method. Use the IQR rule |
| R's mad() gives a different modified z | R's mad() already multiplies by 1.4826 | Compute median(abs(x - median(x))) directly |
| Z-score for a sample mean looks far too small | Divided by the SD instead of the standard error | Divide by SD over the square root of n |
| Model accuracy dropped after standardising | Scaler fitted on the full dataset, leaking test information | Fit on training data only, then transform the test set |
| CSV upload produced empty columns | Non-numeric characters such as currency symbols or thousands separators | Strip them before uploading, or select a different column |
| Two groups have the same z-score but look very different | Different means and SDs produce identical standardised positions | Always report the mean, SD and n alongside the z-score |
⚖ 16. Assumptions and Limitations
What the z-score assumes
| Assumption | Needed for | What happens if it fails |
|---|---|---|
| Data are numeric on an interval or ratio scale | The z-score itself | The mean and SD are undefined for ordinal or nominal data |
| Standard deviation is greater than zero | The z-score itself | Division by zero. No z-score exists |
| Data are approximately normal | Percentile and p-value only | The z-score stays correct; the percentile can be badly wrong |
| Mean and SD represent the right reference population | Any interpretation | The z-score answers a different question than you asked |
| Observations are independent | P-values | Clustered or repeated measures give p-values that are too small |
| Population σ is known | A formal z-test | With an estimated SD and small n, use a t-test instead |
| Sample is large enough | Outlier screening | The ceiling makes the |z| > 3 rule unable to fire |
Limitations you should state in a write-up
- The percentile depends entirely on normality. The z-score does not, and the two are often conflated. Be explicit about which claim you are making.
- The mean and SD are not robust. A single extreme value moves both, so every z-score in the dataset shifts because of one observation.
- The ceiling on small samples is a hard mathematical limit, not a statistical convention. It cannot be argued around.
- A z-score carries no uncertainty. It is a point description with no confidence interval attached and no sensitivity to sample size.
- Standardising does not fix skew. It shifts and rescales the distribution but leaves its shape untouched. A skewed variable is still skewed after standardising.
- Comparability requires a shared reference. Two z-scores computed against different populations are not directly comparable, however similar the numbers look.
- Extreme z-scores are not evidence of error. They flag values worth inspecting, nothing more. The decision to exclude is a scientific judgement, not a statistical one.
🏁 17. Conclusion
The z-score is one of the smallest ideas in statistics and one of the most widely used. Subtract the mean, divide by the standard deviation, and you have converted a measurement in any unit into a position on a common scale. That single operation is what lets an exam mark be compared with an essay grade, a machine tolerance with a laboratory assay, and a height measurement with a weight measurement. It is the reason standardisation appears in almost every statistical method that combines variables.
Its interpretation is equally compact. The sign gives the direction, the magnitude gives the distance in standard deviations, and the empirical rule supplies rough landmarks: about 68% of a normal distribution lies within one standard deviation of the mean, about 95% within two, about 99.7% within three. A z of 2 is therefore uncommon and a z of 3 is rare, in a normal distribution.
That last qualification is where most misuse begins. The z-score itself assumes nothing, but the moment you convert it into a percentile or a p-value you are reading off the normal curve, and if your data are skewed those numbers are wrong even though the z-score is right. Check a histogram before you quote a percentile. If the shape is not roughly symmetric, report the z-score as a distance and use an empirical percentile rank instead.
The second failure is subtler and far more common: using the |z| > 3 rule to find outliers in a small sample. The largest z-score attainable in a sample of n is (n − 1) divided by the square root of n. With ten observations that ceiling is 2.85, so the rule cannot fire no matter how extreme a value is. Worse, an outlier inflates the standard deviation used to compute its own z-score, disguising itself even when the sample is large enough. Example 4 on this page shows exactly that: a heart rate of 180 among readings in the 120s gives an ordinary z of 3.68, barely past the threshold, while the modified z-score built from the median and MAD returns 16.05. Whenever you are screening for outliers, particularly below about n = 30, use the modified z-score with a threshold of 3.5.
Three further habits will keep your results defensible. Decide sample or population once and stay with it, because the difference between STDEV.S and STDEV.P propagates through every number downstream and is the usual reason two people get slightly different answers. Choose your p-value tail before you look at the data, not after. And always report the mean, standard deviation and n alongside any z-score, because the same z means entirely different things depending on the distribution it came from, and without those three numbers nobody, including you in six months, can reconstruct what you did.
Used with those caveats, the z-score remains what it has been since Fisher and Pearson formalised it: the simplest honest way to say how unusual a number is.
❓ 18. Frequently Asked Questions
What is a z-score in simple terms?
What is the formula for a z-score?
Is a high z-score good or bad?
What z-score counts as an outlier?
What is a modified z-score?
Can a z-score be negative?
What is the maximum possible z-score?
How do I convert a z-score to a percentile?
NORM.S.DIST(z, TRUE) in Excel, pnorm(z) in R, or stats.norm.cdf(z) in Python. A z of 1.5 gives about 0.9332, so the 93rd percentile. This conversion assumes the data are normally distributed; on skewed data the percentile will be wrong even though the z-score is correct.What is the difference between a z-score and a t-score?
Should I use n or n minus 1 for the standard deviation?
STDEV.S, R's sd() and NumPy's ddof=1. The population version divides by n and gives a slightly smaller standard deviation, hence slightly larger z-scores. Pick one and use it consistently, because this is the single most common reason two people get different answers from the same data.Why do R and Python give me different z-scores?
sd() always divides by n minus 1, while NumPy's np.std() defaults to dividing by n. So does scipy.stats.zscore() and scikit-learn's StandardScaler. Add ddof=1 in Python to match R.Do my data have to be normally distributed?
What is the empirical rule?
How do I calculate a z-score in Excel?
=(A2-$E$2)/$E$3, or use the built-in =STANDARDIZE(A2, mean, sd). Get the mean with =AVERAGE(range) and the standard deviation with =STDEV.S(range) for a sample. The dollar signs matter: without them the references slide as you drag the formula down.What does a z-score of 0 mean?
How do I find the raw value from a z-score?
NORM.S.INV in Excel, qnorm() in R or stats.norm.ppf() in Python. For example the 95th percentile is z = 1.6449, so with a mean of 500 and SD of 100 the cut-off is 664.49.What is a z-test and how does it relate to a z-score?
Can I compare z-scores from two different datasets?
Why should I standardise features before machine learning?
What is the difference between a z-score and a percentile rank?
🔖 19. Cite This Tool
🔗 20. Related Calculators
📖 21. Glossary
| Term | Meaning |
|---|---|
| Z-score | The number of standard deviations a value lies above or below the mean. Also called a standard score. |
| Standard score | Another name for a z-score. |
| Standardisation | Converting a variable so it has mean 0 and standard deviation 1, by computing the z-score of every value. |
| Standard normal distribution | A normal distribution with mean 0 and standard deviation 1. Every z-score lives on this scale. |
| μ (mu) | The population mean. |
| σ (sigma) | The population standard deviation. |
| x̄ (x-bar) | The sample mean. |
| s | The sample standard deviation, computed with n minus 1. |
| Bessel's correction | Dividing by n minus 1 instead of n when estimating a variance from a sample, which removes downward bias. |
| Degrees of freedom (ddof) | The parameter that controls whether software divides by n or n minus 1. R fixes it at 1, NumPy defaults to 0. |
| Standard error | The standard deviation of a sample mean, σ divided by the square root of n. Used when scoring a mean rather than a single value. |
| Φ (Phi) | The standard normal cumulative distribution function, giving the area to the left of a z-score. |
| Percentile | The percentage of the distribution falling below a given value. |
| Percentile rank | A percentile computed directly by counting data, with no normality assumption. |
| P-value | The probability of observing a value at least this extreme if the null model is true. |
| Two-tailed test | Counts extreme values in both directions. The default when you simply want to know whether a value is unusual. |
| One-tailed test | Counts extremes in one direction only. Must be chosen before seeing the data. |
| Critical value | The z-score marking the boundary of a rejection region, such as 1.96 for a two-tailed test at alpha 0.05. |
| Empirical rule | The 68-95-99.7 approximation for how much of a normal distribution lies within one, two and three standard deviations of the mean. |
| Outlier | An observation far from the rest of the data. Unusual, not necessarily wrong. |
| Masking | When an outlier inflates the standard deviation enough to keep its own z-score below the detection threshold. |
| MAD | Median absolute deviation, the median of the absolute distances from the median. A robust measure of spread. |
| Modified z-score | 0.6745 × (x − median) ÷ MAD. A robust alternative that outliers cannot distort. |
| Robust statistic | One whose value is barely affected by a small number of extreme observations. |
| Skewness | Asymmetry in a distribution. Skewed data break the percentile interpretation of a z-score. |
| Z-test | A hypothesis test comparing a sample mean against a known population mean using a known population standard deviation. |
| T-score | The analogue of a z-score when the standard deviation is estimated from the sample, following the t-distribution. |
| Q-Q plot | A diagnostic plot comparing your data's quantiles against a normal distribution's, used to check normality. |
📚 22. References
- Iglewicz, B., & Hoaglin, D. C. (1993). How to Detect and Handle Outliers. ASQC Quality Press. asq.org
- Shiffler, R. E. (1988). Maximum Z scores and outliers. The American Statistician, 42(1), 79-80. doi.org/10.1080/00031305.1988.10475530
- Leys, C., Ley, C., Klein, O., Bernard, P., & Licata, L. (2013). Detecting outliers: Do not use standard deviation around the mean, use absolute deviation around the median. Journal of Experimental Social Psychology, 49(4), 764-766. doi.org/10.1016/j.jesp.2013.03.013
- Rousseeuw, P. J., & Croux, C. (1993). Alternatives to the median absolute deviation. Journal of the American Statistical Association, 88(424), 1273-1283. doi.org/10.1080/01621459.1993.10476408
- Grubbs, F. E. (1969). Procedures for detecting outlying observations in samples. Technometrics, 11(1), 1-21. doi.org/10.1080/00401706.1969.10490657
- Tukey, J. W. (1977). Exploratory Data Analysis. Addison-Wesley. archive.org
- Moore, D. S., McCabe, G. P., & Craig, B. A. (2021). Introduction to the Practice of Statistics (10th ed.). W. H. Freeman. macmillanlearning.com
- Field, A. (2024). Discovering Statistics Using IBM SPSS Statistics (6th ed.). SAGE. sagepub.com
- Barnett, V., & Lewis, T. (1994). Outliers in Statistical Data (3rd ed.). Wiley. wiley.com
- Abramowitz, M., & Stegun, I. A. (1964). Handbook of Mathematical Functions. National Bureau of Standards. personal.math.ubc.ca
- Press, W. H., Teukolsky, S. A., Vetterling, W. T., & Flannery, B. P. (2007). Numerical Recipes: The Art of Scientific Computing (3rd ed.). Cambridge University Press. numerical.recipes
- Acklam, P. J. (2003). An algorithm for computing the inverse normal cumulative distribution function. Archived notes
- Wicklin, R. (2011). What is a z-score? What is a p-value? SAS Blogs, The DO Loop. blogs.sas.com
- Wilkinson, L., & the Task Force on Statistical Inference (1999). Statistical methods in psychology journals: Guidelines and explanations. American Psychologist, 54(8), 594-604. doi.org/10.1037/0003-066X.54.8.594
- American Psychological Association (2020). Publication Manual of the American Psychological Association (7th ed.). apastyle.apa.org
- Ghasemi, A., & Zahediasl, S. (2012). Normality tests for statistical analysis: A guide for non-statisticians. International Journal of Endocrinology and Metabolism, 10(2), 486-489. doi.org/10.5812/ijem.3505
- Kuhn, M., & Johnson, K. (2019). Feature Engineering and Selection: A Practical Approach for Predictive Models. CRC Press. bookdown.org/max/FES
- Pedregosa, F., et al. (2011). Scikit-learn: Machine learning in Python. Journal of Machine Learning Research, 12, 2825-2830. jmlr.org
- Virtanen, P., et al. (2020). SciPy 1.0: Fundamental algorithms for scientific computing in Python. Nature Methods, 17, 261-272. doi.org/10.1038/s41592-019-0686-2
- R Core Team (2024). R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing. r-project.org
