R Simulation and Random Sampling

Context: FIT2086_MOC · generating random data and evaluating distributions in R · the applied engine behind simulation, the bootstrap and Monte Carlo (LO5) · extends R Toolkit (Cheatsheet) Problem it solves: draw random samples, reproduce them, and compute density / probability / quantile / random values for a named distribution.

Quick Revision

  • 🎯 Trigger: “simulate”, “sample”, “draw from a distribution”, “reproducible random” ➔ set.seed + sample for finite sets, and the d/p/q/r family for named distributions.
  • ⚡ Key Constraint: the first letter selects the operation — d = density/pmf, p = CDF (probability ≤), q = quantile (inverse CDF), r = random draws. Mixing them up is the classic error.

🎲 Reproducibility with set.seed

set.seed(1); rnorm(5)   # a fixed sequence of 5 normals
rnorm(5)                # DIFFERENT next 5 (generator advanced)
set.seed(1); rnorm(5)   # IDENTICAL to the first call — seed reset

Expected output: the two set.seed(1) calls give byte-identical vectors; the middle call differs.

  • Why ➔ R’s random numbers are pseudo-random: a fixed seed makes an experiment reproducible (essential for assignments and debugging).

🃏 sample() — drawing from a finite set

sample(1:10, 4)                 # 4 distinct values, WITHOUT replacement (default)
sample(letters, 5)              # 5 distinct lowercase letters
sample(1:10)                    # a random PERMUTATION of 1..10 (size omitted = all)
sample(1:10, 4, replace = TRUE) # WITH replacement — repeats allowed
sample(1:6, 10, replace = TRUE) # 10 dice rolls (must use replace here — n>set size)
  • Default is no replacement ➔ so size cannot exceed the set unless replace = TRUE.
  • Omit size ➔ returns a full random permutation.

📊 The d / p / q / r family (per distribution)

Each named distribution has four functions sharing a suffix — shown here for the normal (norm):

PrefixFunctionReturnsNormal call
ddensity (pmf/pdf) height of the curve at xdnorm(x, mean=0, sd=1)
pprobability (CDF) lower-tail probabilitypnorm(q, mean=0, sd=1, lower.tail=TRUE)
qquantile the value with p below itqnorm(p, mean=0, sd=1)
rrandom drawsa vector of n samplesrnorm(n, mean=0, sd=1)
  • Same pattern, other distributions*binom (args size, prob), *pois (lambda), *unif (min, max), *exp (rate). E.g. rbinom(10, size=1, prob=0.3), dpois(2, lambda=4), runif(5).
  • p and q are inversespnorm(qnorm(0.975)) == 0.975; use lower.tail=FALSE for upper-tail .

🔧 Minimal working example — simulate + visualise

set.seed(20)
x <- rnorm(1000, mean = 5, sd = 2)   # 1000 draws from N(5, 4)
mean(x); sd(x)                       # ≈ 5 and ≈ 2 (sampling error)
hist(x, breaks = 30)                 # empirical distribution
curve(dnorm(x, 5, 2), add = TRUE, col = "red")  # true density overlaid

Expected output: a bell-shaped histogram with the red theoretical density tracking it; sample mean/sd close to 5/2. (plotting: see R Visualisation (base graphics).)

📈 Simulating the WLLN — the running-mean convergence plot (Studio 2)

The running mean for makes the WLLN visible: plot it against and watch it settle onto the population mean.

runningmean <- function(x) {
  mu <- vector(mode = "numeric", length = length(x))  # preallocate — avoids O(n^2) regrowth
  S  <- 0
  for (i in 1:length(x)) {
    S     <- S + x[i]          # ONE running accumulator ...
    mu[i] <- S / i             # ... so the whole vector costs O(n), not O(n^2)
  }
  return(mu)
}
 
# reference line = the population mean, then overlay three independent runs
plot(1:1000, rep(0, 1000), col = "black", type = "l",
     xlab = "Samples", ylab = "Sample mean")
lines(1:1000, runningmean(rnorm(1000, 0, 1)), col = "red")
lines(1:1000, runningmean(rnorm(1000, 0, 1)), col = "green")
lines(1:1000, runningmean(rnorm(1000, 0, 1)), col = "blue")
 
# Bernoulli version: ylim pins the axis to the parameter space [0, 1]
plot(1:1000, rep(1/2, 1000), col = "black", type = "l", ylim = c(0, 1),
     xlab = "Samples", ylab = "Sample mean")
lines(1:1000, rep(0.9, 1000), col = "black")
lines(1:1000, runningmean(rbinom(1000, 1, 0.5)), col = "red")
lines(1:1000, runningmean(rbinom(1000, 1, 0.9)), col = "blue")

Expected output: every curve is wild for small and tightens onto its flat reference line as grows. The curve hugs its line visibly sooner and tighter than the curve.

  • Why the two Bernoulli curves differ, and against less variable data converges faster; is the maximum-variance Bernoulli.
  • rep(c, n) builds the reference line ➔ a constant vector of length is how you draw a horizontal line with plot/lines.
  • ylim = c(0,1) ➔ set on the first plot() call; passing it to lines() is silently ignored.

✍️ Practice

⚠️ Common Mistakes

  • 💡 d/p/q/r mix-uprnorm draws samples; dnorm gives density heights (not probabilities); pnorm gives ; qnorm inverts it. Reaching for the wrong prefix is the top mistake.
  • 💡 sample without replace caps at set sizesample(1:6, 10) errors; you need replace = TRUE whenever size exceeds the population.
  • 💡 Seed placement mattersset.seed() must precede each block you want reproducible; the generator advances with every draw.
  • 💡 rbinom’s n is not the binomial’s ➔ in rbinom(n, size, prob) the first argument is how many variates to generate and size is the binomial’s number of trials ➔ rbinom(10, 5, 0.25) returns 10 draws from , not one draw from .
  • 💡 Growing a vector inside a loopmu <- c(mu, val) reallocates every iteration (); vector(mode="numeric", length=n) preallocates and keeps the running mean .
  • 💡 A density is not a probabilitydnorm(0) ≈ 0.399, not a probability — consistent with continuous densities where only integrals (via pnorm) are probabilities.

🧠 Active Recall