Probability Distributions

Reviewed & published by Brayan K

R provides every common probability distribution through one consistent system: a distribution name (norm, binom, pois…) prefixed by d, p, q, or r to ask for a density, a cumulative probability, a quantile, or random draws.

Part of the free R course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

In this lesson you'll master the d/p/q/r prefix system with the normal distribution, branch out to runif/rbinom/rpois, lock down reproducibility with set.seed(), and draw from explicit sets with sample().

What You'll Learn in This Lesson

1️⃣ The d / p / q / r System

Learn one distribution's four functions and you've learned them all. For the normal distribution, the suffix is norm, and the prefix selects the question. The three deterministic ones below give the same answer on every machine.

# Every distribution in R shares a 4-FUNCTION naming system. Take the
# normal distribution ("norm") and bolt one of four prefixes on front:
#
#   d -> DENSITY        height of the curve at a value
#   p -> PROBABILITY    cumulative area to the LEFT (a percentile)
#   q -> QUANTILE       the inverse of p: value at a given percentile
#   r -> RANDOM         draw random numbers from the distribution

dnorm(0)        # density of standard normal at 0
pnorm(0)        # P(X <= 0) for standard normal
qnorm(0.975)    # the value with 97.5% of area to its left

Add mean and sd to describe any normal. Here's a worked "heights" example that answers real probability questions exactly.

# d/p/q all accept mean and sd so you can describe ANY normal, not just
# the standard one. Suppose adult heights are Normal(mean=170, sd=10) cm.

# What fraction of people are SHORTER than 180 cm?  (a 'p' question)
pnorm(180, mean = 170, sd = 10)        # -> 0.8413447

# What height is the 90th percentile?               (a 'q' question)
qnorm(0.90, mean = 170, sd = 10)       # -> 182.8155

# What fraction are BETWEEN 160 and 180 cm? Subtract two p-values:
pnorm(180, 170, 10) - pnorm(160, 170, 10)   # -> 0.6826895

2️⃣ Beyond the Normal

The same four-letter pattern covers the binomial, Poisson, uniform, exponential, t, chi-squared, and more — just change the suffix and supply that distribution's parameters. These density/probability calls are exact, so their output is stable across machines.

# The same prefix system covers many distributions. Swap the suffix:
#
#   unif  -> Uniform        runif(n, min, max)
#   binom -> Binomial       rbinom(n, size, prob)
#   pois  -> Poisson        rpois(n, lambda)
#   exp, gamma, beta, t, chisq, ... and more.

# Probability of exactly 3 heads in 10 fair coin flips (a 'd' on binom):
dbinom(3, size = 10, prob = 0.5)       # -> 0.1171875

# Probability of AT MOST 2 calls/hour when the mean rate is 4 (Poisson):
ppois(2, lambda = 4)                   # -> 0.2381033

# The d/p/q functions are exact maths, so these numbers are stable
# across machines and R versions — only the r* draws below vary.

Reading them: dbinom(3, 10, 0.5) is the probability of exactly 3 successes; ppois(2, 4) is the probability of at most 2 events. Use d for "exactly," p for "at most / less than."

3️⃣ Random Draws & set.seed()

The r* functions simulate data. Because they use R's pseudo-random generator, every run differs — until you fix the starting state with set.seed(). We omit the printed numbers here because exact draws depend on your R version, but the code is reproducible on a given machine.

# The r* functions DRAW random numbers. They use R's RNG, so results
# differ every run UNLESS you fix the seed with set.seed() first.
# NOTE: the exact draws can also differ between R VERSIONS (the default
# RNG changed in R 3.6), so treat printed draws as illustrative.

set.seed(123)
rnorm(5, mean = 0, sd = 1)     # 5 standard-normal draws
runif(3, min = 0, max = 10)    # 3 uniform draws in [0, 10]
rbinom(4, size = 1, prob = 0.5) # 4 coin flips (0/1)
rpois(3, lambda = 2)           # 3 Poisson counts

# Run it: with set.seed(123) the SAME machine reproduces the SAME
# numbers every time. Remove set.seed() and they change each run.

sample() is different: it draws from a set you provide — perfect for dice, cards, shuffling, and bootstrapping. Set replace = TRUE to allow repeats.

# sample() draws from a set you supply (not a named distribution).
set.seed(7)

sample(1:6, 1)                 # one roll of a die
sample(1:6, 10, replace = TRUE)  # ten rolls (replace = repeats allowed)
sample(c("H", "T"), 5, replace = TRUE)  # five coin flips
sample(letters, 3)             # 3 distinct letters, no replacement

# Shuffle a vector by sampling it at full length without replacement:
sample(1:5)                    # a random permutation of 1..5

# Reproducible because of set.seed(7); exact values are R-version
# dependent, so run it yourself to see your machine's draws.

Your turn. Fill in the ___ blank and run it — the deterministic p/q answers are shown.

# 🎯 YOUR TURN — replace each ___ then run it.
# Exam scores are Normal(mean = 75, sd = 8).

# 1) What fraction of students score BELOW 60? (a 'p' question)
below60 <- pnorm(60, mean = 75, sd = 8)
cat("P(score < 60) =", round(below60, 4), "\n")   # 0.0304

# 2) What is the 95th-percentile score? (a 'q' question)
top5 <- qnorm(0.95, mean = 75, sd = ___)   # 👉 replace ___ with 8
cat("95th percentile =", round(top5, 2), "\n")    # 88.16

# 3) Simulate 1000 scores and check the sample mean is near 75.
set.seed(2024)
scores <- rnorm(1000, mean = 75, sd = 8)
cat("Simulated mean =", round(mean(scores), 2), "\n")  # close to 75

Mini-Challenge: Simulate a Dice Game

