StatisticsSciPy 1.18 · pandas 3.0 · statsmodels 0.15 · Python 3.12 or 3.13
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
57 small wins to finish your pathNext lesson →

One-sample z-test

A one-sample z-test is a hypothesis test that checks whether a population mean equals a stated value μ₀, using the sample mean and a known population standard deviation σ.

Last updated: 07 Oct, 2026 · SciPy 1.18

A new medication is given to 30 people. Their mean IQ is 140, while the population's is 100. Is that gap the drug, or could it be the luck of the sample? The z-test answers with one number and a fixed rule.

The IQ medication z-test · from the Complete Statistics for Data Science in 6 Hours video · 3:47:20 to 3:52:01

Setting up the IQ medication problem

The video's problem: in the population the average IQ is 100 with a standard deviation of 15. Researchers want to know whether a new medication has a positive or negative effect on intelligence, or no effect. A sample of 30 participants who took it has a mean IQ of 140.

  • Hypothesized mean μ₀ = 100
  • Population standard deviation σ = 15 (known)
  • Sample size n = 30
  • Sample mean x̄ = 140
  • Significance level α = 0.05

A z-test needs σ to be known. It also needs the sample mean to follow a normal curve, which holds when the population is normal or the sample is large enough for the Central limit theorem (n ≥ 30 is the usual rule of thumb). When σ is unknown, the test to use is the One-sample t-test and the t distribution, whatever the sample size.

Stating the hypotheses and the decision rule

The null hypothesis says the medication changes nothing: H₀: μ = 100. The alternative says the mean is different: H₁: μ ≠ 100. The question asks about a positive or a negative effect, so the test is two-tailed: a z far out on either side counts against H₀.

With α = 0.05 the confidence level is 1 − α = 95%. The 0.05 is split into 0.025 in each tail and 0.95 stays in the middle. The upper critical value is the z with 1 − 0.025 = 0.975 of the area to its left, which the z table gives as 1.96.

The two-tailed critical value at α = 0.05

The decision rule, fixed before computing anything: reject H₀ if z < −1.96 or z > 1.96; otherwise fail to reject H₀.

Five steps of the IQ z-test in a row: hypotheses H0 mu = 100 and H1 mu not 100, alpha 0.05 two-tailed, the rule reject if z is below -1.96 or above 1.96, the statistic z = 14.61, and the decision 14.61 > 1.96 so reject H0.

Computing the z statistic

The test statistic measures how far the sample mean is from μ₀ in units of the standard error σ/√n, the standard deviation of the sample mean. A mean of 30 values varies less than a single value, because Var(x̄) = σ²/n for independent draws. For one observation (n = 1) the formula becomes the ordinary Z-score and the standard normal distribution, (x − μ)/σ.

The one-sample z statistic
The IQ medication sample

Stating the decision

14.61 is far beyond 1.96, so we reject H₀. The data are strong evidence that the mean IQ of people who take the medication is not 100. The two-tailed test only says "different"; the sign of z gives the direction, and a positive z means the medication raised IQ.

The same decision comes from the P-value. For a two-tailed z-test p = 2 × (1 − Φ(|z|)), the area in both tails beyond the observed z. Here p is about 2.6 × 10⁻⁴⁸: if the true mean were 100, a sample of 30 with a mean at least 40 points away from 100 would essentially never occur. p ≤ α and |z| > 1.96 always agree.

The video leaves a homework: the same problem with a sample mean of 110. Then z = 10/(15/√30) = 3.65, which is still beyond 1.96, so H₀ is rejected again.

Running the z-test in Python

The z statistic from the board's numbers

python
import numpy as np
from scipy.stats import norm

mu0, sigma, n, xbar = 100, 15, 30, 140     # the board's problem
se = sigma / np.sqrt(n)                     # standard error of the mean
z = (xbar - mu0) / se

The critical value and the p-value

python
alpha = 0.05
z_crit = norm.ppf(1 - alpha / 2)            # 1.96 for a two-tailed test
p_value = 2 * norm.sf(abs(z))               # area in both tails beyond |z|
ExampleFrom the video, run on SciPy 1.18.1
for xbar in (140, 110):                     # the board's sample mean, then the homework
    z = (xbar - mu0) / se
    p_value = 2 * norm.sf(abs(z))
    decision = "reject H0" if p_value <= alpha else "fail to reject H0"
    print(f"x-bar {xbar}: z = {z:.2f}, critical ±{z_crit:.2f}, p = {p_value:.2g} -> {decision}")

Drawing the rejection regions

ExampleFrom the video's board, run on matplotlib 3.11.2
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

x = np.linspace(-4, 4, 400)
plt.figure(figsize=(8, 4))
plt.plot(x, norm.pdf(x), color="black")
for tail in (x <= -1.96, x >= 1.96):                 # the two rejection regions
    plt.fill_between(x[tail], norm.pdf(x[tail]), color="red", alpha=0.4)
plt.axvline(3.65, color="green", linestyle="--", label="homework z = 3.65")
plt.annotate("board z = 14.61\n(off the axis)", xy=(4, 0.01), xytext=(1.9, 0.3),
             arrowprops=dict(arrowstyle="->"))
