# Plot a standard normal distribution (mean = 0, SD = 1)
x <- seq(-4, 4, length = 500)
plot(x, dnorm(x, mean = 0, sd = 1),
type = "l", lwd = 2,
col = "#2C5F8A",
xlab = "Standard Deviations from Mean (z)",
ylab = "Probability Density",
main = "Standard Normal Distribution N(0, 1)")
# Shade the area within 1 SD of the mean
x_1sd <- seq(-1, 1, length = 200)
polygon(c(-1, x_1sd, 1), c(0, dnorm(x_1sd), 0),
col = "#2C5F8A40", border = NA)
abline(v = c(-1, 0, 1), lty = c(2, 1, 2), col = c("grey", "#C8102E", "grey"))
text(0, 0.15, "68%", col = "#1A3D5C", font = 2)Lab 15: The Normal Distribution & the Central Limit Theorem
BI 255 · Bethune-Cookman University · Fall 2026
The Most Important Distribution in Statistics
Biological measurements — enzyme reaction rates, cell sizes, body temperatures, blood glucose, leaf areas — tend to cluster around a central value with symmetric variation in both directions. This pattern is so pervasive that it has a name: the normal distribution. Understanding it is a prerequisite for almost every statistical test covered later in this course.
Equally important is the Central Limit Theorem, which explains why the normal distribution matters so much even when the underlying data are not themselves normally distributed.
By the end of this lab you will be able to:
- Describe the properties of the normal distribution
- Use
dnorm(),pnorm(),qnorm(), andrnorm()in R - Apply the 68-95-99.7 rule to biological problems
- Calculate and interpret z-scores
- State the Central Limit Theorem and demonstrate it by simulation
- Explain why the CLT underpins most parametric statistical tests
Part 1: Properties of the Normal Distribution
The normal (Gaussian) distribution is defined by two parameters:
- μ (mu) — the mean: where the distribution is centred
- σ (sigma) — the standard deviation: how spread out it is
- Symmetric — the left and right halves are mirror images
- Unimodal — one peak at the mean
- Mean = Median = Mode — all three are the same value
- Asymptotic tails — the curve never touches zero but extends infinitely in both directions
- Total area = 1 — the area under the curve represents the total probability
The 68-95-99.7 Rule
# Demonstrate the 68-95-99.7 rule numerically
cat("Area within 1 SD:", round(pnorm(1) - pnorm(-1), 4), "\n")Area within 1 SD: 0.6827
cat("Area within 2 SD:", round(pnorm(2) - pnorm(-2), 4), "\n")Area within 2 SD: 0.9545
cat("Area within 3 SD:", round(pnorm(3) - pnorm(-3), 4), "\n")Area within 3 SD: 0.9973
# Visualise all three
x <- seq(-4, 4, length = 500)
plot(x, dnorm(x), type = "l", lwd = 2, col = "#2C5F8A",
xlab = "z", ylab = "Density",
main = "The 68-95-99.7 Rule")
shade_region <- function(lo, hi, col) {
xs <- seq(lo, hi, length = 200)
polygon(c(lo, xs, hi), c(0, dnorm(xs), 0), col = col, border = NA)
}
shade_region(-3, 3, "#1A3D5C30")
shade_region(-2, 2, "#2C5F8A50")
shade_region(-1, 1, "#5B8DB880")
legend("topright",
legend = c("±1 SD (68%)", "±2 SD (95%)", "±3 SD (99.7%)"),
fill = c("#5B8DB880", "#2C5F8A50", "#1A3D5C30"),
bty = "n")Part 2: Working with the Normal Distribution in R
R provides four functions for the normal distribution. They all start with a letter that indicates what they compute:
| Function | What it computes | Input |
|---|---|---|
dnorm(x) |
Density at x (height of the curve) | x value |
pnorm(q) |
Probability ≤ q (area to the left) | quantile |
qnorm(p) |
Quantile for probability p | probability |
rnorm(n) |
Random sample of n values | sample size |
This pattern (d, p, q, r) applies to every distribution in R: dpois/ppois/qpois/rpois, dt/pt/qt/rt, etc.
Biological Example: Haemoglobin Concentration
Adult haemoglobin (Hgb) concentration in healthy males follows approximately N(μ = 15.5, σ = 1.2) g/dL.
mu_hgb <- 15.5
sd_hgb <- 1.2# What is the probability that a randomly selected male has Hgb < 13.5 g/dL (anaemia threshold)?
pnorm(13.5, mean = mu_hgb, sd = sd_hgb)[1] 0.04779035
# What Hgb value separates the lowest 5% of the population?
qnorm(0.05, mean = mu_hgb, sd = sd_hgb)[1] 13.52618
# What fraction of males have Hgb between 14 and 17?
pnorm(17, mu_hgb, sd_hgb) - pnorm(14, mu_hgb, sd_hgb)[1] 0.7887005
# Visualise the Hgb distribution with clinical thresholds marked
x_hgb <- seq(11, 20, length = 400)
plot(x_hgb, dnorm(x_hgb, mu_hgb, sd_hgb),
type = "l", lwd = 2, col = "#2C5F8A",
xlab = "Haemoglobin Concentration (g/dL)",
ylab = "Probability Density",
main = "Haemoglobin Distribution in Healthy Adult Males\nN(15.5, 1.2)")
# Shade anaemia region
x_shade <- seq(11, 13.5, length = 200)
polygon(c(11, x_shade, 13.5), c(0, dnorm(x_shade, mu_hgb, sd_hgb), 0),
col = "#C8102E50", border = NA)
abline(v = 13.5, col = "#C8102E", lty = 2, lwd = 2)
text(13.0, 0.25, "Anaemia\nthreshold\n(13.5)", col = "#C8102E", cex = 0.8, adj = 1)Part 3: Z-scores
A z-score transforms a raw measurement into the number of standard deviations it lies from the mean:
z = (x − μ) / σ
# A patient has Hgb = 13.0 g/dL. What is their z-score?
x_patient <- 13.0
z_score <- (x_patient - mu_hgb) / sd_hgb
cat("Z-score:", round(z_score, 3), "\n")Z-score: -2.083
cat("This patient is", abs(round(z_score, 2)), "standard deviations below the mean.\n")This patient is 2.08 standard deviations below the mean.
# Z-scores allow comparison across different scales
# E.g.: which is more unusual — Hgb of 13.0 or a fasting glucose of 60 mg/dL?
# (Fasting glucose in healthy adults: mean = 90, SD = 10 mg/dL)
z_hgb <- (13.0 - 15.5) / 1.2
z_glucose <- (60 - 90) / 10
cat("Hgb z-score: ", round(z_hgb, 2), "\n")Hgb z-score: -2.08
cat("Glucose z-score: ", round(z_glucose, 2), "\n")Glucose z-score: -3
cat("Glucose is more unusual (further from mean in SD units)\n")Glucose is more unusual (further from mean in SD units)
Part 4: The Central Limit Theorem
The Central Limit Theorem (CLT) is one of the most important results in all of statistics.
If you take repeated random samples of size n from any population (regardless of its shape), the distribution of the sample means will approach a normal distribution as n increases.
The distribution of sample means has: - Mean = μ (the population mean) - Standard deviation = σ / √n (called the standard error of the mean)
This is why the normal distribution appears so often in statistics — even non-normal data produces normally distributed means for large enough samples.
Demonstrating the CLT by Simulation
Let us start with a highly non-normal distribution — the exponential distribution, which is strongly right-skewed (like species abundances or time between rare events):
set.seed(42)
pop_size <- 100000
# Create a right-skewed population (exponential distribution)
population <- rexp(pop_size, rate = 0.2) # Mean = 1/rate = 5
par(mfrow = c(1, 2))
hist(population[1:500],
col = "#2C5F8A", border = "white",
xlab = "Value",
main = "Population Distribution\n(Exponential, right-skewed)",
breaks = 30)
# Draw 5000 samples of size 30 and record the sample mean each time
sample_means_30 <- replicate(5000, mean(sample(population, 30)))
hist(sample_means_30,
col = "#C8102E60", border = "white",
xlab = "Sample Mean",
main = "Distribution of Sample Means\n(n = 30, 5000 samples)")
curve(dnorm(x, mean(sample_means_30), sd(sample_means_30)) * 5000 * diff(range(sample_means_30)) / 30,
add = TRUE, col = "#1A3D5C", lwd = 2)par(mfrow = c(1, 1))# Effect of sample size on normality of the sampling distribution
par(mfrow = c(1, 3))
for (n in c(5, 15, 50)) {
sm <- replicate(5000, mean(sample(population, n)))
hist(sm,
col = "#2C5F8A60", border = "white",
main = paste0("n = ", n),
xlab = "Sample Mean",
breaks = 25)
}par(mfrow = c(1, 1))By n = 30, the distribution of sample means is approximately normal even though the original data are exponential. This is why:
- t-tests work even when your raw data are not perfectly normal (for n ≥ 20–30)
- Means and confidence intervals are valid summaries for most biological data
- The standard error (SE = SD / √n) decreases with sample size — more samples give a more precise estimate of the true mean
The CLT is the mathematical justification for almost every parametric test you will use in biology.
Part 5: Standard Error vs Standard Deviation
# Demonstrate how SE decreases with n
set.seed(99)
ns <- c(5, 10, 20, 50, 100, 200)
results <- sapply(ns, function(n) {
sample_means <- replicate(2000, mean(rnorm(n, mean = 15.5, sd = 1.2)))
c(SD_of_means = sd(sample_means),
Theoretical_SE = 1.2 / sqrt(n))
})
colnames(results) <- ns
round(results, 4) 5 10 20 50 100 200
SD_of_means 0.5520 0.3824 0.2660 0.1712 0.1219 0.0852
Theoretical_SE 0.5367 0.3795 0.2683 0.1697 0.1200 0.0849
plot(ns, results["SD_of_means", ],
type = "b", pch = 16, col = "#2C5F8A",
xlab = "Sample Size (n)",
ylab = "Standard Error of the Mean",
main = "SE Decreases with Sample Size\n(Haemoglobin example, sigma = 1.2)")
lines(ns, results["Theoretical_SE", ],
col = "#C8102E", lty = 2, lwd = 2)
legend("topright",
legend = c("Simulated SE", "Theoretical SE = sigma/sqrt(n)"),
col = c("#2C5F8A", "#C8102E"),
lty = c(1, 2), pch = c(16, NA))3-Minute Knowledge Check
Close your notes. Answer these on your own — you have 3 minutes. We’ll go through the answers together after.
1. Blood pressure in a healthy adult population is approximately N(120, 15) mmHg. What R function and arguments give you the probability of a randomly selected person having blood pressure below 100?
2. Using the same distribution, what blood pressure value separates the highest 10% of the population from the rest?
3. A cell culture experiment measures protein yield. One sample gives 42 mg/L in a population with mean 50 mg/L and SD 8 mg/L. What is the z-score for this sample?
a) −0.9 b) −1.0 c) +1.0 d) +0.9
4. The Central Limit Theorem states that the distribution of sample means will be approximately normal when:
a) The original population is normally distributed b) Sample size is sufficiently large, regardless of population shape c) The variance is small d) All observations are independent
5. True or False: The standard error of the mean equals the sample standard deviation divided by the square root of the sample size.
1. pnorm(100, mean = 120, sd = 15) — pnorm gives the probability of a value at or below q. Here q = 100, mean = 120, sd = 15. The answer is approximately 0.091 (about 9.1% of healthy adults have BP below 100).
2. qnorm(0.90, mean = 120, sd = 15) — qnorm gives the value at a given cumulative probability. The 90th percentile gives the value above which the top 10% lie. Answer ≈ 139.2 mmHg.
3. b) −1.0 — z = (42 − 50) / 8 = −8 / 8 = −1.0. This sample is exactly one standard deviation below the mean.
4. b) Sample size is sufficiently large, regardless of population shape — this is the essence of the CLT. The population can be skewed, bimodal, or any shape. For n ≥ 30, the sampling distribution of the mean is approximately normal.
5. True — SE = SD / √n. This is both the theoretical result from the CLT and what you observe empirically when you simulate repeated sampling. As n grows, SE shrinks, making your estimate of the mean more precise.
Lab 15 Checklist
Before you leave, make sure you can:
The serum ferritin concentration in adult women follows a right-skewed distribution (not normal) with mean 45 ng/mL and SD 35 ng/mL. Simulate a population of 50,000 values using rlnorm() (a log-normal distribution is a good approximation). Then demonstrate the CLT by drawing 3,000 samples of size 40 and plotting the distribution of sample means. Does the result look normal? Calculate the theoretical SE and compare it to the standard deviation of your sample means.
Before Next Class (Wednesday)
- Wednesday (Lab 16): Assessing normality formally — QQ plots, Shapiro-Wilk test, and what to do when data are not normal
- Read R for Data Science Ch. 9–10
- Quiz 4 is this Friday (Lab 15 and 16 content)