Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

1Objectives

By the end of this lesson you will be able to:

  1. Explain why set.seed() makes a “random” simulation repeatable, and use it before any function that involves chance (like do() *, previewed in Lesson 8).

  2. 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.

  3. Write comments that explain why, not just restate what a line does.

  4. 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  2
sample(1:20, 5)
[1]  9  3  6 11 19

Two 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 16
set.seed(1209)
sample(1:20, 5)
[1] 13  4  5 17 16

Identical, 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.4

Run 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 L06

A 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       0

Nothing 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:

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

9Check your understanding

  1. You and a classmate both run set.seed(1209) followed by sample(1:20, 5). Will you get the same 5 numbers? What if only one of you runs set.seed(1209) first?

  2. 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?

  3. Rewrite this comment so it explains why, not just what: set.seed(1209) # sets the seed to 1209

  4. Name the one line of sessionInfo()'s output that tells you whether mosaic is really loaded, and what else it tells you about mosaic besides that.