plt.text(-0.3, 0.15, "0.95")
plt.text(-3.6, 0.06, "0.025")
plt.text(2.3, 0.06, "0.025")
plt.xticks([-4, -1.96, 0, 1.96, 4])
plt.title("Two-tailed z-test at α = 0.05: reject H₀ beyond ±1.96")
plt.xlabel("z")
plt.ylabel("density")
plt.legend(loc="upper left")
plt.show()
print("area in each red tail:", round(norm.sf(1.96), 4))
A standard normal curve with both tails beyond -1.96 and +1.96 shaded red, 0.025 each and 0.95 in the middle; a dashed green line marks the homework z = 3.65 inside the right tail and an arrow points off the axis to the board's z = 14.61.

Running a z-test on raw data with statsmodels

The video's notebook gives 20 patients' IQ scores, again from a population with μ = 100 and σ = 15, and calls ztest from statsmodels. Called with the hypothesized mean as the second argument, it fails:

ExampleFrom the video, run on statsmodels 0.15.0
from statsmodels.stats.weightstats import ztest

data = [88, 92, 94, 94, 96, 97, 97, 97, 99, 99,
        105, 109, 109, 109, 110, 112, 112, 113, 114, 115]
ztest(data, 100)

The second positional parameter of ztest is x2, a second sample for a two-sample test. The hypothesized mean goes in value=:

ExampleFrom the video, run on statsmodels 0.15.0
z, p = ztest(data, value=100)               # value= is the hypothesized mean
print("ztest, value=100:", round(z, 4), " p =", round(p, 4))
z, p = ztest(data, value=110)
print("ztest, value=110:", round(z, 4), " p =", round(p, 5))

Checking which standard deviation ztest uses

ztest never sees σ = 15. It estimates the standard deviation from the 20 values (with ddof = 1) and uses the normal distribution for the p-value, so it is a large-sample z-test. For a known-σ test, compute z yourself:

ExampleRun on SciPy 1.18.1
import numpy as np
from scipy.stats import norm, ttest_1samp

x = np.array(data)
s = x.std(ddof=1)                                    # what ztest divides by
z_known = (x.mean() - 100) / (15 / np.sqrt(len(x)))  # sigma = 15 is known
res = ttest_1samp(x, 100)
print("mean", x.mean(), " sample SD s =", round(s, 2), " sigma = 15")
print("known-sigma z =", round(z_known, 3), " p =", round(2 * norm.sf(abs(z_known)), 3))
print("t-test t =", round(res.statistic, 4), " p =", round(res.pvalue, 4), " df =", res.df)

What the three p-values say

  • ztest with value=100 gives z = 1.5976 and p = 0.1101. It divides by s = 8.54, the sample's own spread. p > 0.05, so we fail to reject H₀.
  • The known-σ z-test gives z = 0.909 and p = 0.363. If the true mean were 100 with σ = 15, a sample of 20 with a mean at least 3.05 points from 100 would turn up in about 36% of samples. We fail to reject H₀: no evidence that the drug changes IQ. That is not proof of no effect; the sample is small.
  • The t-test gives the same statistic as ztest, t = 1.5976, but p = 0.1266, because it uses the t distribution with 19 degrees of freedom. With σ unknown and n = 20, this is the textbook test.
  • ztest with value=110 gives p = 0.00027. That is below 0.05, so we reject H₀: μ = 110. The sample mean 103.05 is far below 110.

Z-test vs t-test

One-sample z-testOne-sample t-test
Standard deviationPopulation σ, knownSample s, estimated
Reference distributionStandard normalt with n − 1 degrees of freedom
Two-tailed critical value, α = 0.051.962.045 at df 29, 2.093 at df 19
In PythonBy hand with scipy.stats.norm; statsmodels ztest uses sscipy.stats.ttest_1samp
Use whenσ is known and the data are normal or n is largeσ is unknown

Where you use a one-sample z-test

  • Standardized scores: IQ and many exam scales are built with a published σ (15 for IQ), so a group's mean can be tested against the norm.
  • Quality control: a filling machine whose spread is known from long running checks whether its mean fill has drifted from the target.
  • Proportions and large samples: the Z-test for a proportion and tests on thousands of users in A/B experiments use the normal curve, because the standard error is known or estimated very precisely.
Watch out. statsmodels' ztest estimates the standard deviation from the data and never uses a known σ, and its second positional argument is a second sample. For a known-σ test compute z = (x̄ − μ₀)/(σ/√n) and p = 2·norm.sf(|z|) yourself; with σ unknown and a small sample, use ttest_1samp.
Try it yourself
  • Change the board's sample mean to 104. Is z = 1.46 inside ±1.96, and what does the test decide?
  • Set alpha = 0.01. The critical value becomes 2.58; does the homework's z = 3.65 still reject H₀?
  • Run ztest(data, value=100, alternative="larger") and compare its p-value with the two-sided 0.110.

Every expert started right here.