1Objectives¶
By the end of this lesson you will be able to:
Explain why
set.seed()makes a “random” simulation repeatable, and use it before any function that involves chance (likedo() *, previewed in Lesson 8).Organize a script or notebook so it runs cleanly from top to bottom in a fresh session — not just piece by piece as you wrote it.
Write comments that explain why, not just restate what a line does.
Report your R and package versions with
sessionInfo(), and explain why that matters if you ever ask someone for help.
None of this is new statistics. It’s the small set of habits that make the difference between “it worked on my computer” and “it works, full stop” — and every example, table, and figure in this entire book was produced by following exactly these habits.
2Habit 1: set.seed() before anything random¶
R’s “random” numbers are actually pseudo-random: generated by a
predictable formula that looks random, starting from a starting point
called a seed. Leave the seed unset, and R picks a new starting point
every time, so you get different numbers each run. Set the seed yourself with
set.seed(), and you get the exact same “random” numbers every time — which
is essential for a reproducible script, an answer key, or a homework solution
someone else needs to check.
Here’s the difference, run for real. First, with no seed set, calling
sample() (which picks random values) twice in the same session:
library(mosaic)
library(BSDA)
sample(1:20, 5)[1] 16 19 14 15 2sample(1:20, 5)[1] 9 3 6 11 19Two different draws of 5 numbers — exactly what “random” should do. Now watch
what set.seed(1209) does, run right before the same sample() call, twice,
in two completely separate script runs:
set.seed(1209)
sample(1:20, 5)[1] 13 4 5 17 16set.seed(1209)
sample(1:20, 5)[1] 13 4 5 17 16Identical, both times — because set.seed(1209) resets R’s random-number
generator to the exact same starting point right before the draw. The number
1209 itself isn’t special (this book uses it because it’s the course
number); any whole number works as a seed, and different seeds give different
(but each individually reproducible) sequences.
Lesson 8 will introduce do(n) *, which repeats a random process
n times to build a simulation — the single place in this course where a
seed matters most, because a simulation is a big pile of random draws. Here
is a small real preview, so you can see the seed rule apply to it exactly the
same way: do(3) * rflip(10) simulates flipping 10 coins, three separate
times.
set.seed(1209)
do(3) * rflip(10) n heads tails prop
1 10 5 5 0.5
2 10 3 7 0.3
3 10 4 6 0.4Run that exact code again, anywhere, with the same seed, and you will get the same three rows back — 5, 3, and 4 heads out of 10, in that order. You’ll learn what each of those columns means properly in Lesson 8; for now, notice only that the seed rule you just learned already applies to it.
3Habit 2: write a script, run it top to bottom, in a fresh session¶
It’s tempting to build up an analysis by typing one line into the console, checking the answer, typing the next line, and so on — and that’s a fine way to explore. But the console remembers everything you’ve typed in whatever order you typed it, including lines you later deleted or fixed, which means your console history is not a reliable record of what your code actually does.
A script (a plain .R file) or a notebook (Lesson 3) is different: it’s
a saved, ordered list of every line, meant to be run from the top every time.
The real test of “does my code work” is not “did each line work when I ran
it” — it’s:
This is exactly the failure mode behind one of Lesson 12’s most common errors: code that “worked a minute ago” but fails after a restart, because a line that created some object got edited or deleted along the way, and only the console’s memory — not the script — still had it.
4Habit 3: comments that explain why¶
A comment starts with #; R ignores everything after it on that line.
Comments don’t change what your code does — they’re notes to your future self
(or a classmate, or your instructor) explaining your reasoning.
# not helpful: just restates the code in English
coffee <- read.csv("data/coffee_wait_sim.csv") # read the csv file
# helpful: explains why this line exists / what question it answers
coffee <- read.csv("data/coffee_wait_sim.csv") # 20 timed customers; see L06A comment that just repeats the code (# read the csv file, right next to
read.csv()) adds nothing — anyone can already see it’s reading a CSV file.
A useful comment answers a question the code itself can’t: why this file,
why this step, what does the result mean, what should the reader watch for.
5Habit 4: keep your data and your code together¶
Lesson 6 already established the habit this depends on: a
relative path like read.csv("data/coffee_wait_sim.csv") only works if a
data/ folder sits right next to your script or notebook. Reproducibility
means someone else (or you, on a different computer, or on CSUB JupyterHub)
can get your whole folder — script plus its data/ subfolder — and run
it with zero changes. If your code only works because a file happens to sit
in one particular spot on your personal laptop, it isn’t actually
reproducible yet.
6Putting it together: a script that runs clean¶
Here is a complete, small script following every habit above — a fixed seed up front, both packages loaded first, a comment explaining the question being asked (not just narrating each line), and a relative data path. It was run exactly as shown, in a brand-new R session, top to bottom, with no errors:
# coffee_wait_analysis.R
# Question: do customers wait longer on weekdays or weekends at the campus
# coffee cart?
# Data: coffee_wait_sim.csv, a classroom-simulation dataset (see L06).
library(mosaic)
library(BSDA)
set.seed(1209) # not strictly needed here (no randomness yet), but it is
# a habit we start on line 1 of every script, every time
# Load the data. Relative path "data/..." works because this script lives
# right next to the data/ folder (same layout as JupyterHub -- see L06).
coffee <- read.csv("data/coffee_wait_sim.csv")
# Quick sanity check before trusting any numbers below (habit from L06).
str(coffee)
# The actual question: wait time, broken down by day type.
favstats(wait_minutes ~ day_type, data = coffee)The library() load messages are exactly what you saw in
Lesson 5 — normal “masking” notices, not errors — so they’re
skipped here. The rest of the output, exactly as R printed it:
'data.frame': 20 obs. of 3 variables:
$ customer_id : int 1 2 3 4 5 6 7 8 9 10 ...
$ wait_minutes: num 3.7 2.3 1.4 3.7 5.9 6.2 5.9 2.3 3.7 2.6 ...
$ day_type : chr "Weekday" "Weekday" "Weekend" "Weekend" ...
day_type min Q1 median Q3 max mean sd n missing
1 Weekday 2.3 3.675 4.45 5.9 8.1 4.716667 1.752833 12 0
2 Weekend 1.4 2.100 2.70 3.1 6.2 2.925000 1.521043 8 0Nothing surprising — but notice why nothing is surprising: the comment at the top states the question, the seed is set before anything else, both packages load before they’re needed, and the path matches where the file actually lives. That combination is what “reproducible” means in practice.
7Habit 5: report your setup with sessionInfo()¶
When something behaves differently than this book describes — or when you ask
your instructor or a classmate for help — the very first useful fact you can
provide is exactly which versions of R and which packages you’re running.
sessionInfo() prints all of it in one call:
sessionInfo()R version 4.5.2 (2025-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 22631)
Matrix products: default
LAPACK version 3.12.1
locale:
[1] LC_COLLATE=English_United States.utf8
[2] LC_CTYPE=English_United States.utf8
[3] LC_MONETARY=English_United States.utf8
[4] LC_NUMERIC=C
[5] LC_TIME=English_United States.utf8
time zone: America/Los_Angeles
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] BSDA_1.2.2 mosaic_1.9.2 mosaicData_0.20.4 ggformula_1.0.1
[5] dplyr_1.2.0 Matrix_1.7-4 ggplot2_4.0.2 lattice_0.22-7
loaded via a namespace (and not attached):
[1] gtable_0.3.6 compiler_4.5.2 tidyselect_1.2.1
[4] Rcpp_1.1.1-1 stringr_1.6.0 tidyr_1.3.2
[7] fontquiver_0.2.1 systemfonts_1.3.2 scales_1.4.0
[10] labelled_2.16.0 fastmap_1.2.0 R6_2.6.1
[13] gdtools_0.5.0 generics_0.1.4 htmlwidgets_1.6.4
[16] MASS_7.3-65 forcats_1.0.1 mosaicCore_0.9.5
[19] tibble_3.3.1 pillar_1.11.1 RColorBrewer_1.1-3
[22] rlang_1.1.7 stringi_1.8.7 S7_0.2.1
[25] ggiraph_0.9.6 cli_3.6.5 withr_3.0.2
[28] magrittr_2.0.4 class_7.3-23 digest_0.6.39
[31] grid_4.5.2 haven_2.5.5 hms_1.1.4
[34] lifecycle_1.0.5 vctrs_0.7.2 proxy_0.4-29
[37] glue_1.8.0 farver_2.1.2 fontLiberation_0.1.0
[40] e1071_1.7-17 fontBitstreamVera_0.1.1 purrr_1.2.1
[43] tools_4.5.2 pkgconfig_2.0.3 htmltools_0.5.9
[46] ggridges_0.5.7 That is a lot of text, and — just like library(mosaic)'s masking messages
in Lesson 5 — you are not meant to read all of it. Three lines matter almost
all the time:
R version 4.5.2— the R version itself (this book is written and tested against 4.5.2; CSUB JupyterHub keeps this current for you, so you will usually see the same version or a newer one).The
other attached packagesline — confirmsmosaicandBSDAare really loaded, and shows their exact version numbers (mosaic_1.9.2,BSDA_1.2.2— the same numberspackageVersion()gave you in Lesson 5).Everything under
loaded via a namespaceis packages thatmosaicandBSDAquietly depend on — you never load these yourself, and you can ignore this whole block.
If you ever post a question about R code that “isn’t working” — to your
instructor, a classmate, or an online forum — pasting your sessionInfo()
output alongside your code and the exact error message is the single most
useful thing you can add: it tells whoever’s helping exactly what you’re
running, instead of them guessing.
8Summary¶
set.seed(n), run right before anything involving chance, makes that randomness exactly repeatable — same seed, same result, every time.Write code as a script or notebook, not just console history; the real test of working code is running it top to bottom in a freshly restarted session (Restart R / Restart Kernel, then run all).
Comments (
#) should explain why a line exists, not just restate what it obviously does.Keep a script’s
data/folder right next to it, using relative paths (Lesson 6) — reproducible means it runs on any computer, not just yours.sessionInfo()reports your exact R and package versions — the first thing to share whenever you ask for help.
9Check your understanding¶
You and a classmate both run
set.seed(1209)followed bysample(1:20, 5). Will you get the same 5 numbers? What if only one of you runsset.seed(1209)first?A script “worked” when you ran it line by line but fails with an error the next morning when you Restart R and run it top to bottom. What almost certainly happened, and which habit in this lesson prevents it?
Rewrite this comment so it explains why, not just what:
set.seed(1209) # sets the seed to 1209Name the one line of
sessionInfo()'s output that tells you whethermosaicis really loaded, and what else it tells you aboutmosaicbesides that.