
Lecture 9: Probability
August 22, 2026
π Last lecture we were studying methods to describe and visualize real world data, including:
π This week we will be briefly stepping away from data science and focussing more on probability.
The next four lectures are relatively self-contained and will be broken down into the following subtopics:
Very rarely in data science will our data show a perfectly clear relationship.
We typically assume that all data has two components: a deterministic component (i.e. some function we know exactly and so can predict exactly) and a random (noise) component.

For example, consider you run an experiment and receive the following βnoisyβ sin curve.

How might we try to determine the true value?
We run the experiment several times over and take the average after many trials! The higher the number of trials, the closer our average gets to the true value. This is an example of Monte-Carlo simulation.

Consider rolling two dice. We wish to determine the probability that the sum of the two rolls is 8.

Probability
The probability of some discrete event \(A\) occurring is given by:
\[ \mathbb{P}(A) = \frac{\text{number of ways for event A to occur}}{\text{total number of possible outcomes}}. \]
In terms of our dice rolls:
02:00
Suppose that you flip three fair coins, that is for each coin
\[ \mathbb{P}(H)=\mathbb{P}(T)=\frac{1}{2}. \]
Determine the probability that you flip exactly one head by working through the following:
The number of outcomes with exactly one head is 3: \[\text{(HTT, THT, TTH)}\]
The total number of outcomes is 8: \[\text{(HHH, HHT, HTH, THH, HTT, THT, TTH, TTT)}\]
The ratio is therefore 3/8.
For four coins, the number of outcomes with exactly three heads is 4: \[\text{(HHHT, HHTH, HTHH, THHH)}\]
The total number of outcomes is \(2^4=16\), so the probability is \(4/16 = 1/4\).
Sample Space
The sample space is the set of all possible outcomes of the experiment. It is usually denoted by the capital Greek letter omega \(\Omega\).
From our previous examples we have:
Each of these examples is known as a discrete sample space.
Discrete Sample Space
A sample space is said to be discrete if it has finite (more specifically countable) possible outcomes, for example:
Continuous Sample Space
If a sample space is not discrete it is said to be continuous, for example:
π Essentially, if the outcomes are values in some continuous interval(s) then the sample space is continuous.
03:00
For each of the following experiments determine the sample space, noting whether it is continuous or discrete.
Often times we are unable to determine the probability analytically due to reasons such as:
In these cases we can accurately approximate the solution using simulation!
If we repeat a random experiment many times and record whether an event happens on each repetition, then the proportion of repetitions where it happens gives us a good estimate of its probability.
Roll a die 6000 times and count how often a 4 comes up β you should see roughly 1000 of them, i.e. roughly \(1/6\) of trials, even though we never explicitly computed \(1/6\).
Think back to our dice rolling experiment. Assuming we cannot compute the probability, we look to set up a simulation to approximate it.
We begin by outlining the structure of the simulation:
TRUE if their sum is 8 and FALSE otherwise.n number of times.n_true and compute the probability as n_true/n or equivalently mean(n_true).We havenβt seen the code yet, but hereβs a preview of applying this same structure to our coin flip exercise from earlier, for a growing number of trials n:
n P(1 head in 3 flips) P(3 heads in 4 flips)
1 10 0.400 0.200
2 100 0.370 0.230
3 1000 0.394 0.228
Notice how the approximations get closer to the true values (3/8 = 0.375 and 1/4 = 0.25) as n grows.
An experiment is a repeatable process with random outcomes.
An event is the set of all outcomes of an experiment satisfying a logical condition.
The probability of an event can be thought of as the proportion of TRUE outcomes divided by the total number of trials n as n approaches infinity.
π This is not a rigorous definition β itβs just a helpful, intuitive way to think about probability. Formally, probability is defined more abstractly as a measure: a function assigning a number to subsets of some abstract space, satisfying a small set of axioms. We wonβt need this formal machinery in this course.
sample() FunctionThe sample() function is the key function for discrete probability calculation. It randomly draws elements from a set, and is exactly the tool we need to simulate experiments like coin flips and dice rolls.
Here the arguments are:
x is the set you sample from;size is the size of the sample you wish to take;replace specifies whether you replace sampled items; andprob assigns different weights (probabilities) to different outcomes.For example, to simulate a single roll of a fair six-sided die:
π Careful: if x is a single number n (not a vector), sample() treats it as shorthand for sample(1:n, ...). This is a common source of bugs when x is meant to represent a vector containing one value β always double check what x actually is!
sample() β Without ReplacementLetβs consider randomly sampling from animal types:
By default we sample without replacement, so each time we select an animal we can no longer choose it. We therefore cannot sample more than the size of the set:
[1] "lion" "gorilla" "elephant"
Error in sample.int(length(x), size, replace, prob): cannot take a sample larger than the population when 'replace = FALSE'
A few more examples of sampling without replacement:
[1] 44 37 43 46 13
[1] "gorilla" "elephant" "crocodile" "gibbon" "lion"
Setting size equal to the length of x is a handy way to randomly shuffle a vector.
sample() β With ReplacementIf we specify to sample with replacement we can sample as much as we want.
We sample 1000 times from animals with replacement and inspect the count plot:
sample() β ProbabilitiesNotice that our last plot had (roughly) equal bar heights, since we are equally likely to select each animal.
If we are more likely to select certain animals we can specify the probabilities using the prob argument. prob takes a vector of weights, one per element of x (matched by position), giving the relative likelihood of selecting each item β the values must be non-negative, and R automatically rescales them to sum to 1. So prob = c(0.4, 0.3, 0.2, 0.05, 0.05) means we should select "lion" about 40% of the time, "elephant" about 30% of the time, and so on.
We again produce the count plot to see the impact this has on our sample.
sample() β Probabilities (Result)prob weights, since our sample size (1000) is large.Returning to our example, you can use sample() to generate one dice roll. We then use a loop to approximate the probability:
sample(1:6, 1, replace = TRUE)
# function computing the probability
prob_dice_sum <- function(sims = 10000) {
count <- 0
for(i in seq_len(sims)) {
dsum <- sample(1:6, 1) + sample(1:6, 1)
if (dsum == 8) count <- count + 1
}
return(count / sims)
}
# compute probability example
prob_dice_sum(sims = 10000)[1] 2
[1] 0.1363
We can use exactly the same structure to code up the 3 heads in 4 flips case from earlier:

