⚙️ Setup¶
import numpy as np
rng = np.random.default_rng(341)
print("ready")
ready
p_spam, p_legit = 0.30, 0.70
p_off_spam, p_off_legit = 0.60, 0.05
p_off = p_spam*p_off_spam + p_legit*p_off_legit # total probability
post = p_spam*p_off_spam / p_off # Bayes
print(f"P(\"offer\") = {p_off:.3f}")
print(f"P(spam | \"offer\") = {post:.3f}")
P("offer") = 0.215
P(spam | "offer") = 0.837
Answer: P("offer") = 0.30(0.60) + 0.70(0.05) = 0.18 + 0.035 = 0.215. Then P(spam | "offer") = 0.18 / 0.215 ≈ 0.837. One suggestive word lifts the spam probability from a 0.30 prior to about 84%.
w = np.array([0.50, 0.30, 0.20]) # share from each supplier
d = np.array([0.01, 0.02, 0.03]) # defect rate of each
p_def = (w*d).sum()
for i,(wi,di) in enumerate(zip(w,d),1):
print(f"S{i}: {wi:.2f} * {di:.2f} = {wi*di:.3f}")
print(f"P(defective) = {p_def:.3f}")
S1: 0.50 * 0.01 = 0.005 S2: 0.30 * 0.02 = 0.006 S3: 0.20 * 0.03 = 0.006 P(defective) = 0.017
Answer: the Law of Total Probability averages the defect rates, weighted by each supplier's share: P(defective) = 0.50(0.01) + 0.30(0.02) + 0.20(0.03) = 0.005 + 0.006 + 0.006 = 0.017, that is 1.7%. The suppliers partition every part, so the slices simply add.
prior, sens, spec = 0.02, 0.90, 0.85
p_pos = prior*sens + (1-prior)*(1-spec)
post = prior*sens / p_pos
print(f"P(positive) = {p_pos:.3f}")
print(f"P(condition | positive) = {post:.3f} (about {post:.0%})")
P(positive) = 0.165 P(condition | positive) = 0.109 (about 11%)
Answer: P(positive) = 0.02(0.90) + 0.98(0.15) = 0.018 + 0.147 = 0.165. Posterior = 0.018 / 0.165 ≈ 0.109, about 11%. Even a positive test leaves the condition unlikely, because a 15% false-positive rate on the large healthy group produces many more false alarms than true ones.
prior = 0.109 # the posterior from Challenge 3
sens, spec = 0.90, 0.85
p_pos = prior*sens + (1-prior)*(1-spec)
post = prior*sens / p_pos
print(f"new prior (was posterior) = {prior:.3f}")
print(f"P(condition | 2 positives) = {post:.3f} (about {post:.0%})")
new prior (was posterior) = 0.109 P(condition | 2 positives) = 0.423 (about 42%)
Answer: with the new prior 0.109: P(positive) = 0.109(0.90) + 0.891(0.15) = 0.0981 + 0.1337 = 0.2318, so the posterior = 0.0981 / 0.2318 ≈ 0.423, about 42%. Two positives are far more convincing than one: belief climbed from 2% to 11% to 42% as independent evidence accumulated.
p_spam = 0.50
p_win_spam, p_win_ham = 0.70, 0.05
p_win = p_spam*p_win_spam + (1-p_spam)*p_win_ham
post = p_spam*p_win_spam / p_win
print(f"P(\"win\") = {p_win:.3f}")
print(f"P(spam | \"win\") = {post:.3f} (about {post:.0%})")
P("win") = 0.375
P(spam | "win") = 0.933 (about 93%)
Answer: P("win") = 0.5(0.70) + 0.5(0.05) = 0.35 + 0.025 = 0.375. Posterior = 0.35 / 0.375 ≈ 0.933, about 93%. The strong likelihood ratio (0.70 vs 0.05) makes "win" a powerful spam signal; a full classifier multiplies many such word likelihoods together.