Compare simulation against exact theory — the heart of Monte Carlo thinking. Simulate thousands of games, measure the empirical win rate, and check it against the 8/36 truth.

# 🎯 MINI-CHALLENGE: simulate a casino game.
#
# A simple game: roll two fair dice; you WIN if the total is 7 or 11.
#
# 1. Use set.seed() for reproducibility.
# 2. Simulate 10,000 games. For each, sample() two dice (1:6 with
#    replacement) and add them — or draw both columns at once.
# 3. Compute the EMPIRICAL win rate (proportion of totals in {7, 11}).
# 4. Compare it to the EXACT theoretical probability: there are 6 ways
#    to make 7 and 2 ways to make 11 out of 36 -> 8/36 = 0.2222.
# 5. Print both and confirm the simulation is close.
#
# Write it yourself, then run in RStudio or rdrr.io/snippets.

# your code here

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Distributions

TaskR Syntax
Density at a pointdnorm(x, mean, sd)
P(X ≤ x)pnorm(x, mean, sd)
Percentile valueqnorm(p, mean, sd)
Random normalsrnorm(n, mean, sd)
Reproducible drawsset.seed(123)
Draw from a setsample(1:6, 10, replace = TRUE)

🎉 Lesson Complete!

Practice quiz

In R's distribution naming, what does the p prefix compute?

  • The density at a point
  • A random draw
  • The cumulative probability (area to the left)
  • The quantile

Answer: The cumulative probability (area to the left). p gives P(X <= x), the cumulative probability up to a value.

What does the q prefix (as in qnorm) return?

  • The value at a given percentile
  • A probability
  • A density height
  • A random number

Answer: The value at a given percentile. q is the inverse of p: give it a probability, it returns the value.

What does dnorm() give you?

  • A cumulative probability
  • A random draw
  • A percentile rank
  • The density (curve height) at a point

Answer: The density (curve height) at a point. d gives density, not a probability; it can even exceed 1.

Which function draws random numbers from a normal distribution?

  • pnorm()
  • rnorm()
  • qnorm()
  • dnorm()

Answer: rnorm(). The r prefix generates random draws from the distribution.

What does set.seed(123) accomplish?

  • Makes subsequent random draws reproducible
  • Speeds up the r* functions
  • Sets the mean to 123
  • Draws 123 numbers

Answer: Makes subsequent random draws reproducible. Fixing the RNG seed reproduces the same draws on the same R version.

pnorm and qnorm are related how?

  • They are unrelated
  • They both return densities
  • They are exact inverses of each other
  • qnorm always doubles pnorm

Answer: They are exact inverses of each other. qnorm(pnorm(x)) == x; one maps value->prob, the other prob->value.

What does dbinom(3, size = 10, prob = 0.5) compute?

  • P(at most 3 successes)
  • P(exactly 3 successes in 10 trials)
  • The mean number of successes
  • A random number of successes

Answer: P(exactly 3 successes in 10 trials). d on a discrete distribution gives the probability of exactly that count.

What does ppois(2, lambda = 4) give?

  • P(exactly 2 events)
  • The mean 4
  • A random Poisson count
  • P(at most 2 events) when the mean rate is 4

Answer: P(at most 2 events) when the mean rate is 4. p is cumulative: probability of 2 or fewer events.

Why does sample(1:6, 10) without replace = TRUE error?

  • 10 is too large a number
  • You can't draw 10 distinct values from only 6
  • 1:6 is not a vector
  • sample needs a probability

Answer: You can't draw 10 distinct values from only 6. Without replacement you can draw at most 6; add replace = TRUE for repeats.

qnorm() expects its first argument to be...

  • A raw data value like 95
  • A count of trials
  • A probability between 0 and 1
  • A standard deviation

Answer: A probability between 0 and 1. q takes a probability in [0, 1]; pass 0.95, not 95.

Continue this course

Frequently asked questions

What do the d, p, q, and r prefixes actually mean?

They pick which question you're asking about a distribution. d (density) gives the height of the probability curve at a point — for continuous variables this is a density, not a probability. p (probability) gives the cumulative probability, the area to the LEFT of a value, i.e. a percentile. q (quantile) is the inverse of p: give it a probability and it returns the value at that percentile. r (random) generates random draws. Memorise these four and you instantly know dnorm/pnorm/qnorm/rnorm, dbinom/pbinom/..., dpois/ppois/..., and dozens more.

Why do I get different random numbers every time, and how do I stop that?

The r* functions and sample() draw from R's pseudo-random number generator, whose state advances on each call — so every run gives fresh numbers. Call set.seed(some_integer) immediately before the random step to fix the starting state; then the same code reproduces the same draws every time on the same R version. This is essential for reproducible analyses, debugging, and teaching. One caveat: R changed its default sampling algorithm in version 3.6, so identical seeds can yield different draws across major R versions.

What's the difference between pnorm and dnorm?

dnorm gives the density — the height of the bell curve at a single point — which on its own isn't a probability and can even exceed 1 for narrow distributions. pnorm gives a genuine probability: the total area under the curve up to that point, i.e. P(X <= value). When you want 'what fraction is below x' use pnorm; when you want the relative likelihood at exactly x (mostly for plotting the curve or likelihood calculations) use dnorm. For the probability of a RANGE, subtract two pnorm values.

How is sample() different from the r* functions?

The r* functions draw from a named theoretical distribution defined by parameters — rnorm needs a mean and sd, rbinom needs size and prob. sample() instead draws from a finite set of values you hand it, with or without replacement, optionally with custom probabilities via the prob argument. Use rbinom/rpois/rnorm to model a known distribution; use sample() to shuffle, bootstrap (resample your data with replacement), draw a random subset, or simulate dice and cards from an explicit set of outcomes.