One-Sample Z-Test
The one-sample z-test tests whether a population mean equals a hypothesized value when the population standard deviation (σ) is known and the sample is large (n ≥ 30).
Assumptions
- Data is from a random sample
- Population standard deviation σ is known
- Either: population is normal, OR n ≥ 30 (CLT applies)
- Observations are independent
Step-by-Step Procedure
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
# Scenario: IQ scores. Population σ = 15 (known).
# We sample 50 students from a school. Do they differ from national μ₀ = 100?
np.random.seed(42)
sigma = 15 # known population std dev
mu_0 = 100 # null hypothesis mean
alpha = 0.05
n = 50
sample = np.random.normal(105, sigma, n) # true mean is 105
x_bar = sample.mean()
# Step 1: State hypotheses
print("H₀: μ = 100 (school same as national average)")
print("H₁: μ ≠ 100 (two-tailed)")
print()
# Step 2: Compute test statistic
z = (x_bar - mu_0) / (sigma / np.sqrt(n))
print(f"x̄ = {x_bar:.4f}")
print(f"z = ({x_bar:.4f} - {mu_0}) / ({sigma}/√{n}) = {z:.4f}")
# Step 3: Critical value and rejection region
z_crit = stats.norm.ppf(1 - alpha/2) # two-tailed
print(f"\nα = {alpha}, critical value z* = ±{z_crit:.4f}")
print(f"Reject H₀ if |z| > {z_crit:.4f}")
# Step 4: P-value
p_value = 2 * stats.norm.sf(abs(z))
print(f"p-value = {p_value:.6f}")
# Step 5: Decision
print(f"\n|z| = {abs(z):.4f} {'>' if abs(z) > z_crit else '≤'} {z_crit:.4f}")
print(f"p = {p_value:.4f} {'<' if p_value < alpha else '≥'} α = {alpha}")
print(f"Decision: {'Reject H₀' if p_value < alpha else 'Fail to reject H₀'}")
if p_value < alpha:
print("Conclusion: Significant evidence students differ from national average")
# Step 6: Confidence interval
se = sigma / np.sqrt(n)
ci_lower = x_bar - z_crit * se
ci_upper = x_bar + z_crit * se
print(f"\n95% CI: ({ci_lower:.4f}, {ci_upper:.4f})")
print(f"Since 100 is {'NOT ' if not (ci_lower <= 100 <= ci_upper) else ''}in CI → {'Reject' if not (ci_lower <= 100 <= ci_upper) else 'Fail to reject'} H₀")
Critical Region Visualization
x = np.linspace(-4, 4, 1000)
y = stats.norm.pdf(x)
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(x, y, 'b-', linewidth=2)
# Rejection regions (two-tailed, α=0.05)
ax.fill_between(x, y, where=x <= -z_crit, alpha=0.4, color='red', label=f'Reject H₀ (each α/2={alpha/2})')
ax.fill_between(x, y, where=x >= z_crit, alpha=0.4, color='red')
ax.fill_between(x, y, where=(-z_crit <= x) & (x <= z_crit), alpha=0.2, color='blue', label='Fail to reject H₀')
ax.axvline(z, color='green', linewidth=2, linestyle='--', label=f'Observed z = {z:.3f}')
ax.axvline(-z_crit, color='black', linewidth=1.5, linestyle=':')
ax.axvline(z_crit, color='black', linewidth=1.5, linestyle=':', label=f'z* = ±{z_crit:.3f}')
ax.set_title(f'One-Sample Z-Test (Two-Tailed, α={alpha})')
ax.set_xlabel('z')
ax.set_ylabel('Density')
ax.legend()
plt.tight_layout()
plt.savefig('z_test.png', dpi=150)
plt.show()
One-Tailed Tests
# Left-tailed: H₁: μ < μ₀
def z_test(sample, mu_0, sigma, alternative='two-sided'):
n = len(sample)
x_bar = sample.mean()
z = (x_bar - mu_0) / (sigma / np.sqrt(n))
if alternative == 'two-sided':
p = 2 * stats.norm.sf(abs(z))
elif alternative == 'less':
p = stats.norm.cdf(z)
elif alternative == 'greater':
p = stats.norm.sf(z)
return z, p
z_stat, p_two = z_test(sample, 100, 15, 'two-sided')
z_stat, p_less = z_test(sample, 100, 15, 'less')
z_stat, p_greater = z_test(sample, 100, 15, 'greater')
print(f"z = {z_stat:.4f}")
print(f"p (two-sided): {p_two:.6f}")
print(f"p (H₁: μ<100): {p_less:.6f}")
print(f"p (H₁: μ>100): {p_greater:.6f}")
When Z-test vs T-test
| Condition | Use |
|---|---|
| σ known AND (n≥30 OR normal population) | Z-test |
| σ unknown (typical in practice) | T-test |
| n < 30 AND σ unknown | T-test |
| Proportion data | Z-test for proportions |
In practice: σ is almost never truly known → use the t-test.
Key Takeaways
- Z-test requires known σ — rare in practice; use t-test otherwise
- Test statistic z measures how many standard errors x̄ is from μ₀
- |z| > 1.96 → reject H₀ at α = 0.05 (two-tailed) — memorize this
- Confidence interval and hypothesis test are equivalent — CI excludes μ₀ iff p < α
- Effect size (d = (x̄ − μ₀)/σ) should accompany the z-statistic