What youβre seeing in the plot above is the Law of Large Numbers in action.
In plain terms: as you repeat a random experiment more times, the average of your results gets closer and closer to the true expected value or probability.
In short: more trials = less noise = a more reliable approximation.
π Weβll state this more formally (with a limit) in the next lecture after we have defined expectation β for now, the intuition is all we need.
Often we would like our work to be replicated by others, and if we are using random generation then others might not generate the same results. To ensure replicability we need to set the seed!

set.seed() FunctionWhen you generate random numbers in R, they are not truly random but are generated using a deterministic algorithm.
The function set.seed() initializes the random number generator to a specific state, so the sequence of random numbers can be reproduced.
[1] 926
[1] 926
Importantly, set.seed() doesnβt just fix the next random number β it resets the entire sequence that follows:
[1] 926 6291 8060
[1] 1174
[1] 926 6291 8060
Notice the first three draws are identical every time we call set.seed(84), while the 4th draw (without re-setting the seed) simply continues on from where the sequence left off.
03:00
In our drawer we keep 6 black socks and 8 white socks. We remove 3 socks without replacement and wish to determine the probability that all of our socks are black.
The probability of selecting 3 black socks is given by:
\[ \frac{6}{14}\times\frac{5}{13}\times\frac{4}{12} = \frac{5}{91} = 0.05494505. \]
05:00
We will now check the solution using simulation.
sock_drawer of 1βs (representing black socks) and 0βs (representing white socks).socks representing the results of 3 selections from the drawer (without replacement) β use the sample() function with argument replace = FALSE.β Solution:
[1] 0.0577
sock_drawer from 6 ones (black) and 8 zeros (white).sum(socks) == 3).replicate() Functioni in a for loop) β as in all of our simulation examples so far!replicate() FunctionThe function replicate() simply replicates a task you set it, a specified number of times, and returns the results in an appropriate data structure. The general form is:
n is the number of times to repeat the task;expr is the expression (code) you want repeated β typically a single random draw or simulation; andsimplify = "array" (the default) tells R to simplify the list of results into a vector, matrix, or array where possible, rather than returning a plain list.replicate() FunctionFirst, letβs use replicate() to simulate 100 individual dice rolls β the expression sample(1:6, size = 1, replace = TRUE) (one dice roll) is repeated 100 times, and the results are simplified into a single vector:
Now letβs revisit our two-dice sum example. We replicate βroll two dice and sum themβ 10000 times, storing every sum in dice_sum, then compute the proportion equal to 8 β no for loop required!
03:00
Suppose you roll 2 fair dice. What is the probability that their product is greater than 15? Simulate with at least 10000 trials.
π Hint: use replicate() (not a for loop) to generate the 10000 products, and prod() to multiply the two rolled values together.
replicate() runs prod(sample(1:6, size = 2, replace = TRUE)) 10000 times, storing each dice product in dice_prod.dice_prod > 15 produces a TRUE/FALSE vector, and mean() of a logical vector gives the proportion of TRUEs β exactly the probability we want.π The true probability is \(11/36\approx 0.3056\).
π€ Today we introduced ourselves to probability, specifically:
sample() Functionreplicate() Functionπ€© Next class we will continue exploring probability by formally defining random variables and exploring some discrete probability distributions, including: