Back to Blog

Math — Bayesian Probability

·Reha Tuncer·Math
BayesianProbabilityStatisticsPosteriorLikelihoodNumPy
View source on GitHub

Math — Bayesian Probability

A progressive implementation of Bayes' theorem — building the complete pipeline from likelihood through posterior probability, using a drug trial scenario with NumPy vectorization.


Learning Objectives

#Concept
1Compute the binomial likelihood P(dataP)P(\text{data} \mid P) for a vector of hypothetical probabilities
2Calculate the intersection P(dataP)P(P)P(\text{data} \mid P) \cdot P(P) — likelihood weighted by prior belief
3Compute the marginal probability P(data)P(\text{data}) as the sum of all intersections
4Derive the posterior P(Pdata)P(P \mid \text{data}) via Bayes' theorem: intersection ÷ marginal
5Understand the Bayesian workflow: prior → likelihood → marginal → posterior
6Apply NumPy vectorized operations on 1D arrays for efficient probability calculations
7Validate inputs with cascading exception checks in a specific order

Task-by-Task Reference

Each task below highlights the unique challenge it posed and the new technique introduced — techniques from earlier tasks are not repeated.


Task 0 — Likelihood (0-likelihood.py)

Challenge: Compute the binomial likelihood P(xn,P)P(x \mid n, P) for an entire vector of hypothetical probabilities — the first step in any Bayesian analysis.

Approach: For a drug trial with nn patients and xx side-effect cases, compute (nx)Px(1P)nx{n \choose x} P^x (1-P)^{n-x} for every probability in the NumPy array PP. The binomial coefficient is computed iteratively to avoid factorials. All operations are vectorized over PP.

New techniques introduced:

TechniquePurpose
Binomial likelihood formula (nx)Px(1P)nx{n \choose x} P^x (1-P)^{n-x}Model the probability of observing xx events in nn trials
Iterative binomial coefficient computationAvoid factorial overflow for large nn
NumPy array broadcasting P ** xApply exponentiation to every element simultaneously
Cascading validation with specific error messagesValidate inputs in a defined priority order

Key takeaway: The likelihood answers "how probable is this data under each hypothetical parameter value?" It's the engine of Bayesian inference — all downstream calculations depend on it.


Task 1 — Intersection (1-intersection.py)

Challenge: Combine the likelihood with prior beliefs — computing P(dataP)P(P)P(\text{data} \mid P) \cdot P(P), the joint probability of data and parameters.

Approach: Call likelihood() from Task 0, then multiply the result element-wise by the prior array Pr. Validate that Pr has the same shape as P and sums to 1 (using np.isclose for floating-point safety). The intersection represents the unnormalized posterior.

New techniques introduced:

TechniquePurpose
np.shape(Pr) == np.shape(P)Validate that prior and hypothesis arrays align
np.isclose(np.sum(Pr), 1.0)Check that prior probabilities sum to 1 (floating-point safe)
Element-wise array multiplication Pr * likelihoodNumPy vectorized product of two 1D arrays
Cross-file function dependencyintersection() depends on likelihood() from Task 0

Key takeaway: The intersection is the numerator of Bayes' theorem: prior × likelihood. It's the "unnormalized posterior" — proportional to the posterior but not yet summing to 1.


Task 2 — Marginal Probability (2-marginal.py)

Challenge: Compute the marginal probability P(data)P(\text{data}) — the normalizing constant that makes the posterior a valid probability distribution.

Approach: Call intersection() from Task 1, then sum all intersection values with np.sum(). The marginal is a single number — the total probability of observing the data under all hypotheses, weighted by prior beliefs. Bundles local copies of likelihood() and intersection() for self-containment.

New techniques introduced:

TechniquePurpose
np.sum(intersection)Sum all intersection values to get the marginal probability
Marginal = P(dataP)P(P)\sum P(\text{data} \mid P) \cdot P(P)The law of total probability — integrate over all hypotheses
Self-contained module with bundled dependenciesCopy dependent functions to make the file standalone

Key takeaway: The marginal probability P(data)P(\text{data}) is the denominator of Bayes' theorem. It's a single number that normalizes the posterior: every possible way the data could occur, weighted by prior belief.


Task 3 — Posterior Probability (3-posterior.py)

Challenge: Compute the posterior probability P(Pdata)P(P \mid \text{data}) — the updated belief about each hypothesis after observing the data. This is the final output of Bayesian inference.

Approach: Call intersection() and marginal() from previous tasks, then divide: posterior = intersection / marginal. The result sums to 1 — it's a proper probability distribution. The posterior answers: "given the data, what should I now believe about each possible probability PP?"

New techniques introduced:

TechniquePurpose
intersection / marginalBayes' theorem: posterior = (prior × likelihood) ÷ marginal
Full Bayesian pipeline: prior → likelihood → posteriorEnd-to-end probabilistic inference in 4 composable steps
Posterior sums to 1 (verifiable with np.sum())Valid probability distribution — can be used for decision-making

Key takeaway: The posterior is the goal of Bayesian analysis — it tells you what to believe after seeing the data. The entire pipeline (likelihood → intersection → marginal → posterior) implements Bayes' theorem: P(HD)=P(DH)P(H)P(D)P(H \mid D) = \frac{P(D \mid H) \cdot P(H)}{P(D)}.


Technique Inventory

TaskNew technique summarizedCategory
0Binomial likelihood, iterative binomial coefficient, NumPy broadcastingLikelihood
1Element-wise prior × likelihood, np.isclose for sum-to-1 checkIntersection
2np.sum() for marginal probability, law of total probabilityMarginal
3Bayes' theorem: posterior = intersection ÷ marginal, full pipelinePosterior

Bayesian Pipeline (Conceptual)

Prior P(H)  ──┐
               ├──→ Intersection ──→ Marginal P(D) ──→ Posterior P(H|D)
Likelihood    ──┘     P(D|H)·P(H)      Σ P(D|H)·P(H)      Intersection/Marginal
P(D|H)

Resources