Menu

Random Numbers in R: rnorm, runif, sample and set.seed

Generate random data with rnorm(), runif(), rbinom() and sample(), make it reproducible with set.seed(), and decode R's d/p/q/r naming system for distributions.

This page includes runnable editors - edit, run, and see output instantly.

set.seed() Comes First

Random draws that change on every run are useless for teaching, grading, debugging, or science - if your simulation says 0.146 today and 0.153 tomorrow, which number goes in the report? set.seed() fixes the generator's starting point, making the entire stream of "randomness" that follows exactly repeatable:

Same seed, identical draws, every time, on every machine. The numbers are pseudo-random: a deterministic sequence engineered to pass every statistical test of randomness, with the seed choosing where in the sequence you start. The seed value itself carries no meaning - 42, 7, 20260807 - pick anything; just write it down. The habit to build: one set.seed() at the top of any script that uses randomness. (Note that consuming draws advances the state, so the order of calls matters to reproducibility too.)

The d/p/q/r System: One Table Decodes the Whole Library

R names every distribution function as prefix + family, and once you see the grid you can read the entire stats library:

PrefixQuestion it answersNormal example
rGive me random drawsrnorm(5)
dDensity: how high is the curve at x?dnorm(0)
pProbability: P(X ≤ x)?pnorm(1.96)
qQuantile: which x sits at this percentile?qnorm(0.975)

p and q are inverses, and they're the pair you met in confidence intervals as qt(0.975, df):

Swap the family name and everything carries over: runif/dunif/punif/qunif, rbinom/..., rpois/..., rt/..., rexp/.... Learn four prefixes, get dozens of distributions.

rnorm(): Normal Draws

rnorm(n, mean, sd) draws from a bell curve - the workhorse for simulating measurement-like data:

The sample mean and sd land near - not on - 100 and 15: that gap is sampling noise, shrinking as n grows. The last line is a trick worth stealing: mean() of a logical vector is the proportion of TRUEs, and the share beyond 130 comes out near the theoretical 1 - pnorm(130, 100, 15) ≈ 2.3%. Defaults are mean = 0, sd = 1 (the standard normal). A histogram of iq shows the familiar bell.

runif(), rbinom(), rpois()

Three more families cover most simulation needs:

runif() spreads draws evenly across a range ("uniform," not "run if" - everyone misreads it once). rbinom(n, size, prob) simulates n experiments of size trials each, returning the success count of each - so each value above is heads out of 10 flips. rpois(n, lambda) generates counts of events that occur independently at a known average rate: support tickets per hour, typos per page.

sample(): Sampling and Shuffling

Where the r* functions invent values from a distribution, sample() draws from values you already have:

The replace argument is the whole story: FALSE (default) deals cards - each value can appear once, and asking for more than you have is an error; TRUE rolls dice - every draw resets. Sampling with replacement from your own data is the engine of bootstrapping. Called with just a vector, sample(x) returns a random permutation - the idiom for shuffling.

Sampling rows of a data frame uses sample() inside row indexing:

sample(nrow(mtcars), 5) picks 5 random row numbers; the indexing pulls those rows. This is the standard move for spot-checking a big dataset or splitting train/test sets.

A Mini Monte Carlo Simulation

Here's the payoff of the whole toolkit. Question: a process yields measurements distributed N(100, 15); you average 10 of them - what's the probability that average exceeds 105? Instead of deriving the answer, simulate it - do the experiment 10,000 times and count:

The simulation lands within a few thousandths of the exact value (about 0.146). That's Monte Carlo in one breath: write one trial as a function, replicate() it thousands of times, take mean() of the successes. The exact answer existed here because the setup was textbook-simple - the moment the question gets messy (weird distributions, max of correlated draws, a rule-based game), the analytic route closes and the simulation recipe keeps working unchanged. Notice the 15 / sqrt(10) in the check: means wobble less than individual draws - the same square-root law that drives confidence interval widths.

Simulation-Grade, Not Secret-Grade

One boundary to respect: R's default generator (Mersenne Twister) is built for statistical quality and speed, not secrecy. Its output is deterministic given the seed and its internal state can be reconstructed from observed output - fatal flaws for passwords, tokens, or anything security-adjacent. For simulation, bootstrapping, and teaching it's excellent; for cryptography, use a purpose-built library (e.g. the openssl package), never sample() or runif().

What You Take Away

  • set.seed() once at the top makes every "random" result reproducible - same seed, same draws.
  • The d/p/q/r prefixes decode the whole distribution library: random draw, density, cumulative probability, quantile.
  • rnorm(n, mean, sd), runif(n, min, max), rbinom(n, size, prob), rpois(n, lambda) cover most simulation needs.
  • sample() draws from your own values - replace = TRUE for dice, default for cards, no size to shuffle; df[sample(nrow(df), k), ] samples rows.
  • Monte Carlo = one-trial function + replicate() + mean() - the recipe that answers probability questions math can't conveniently reach.
  • R's RNG is for simulation, not cryptography.

Next up: debugging - what R's error messages actually mean and how to read a traceback.

Frequently Asked Questions

What does set.seed() do in R?

It fixes the starting point of R's random number generator, so the "random" draws that follow come out identical every run. Call it once at the top of any script that uses randomness - set.seed(42) - and your simulation becomes reproducible: colleagues, graders, and future you all see the same numbers.

How do you generate random numbers in R?

Pick the distribution: rnorm(n, mean, sd) for normal draws, runif(n, min, max) for uniform, rbinom(n, size, prob) for counts of successes, rpois(n, lambda) for event counts. For sampling from existing values, use sample(x, size).

What is the difference between rnorm, dnorm, pnorm and qnorm?

One distribution, four prefixes: r draws random values, d gives the density curve's height, p gives the cumulative probability P(X ≤ x), and q is its inverse - the value at a given percentile. The same four prefixes work for every distribution R knows: runif/dunif/punif/qunif, rbinom/dbinom/pbinom/qbinom, and so on.

How do you take a random sample of rows from a data frame in R?

Index the rows with sample(): df[sample(nrow(df), 5), ] picks 5 rows without replacement. Add replace = TRUE for sampling with replacement (the move behind bootstrapping).

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED