Bayesian Thinking
in Precision Health
An interactive 3-week module on Bayesian inference — from flipping coins to updating clinical diagnoses. Learn to reason under uncertainty like a scientist.
Foundations of Bayesian Reasoning
Understand prior beliefs, evidence, and how to update beliefs rationally using Bayes' Theorem.
The Beta Distribution & Sequential Updating
Code your own Bayesian updating routine in Python or Java. Watch the posterior evolve with each observation.
Bayesian Inference in Precision Health
Apply Bayesian reasoning to real clinical scenarios — disease prevalence, diagnostic tests, and personalized risk.
Course Syllabus
Course Description
This module introduces Bayesian statistical reasoning as a foundational tool for precision medicine and health informatics. Students will progress from first principles — Bayes' Theorem and prior beliefs — through hands-on implementation of Beta-Binomial updating, to real-world applications in clinical diagnostics, digital phenotyping, and personalized medicine.
The course is structured around an integrated interactive platform combining textbook readings, live browser-based simulations, and an embedded Python coding environment. No external software installation is required.
Learning Objectives
By the end of this module, students will be able to:
Weekly Schedule
Grading Summary
| Component | Week | Points | % of Total | Type |
|---|---|---|---|---|
| Assignment 1: Conceptual Foundations | Week 1 | 50 | 28% | Written + Calculation |
| Assignment 2: Beta-Binomial Coding | Week 2 | 60 | 33% | Code + Analysis |
| Assignment 3: Clinical Case Study | Week 3 | 70 | 39% | Case Study + Code |
| Total | — | 180 | 100% | — |
Course Policies
Prerequisites
Students should have completed an introductory statistics course covering basic probability. Familiarity with Python (variables, loops, functions) or Java (classes, methods) is expected for the Week 2 coding assignment. No prior Bayesian experience is required.
Tools & Technology
All course materials are embedded in the interactive HTML platform — no installation required. For the coding assignments, students may use the in-browser Python sandbox (powered by Pyodide) or submit code written in a local Python 3.8+ or Java 11+ environment. Required Python packages: numpy, scipy, matplotlib.
Late Policy
Assignments submitted within 48 hours of the deadline receive a 10% deduction. Submissions beyond 48 hours are not accepted without prior arrangement. Extensions may be granted for documented circumstances with at least 24 hours' notice.
Academic Integrity
Students are encouraged to discuss concepts with peers, but all written responses and submitted code must be independently authored. Use of AI coding assistants is permitted for Week 2 and 3 coding tasks provided the student can explain every line of their submission.
Accessibility
All interactive labs function in any modern browser without plugins. Students requiring alternative formats for any course material should contact the instructor at least one week in advance.
pip install numpy scipy matplotlib, or Java 11+ with Apache Commons Math. Verify your setup works before Week 2 begins.
How to Use This Interactive Lab
This platform is your laboratory, textbook, and workspace — all in one. Here's how to navigate it effectively.
Navigation
Use the sidebar on the left to move between weeks and content types. Each week contains four items:
| Item | Purpose | Who it's for |
|---|---|---|
| Textbook | Core reading material with examples, formulas, and explanations | All students — read before lab |
| Interactive Lab | Hands-on simulation to explore concepts visually | All students — during lab sessions |
| Assignment | Questions and coding tasks to complete for submission | All students — submit to instructor |
| Answer Sheet | Full solutions with explanations | Instructors only (initially) |
Using the Interactive Labs
The labs require no installation — they run directly in your browser. Each lab has:
- Sliders and controls — adjust parameters and see results instantly
- Run Simulation buttons — execute coin flips or updates
- Live charts — watch distributions evolve in real time
- Statistics panel — track key metrics (α, β, mean, variance)
- Flip log — a scrollable record of every flip outcome
Coding Exercises
Assignments include Python and Java code templates. You will write functions to simulate coin flips, update a Beta distribution, and visualize the posterior. The lab shows you the expected output so you can verify your code is correct.
Recommended Weekly Flow
- Read the Textbook section (~45–60 min)
- Open the Interactive Lab and explore for ~30 min
- Complete the Assignment (coding + written responses)
- Review the Answer Sheet after submitting
Bayes' Theorem:
Updating Beliefs with Evidence
1. The Problem of Uncertain Beliefs
Imagine you find a coin on the street. Is it a fair coin? You don't know. Maybe it's perfectly balanced, maybe it's loaded to land heads 70% of the time. Your prior belief is that it's probably fair — but you're not certain.
Now you flip it 10 times and get 8 heads. Should you update your belief? Of course. But by how much? This is precisely the question Bayesian inference answers.
2. The Formula
Bayes' Theorem tells us how to update a probability when we receive new evidence:
| Symbol | Name | Meaning |
|---|---|---|
| P(H) | Prior | Your belief in H before seeing evidence |
| P(E|H) | Likelihood | How probable is the evidence if H is true? |
| P(H|E) | Posterior | Your updated belief after seeing evidence E |
| P(E) | Marginal | Total probability of the evidence (normalizing constant) |
3. An Intuitive Example: The Coin
Let's define two hypotheses:
- H₁: The coin is fair (P(heads) = 0.5)
- H₂: The coin is biased (P(heads) = 0.7)
Suppose we start believing both hypotheses equally: P(H₁) = P(H₂) = 0.5. We flip the coin and get Heads.
P(E) = 0.5 × 0.5 + 0.5 × 0.7 = 0.60
P(fair | Heads) = (0.5 × 0.5) / 0.60 ≈ 0.417
P(biased | Heads) = (0.7 × 0.5) / 0.60 ≈ 0.583
One heads made us slightly more convinced the coin is biased. Makes sense! Each flip is weak evidence, but evidence accumulates.
4. The Key Insight: Updating Is Iterative
The posterior from one update becomes the prior for the next update. This is the beating heart of Bayesian inference: beliefs evolve continuously as evidence arrives, making it perfectly suited to medical data streams, sensor readings, and longitudinal studies.
5. Frequentist vs. Bayesian Thinking
| Approach | Question Asked | What's Random? |
|---|---|---|
| Frequentist | "What would happen over many repetitions?" | The data |
| Bayesian | "Given this data, what should I believe?" | The hypothesis |
Neither is universally superior — but Bayesian methods shine when prior knowledge is valuable and when you need to update beliefs sequentially.
6. Summary
Coin Flip Explorer
Flip a coin with an unknown bias and watch how your belief about P(heads) updates with each observation.
Bayesian Coin Flip Simulator
Set the coin's true bias (hidden), choose your prior, and flip. Can you detect the bias?
Conceptual Foundations of Bayesian Inference
Week 1 — Answer Sheet
Grading note: Award full credit for correct directional understanding. Accept varied wording. Deduct 3 pts if student confuses prior and posterior.
P(D) = 0.01 P(T+|D) = 0.95 P(T+|¬D) = 0.10
P(T+) = P(T+|D)·P(D) + P(T+|¬D)·P(¬D)
= 0.95 × 0.01 + 0.10 × 0.99 = 0.0095 + 0.099 = 0.1085
P(D|T+) = (0.95 × 0.01) / 0.1085 ≈ 0.0876 ≈ 8.76%
Despite a 95% sensitive test, only ~9% of positives actually have the disease — because the disease is rare. This is the base rate fallacy.
After 100 flips: posterior mean should be closer to 0.70, with a narrower distribution.
Pattern: The posterior mean converges toward the true bias (0.70) as more data accumulates. The distribution also becomes narrower (more certain). This demonstrates the law of large numbers in a Bayesian framework.
Accept any reasonable empirical observation. Award full credit if student correctly describes convergence and reduced uncertainty.
The Beta Distribution:
A Prior Made for Probabilities
1. Why We Need a Distribution for P
In Week 1, we treated our hypotheses as discrete ("fair" vs. "biased"). But in reality, the coin's bias θ is a continuous value between 0 and 1. We need a probability distribution that lives on [0,1] and can represent all our uncertainty about θ.
Enter the Beta distribution: Beta(α, β).
2. Understanding α and β
Think of α and β as "pseudo-counts":
| Parameter | Intuition | Effect on Distribution |
|---|---|---|
| α | Prior heads observations | Shifts distribution right (toward 1) |
| β | Prior tails observations | Shifts distribution left (toward 0) |
| α+β | Total prior "confidence" | Concentration: higher = narrower = more certain |
A flat prior (total ignorance) is Beta(1,1) — a uniform distribution. "I believe the coin is fair, with moderate confidence" might be Beta(10,10).
3. The Magic: Conjugate Updating
The Beta distribution is a conjugate prior for the Binomial likelihood. This means: if you start with Beta(α, β) and observe h heads and t tails, your posterior is simply:
No integration. No numerical approximation. Just add your counts to the prior parameters. This is the mathematical elegance that makes Beta-Binomial updating so powerful and intuitive.
4. Python Implementation
import numpy as np import matplotlib.pyplot as plt from scipy.stats import beta as beta_dist import random # === PARAMETERS === TRUE_BIAS = 0.65 # The "true" bias of the coin (unknown to us) ALPHA_PRIOR = 2 # Initial prior: pseudo-heads count BETA_PRIOR = 2 # Initial prior: pseudo-tails count N_FLIPS = 100 # Number of coin flips to simulate def flip_coin(p_heads=0.5): """Simulate a single biased coin flip.""" return 1 if random.random() < p_heads else 0 def update_posterior(alpha, beta_param, outcome): """Update Beta parameters given a coin flip outcome (1=H, 0=T).""" if outcome == 1: # Heads alpha += 1 else: # Tails beta_param += 1 return alpha, beta_param # === SIMULATION === alpha, beta_param = ALPHA_PRIOR, BETA_PRIOR theta = np.linspace(0, 1, 200) snapshots = [(0, alpha, beta_param)] for i in range(N_FLIPS): outcome = flip_coin(TRUE_BIAS) alpha, beta_param = update_posterior(alpha, beta_param, outcome) if (i + 1) in [5, 10, 25, 50, 100]: snapshots.append((i + 1, alpha, beta_param)) # === PLOTTING === fig, axes = plt.subplots(1, len(snapshots), figsize=(18, 4)) for ax, (n, a, b) in zip(axes, snapshots): y = beta_dist.pdf(theta, a, b) ax.fill_between(theta, y, alpha=0.3, color='steelblue') ax.plot(theta, y, color='steelblue') ax.axvline(TRUE_BIAS, color='red', ls='--', alpha=0.7) ax.set_title(f'n={n}\nBeta({a},{b})') ax.set_xlabel('θ (P heads)') plt.suptitle('Posterior Evolution: Beta-Binomial Updating', fontsize=14) plt.tight_layout() plt.savefig('posterior_evolution.png', dpi=150) plt.show()
5. Java Implementation
import org.apache.commons.math3.distribution.BetaDistribution; import java.util.Random; public class BayesianCoinFlip { private double alpha; private double beta; private final double trueBias; private final Random rng = new Random(); public BayesianCoinFlip(double alpha, double beta, double trueBias) { this.alpha = alpha; this.beta = beta; this.trueBias = trueBias; } /** Simulate one coin flip and update the posterior. */ public boolean flipAndUpdate() { boolean heads = rng.nextDouble() < trueBias; if (heads) alpha++; else beta++; return heads; } public double posteriorMean() { return alpha / (alpha + beta); } public double posteriorVariance() { double n = alpha + beta; return (alpha * beta) / (n * n * (n + 1)); } public static void main(String[] args) { BayesianCoinFlip sim = new BayesianCoinFlip(2, 2, 0.65); int[] snappoints = {5, 10, 25, 50, 100}; int next = 0; for (int i = 1; i <= 100; i++) { sim.flipAndUpdate(); if (next < snappoints.length && i == snappoints[next]) { System.out.printf("n=%3d | Mean: %.3f | Var: %.5f%n", i, sim.posteriorMean(), sim.posteriorVariance()); next++; } } } }
Beta Distribution Shaper
Explore how α and β shape the Beta distribution. Then run the full sequential Bayesian updating simulation.
Beta Distribution Explorer
Adjust α and β to shape your prior belief about P(heads). Observe mean, mode, and variance.
Sequential Bayesian Updating
Run a full simulation and watch posterior snapshots at 5, 10, 25, 50, and 100 flips.
Coding the Beta-Binomial Updater
flip_and_update() function in Python (or the Java equivalent). Your function should accept the current α and β, simulate one coin flip using a given true bias, and return the updated (α, β) pair. Run 200 flips and print the posterior mean every 10 flips.Week 2 — Answer Sheet
import random
def flip_and_update(alpha, beta, true_bias):
outcome = 1 if random.random() < true_bias else 0
if outcome == 1:
alpha += 1
else:
beta += 1
return alpha, beta
alpha, beta = 2, 2
for i in range(200):
alpha, beta = flip_and_update(alpha, beta, 0.70)
if (i + 1) % 10 == 0:
print(f"n={i+1:3d} | Posterior mean = {alpha/(alpha+beta):.4f}")
Expected output: posterior mean converges toward ~0.70 over 200 flips.
Experiment B (Beta(20,20)): After 100 flips with θ=0.75, expected α≈95, β≈45. Posterior mean ≈ 95/140 ≈ 0.679. Wider and slower to converge.
Flat prior recovers true bias more quickly because the data can dominate without fighting a strong prior assumption of fairness. The strong prior requires more evidence to shift. Grading: award credit for correct directional reasoning even if numeric values differ.
5 pts: Distributions visually narrow and shift toward true bias over time
3 pts: True bias dashed line present
2 pts: Proper labels (α, β values, n, axes)
Bayesian Inference
in Precision Health
1. The Clinical Relevance of Base Rates
In Week 1, we showed that even a 95%-accurate test for a 1% prevalence disease is only 8.76% likely to be correct on a positive result. This is not a flaw in the test — it's a fundamental property of probability that physicians must internalize.
2. Sensitivity, Specificity, and Predictive Values
| Metric | Formula | Bayesian Interpretation |
|---|---|---|
| Sensitivity (TPR) | TP / (TP + FN) | P(Test+ | Disease+) = Likelihood |
| Specificity (TNR) | TN / (TN + FP) | 1 − P(Test+ | Disease−) |
| PPV | TP / (TP + FP) | P(Disease+ | Test+) = Posterior |
| NPV | TN / (TN + FN) | P(Disease− | Test−) = Posterior |
PPV and NPV are the clinically actionable numbers — and both depend critically on disease prevalence (the prior).
3. Sequential Updating in Patient Care
Bayesian updating mirrors how good clinicians think. A physician's initial assessment ("fever + cough → 30% probability of pneumonia") is a prior. A chest X-ray is evidence. The updated probability is the posterior. A positive culture is more evidence. The final treatment decision is informed by the final posterior.
4. Precision Medicine Applications
- Pharmacogenomics — Bayesian models update drug response probabilities given genotype
- Cancer screening — Combining PSA, family history, and imaging with Bayesian networks
- Wearable sensor data — Continuously updating arrhythmia risk from ECG streams
- Clinical trial design — Bayesian adaptive trials can stop early for efficacy or futility
- Digital phenotyping — Updating behavioral models from smartphone sensor data over time
5. The Bayesian Brain
Remarkably, neuroscientists now believe the brain itself performs Bayesian inference — combining prior expectations (based on experience) with sensory evidence to construct perception. In this sense, Bayesian thinking is not just a statistical method — it's how intelligence works.
Diagnostic Testing Simulator
Simulate clinical Bayesian inference. Adjust disease prevalence and test performance, and see how posterior probability changes with sequential testing.
Bayesian Diagnostic Simulator
Set prevalence, sensitivity, and specificity. Run tests and update your clinical probability estimate.
Bayesian Reasoning in Clinical Cases
(a) Calculate P(Alzheimer's | Positive test).
(b) The same patient undergoes a second independent test (same metrics) and again tests positive. Calculate the updated probability.
(c) Explain in plain language why two positive tests increase certainty substantially more than one.
Week 3 — Answer Sheet
P(+) = 0.90×0.10 + 0.15×0.90 = 0.09 + 0.135 = 0.225
P(D|+) = 0.09 / 0.225 = 0.400 (40%)
P(+) = 0.90×0.40 + 0.15×0.60 = 0.36 + 0.09 = 0.45
P(D|++) = 0.36 / 0.45 = 0.800 (80%)
import random
import matplotlib.pyplot as plt
def run_diagnostic_simulation(prevalence, sensitivity, specificity, has_disease, n_tests=5):
fp_rate = 1 - specificity
prior = prevalence
history = [prior]
for _ in range(n_tests):
# Simulate test outcome
if has_disease:
test_pos = random.random() < sensitivity
else:
test_pos = random.random() < fp_rate
if test_pos:
likelihood = sensitivity
likelihood_neg = fp_rate
else:
likelihood = 1 - sensitivity
likelihood_neg = specificity
# Bayes update
numerator = likelihood * prior
denominator = numerator + likelihood_neg * (1 - prior)
posterior = numerator / denominator
history.append(posterior)
prior = posterior
return history
history = run_diagnostic_simulation(0.10, 0.90, 0.85, has_disease=True)
plt.plot(range(len(history)), [h*100 for h in history], 'o-', color='purple')
plt.xlabel('Tests Completed')
plt.ylabel('P(Disease) %')
plt.title('Sequential Bayesian Diagnostic Updating')
plt.grid(True, alpha=0.3)
plt.savefig('diagnostic_trajectory.png', dpi=150)
plt.show()