learn-statistics-with-phoebe / Builder session 4 of 10
Learn Statistics with Phoebe · Builder session 4 of 10

Distributions in scipy, one API for every shape of randomness

Here is the quiet superpower of scipy.stats: every distribution - normal, binomial, Poisson, lognormal, all of them - speaks the same five-verb language. Learn .pdf/.pmf, .cdf, .ppf, .rvs, and .fit once and you can work with any distribution you will ever meet. Then we do the honest thing with Lumen: order value is not normal, it is lognormal, and a Q-Q plot proves it.

🟡 Building up Builders · DA / DE / DS Python · scipy / matplotlib 45 min
0-3 · Setup 3-16 · The scipy API 16-40 · Fit & check on Lumen 40-45 · Wrap
Part 0

Learn one API, get every distribution free

Most people learn distributions as a pile of unrelated formulas. scipy takes a better view: a distribution is an object, and every object has the same methods. Ask any of them for its density, its cumulative probability, its quantiles, some random samples, or a fit to your data - the method names never change. That means the hard part is no longer the math, it is picking the right distribution for your data. That choice is what this session drills.

Live - built in session Self-study - run after ★ The one big idea The case: Lumen Skincare
★ What you walk out with today The shared five-verb scipy API - .pdf/.pmf, .cdf, .ppf (the inverse cdf / quantile), .rvs (sample), and .fit (estimate params) - across normal, binomial, Poisson, uniform, exponential, and lognormal. You will model Lumen conversions as binomial and daily orders as Poisson, prove order value is lognormal not normal with a Q-Q plot, and use .ppf and .cdf to answer real business questions about order size.
Part 1 · the vocabulary in code

The five verbs, and which distribution to reach for 7 min live

Two things to hold in your head. First, the five methods every scipy distribution shares. Second, a quick decision for which distribution fits your data - is it a continuous symmetric measurement, a count of yes/no trials, a count of events per period, or right-skewed money? The map below is the whole decision.

Binomial(4,000, 3.2%) and Poisson land on the same mean Binomial n=4,000, p=3.2% Poisson mean ~128/day Binomial(n=4,000, p=3.2%) and Poisson(mean ~128) center on the same number - different shapes.
🔍 Click to zoom - two different shapes, the same average of about 128
What shape is your data? continuous, symmetric n yes/no trials events per period right-skewed money Normal st.norm(mu, sigma) heights, errors Binomial st.binom(n, p) conversions Poisson st.poisson(lam) orders / day Lognormal st.lognorm(s, scale) order value every one answers: .pdf/.pmf (height) · .cdf (P below) · .ppf (quantile) · .rvs (sample) · .fit (estimate params)
🔍 Click to zoom - pick the distribution by shape; the five verbs are always the same
LiveThe five verbs every distribution shares4 min▶
  • .pdf(x) / .pmf(k): the height. pdf for continuous (a density), pmf for discrete (a real probability).
  • .cdf(x): P(X <= x). "What fraction sits at or below here." Answers "P(order < $100)".
  • .ppf(q): the inverse cdf, a quantile. "What value has q of the mass below it." Answers "the 95th-percentile order".
  • .rvs(size): draw random samples. Your simulation engine from b2 and b3, built in.
  • .fit(data): estimate the distribution's parameters from data by maximum likelihood. Turns raw numbers into a fitted model.
cdf and ppf are inverses cdf takes a value and returns a probability; ppf takes a probability and returns a value. So rv.ppf(rv.cdf(100)) gives you 100 back. Whenever a question says "what value" you want ppf; whenever it says "what fraction" or "what probability" you want cdf.
★ everyone builds · fit and check on Lumen

Model conversions, then prove order value is lognormal ★ 22 min · everyone builds

First the easy fits: conversions as binomial, daily orders as Poisson. Then the honest one - hold order value up against both a normal and a lognormal, and let the Q-Q plot decide. Finally use ppf and cdf to answer questions the business actually asks.

Conversions and daily orders. Binomial for "successes out of 4,000 sessions", Poisson for "orders per day". They center on the same ~128.

discrete_models.py
import numpy as np, scipy.stats as st, matplotlib.pyplot as plt

orders = st.binom(4000, 0.032)     # conversions among 4,000 daily sessions
daily  = st.poisson(128)           # daily orders, lambda = 4000 * 0.032 = 128

print("binomial mean:", orders.mean(), " Poisson mean:", daily.mean())  # ~128 both

k = np.arange(90, 170)
fig, ax = plt.subplots(1, 2, figsize=(11, 4))
ax[0].bar(k, orders.pmf(k), color="#7FD1DE"); ax[0].set_title("Binomial(4000, 0.032)")
ax[1].bar(k, daily.pmf(k),  color="#7FD1DE"); ax[1].set_title("Poisson(128)")
plt.tight_layout(); plt.show()
# Poisson is the binomial's large-n, small-p limit - the two pmfs nearly overlay

The honest fit. Order value is right-skewed money, so fit lognormal AND normal, overlay both, then let a Q-Q plot expose which one fails.

lognormal_vs_normal.py
import pandas as pd, numpy as np, scipy.stats as st, matplotlib.pyplot as plt

df = pd.read_parquet("lumen_orders.parquet")
ov = df["order_value"].values

shape, loc, scale = st.lognorm.fit(ov, floc=0)   # fit lognormal (fix loc=0)
mu, sigma         = st.norm.fit(ov)              # fit normal

x = np.linspace(ov.min(), ov.max(), 300)
fig, ax = plt.subplots(1, 2, figsize=(11, 4))
ax[0].hist(ov, bins=60, density=True, color="#7FD1DE", edgecolor="white")
ax[0].plot(x, st.lognorm.pdf(x, shape, loc, scale), color="#0E7C90", lw=2, label="lognormal")
ax[0].plot(x, st.norm.pdf(x, mu, sigma),            color="#F97362", lw=2, label="normal")
ax[0].legend(); ax[0].set_title("Order value - lognormal hugs it, normal does not")

st.probplot(ov, dist="norm", plot=ax[1])         # normal Q-Q plot
ax[1].set_title("Normal Q-Q - points peel off in the right tail")
plt.tight_layout(); plt.show()
Real world

A Lumen analyst once built a "flag orders more than 2 standard deviations above the mean" rule, assuming normality. On lognormal data that rule flags almost nothing on the low side and a flood of ordinary large orders on the high side, because the normal model puts the mean and SD in the wrong place. The Q-Q plot above is the 10-second check that would have caught it: when the tail points bend away from the line, stop assuming normal.

A normal-shaped assumption on right-skewed money ✗ Assume normal order value Flags almost nothing on the low side Floods the flag with big ordinary orders Normal puts mean, SD in wrong place ✓ Check a Q-Q plot first Order value is lognormal, not normal Fit lognormal, then set the flag line Bent tail = stop assuming normal A normal-based flag mis-fires on lognormal order value; a Q-Q plot catches the bent tail first.
🔍 Click to zoom - check the Q-Q plot before you trust a normal-based rule

Answer business questions with ppf and cdf. How big is a "big" order, and how many orders come in under $100?

ppf_and_cdf.py
import scipy.stats as st

# reuse the fitted lognormal params: shape, loc, scale
p95 = st.lognorm.ppf(0.95, shape, loc, scale)   # ppf = quantile / inverse cdf
print("95th-percentile order:", round(p95, 2))  # the size of a top-5% order

p_under_100 = st.lognorm.cdf(100, shape, loc, scale)
print("P(order < $100):", round(p_under_100, 3))

# sanity: cdf and ppf are inverses
print(round(st.lognorm.cdf(p95, shape, loc, scale), 2))   # 0.95
See it live

The sampler - watch samples fill the curve interactive

This is the code you just ran, made draggable. Flip between Normal, Binomial, and Poisson, drag the parameters, and watch the random samples pile up into the theoretical pmf / pdf. More samples, tighter fit - the law of large numbers from b2 and the distributions from today, in one widget.

Before Session 5

This week ◐ 40 min total

Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · You want the order value below which 95% of orders fall. Which scipy method?

ppf maps a probability to a value (the quantile), which is exactly "the value with 95% of the mass below it." cdf goes the other way (value to probability); pdf gives a density height.

2 · A normal Q-Q plot of Lumen order value shows the points bending away from the line in the upper tail. What does that tell you?

Q-Q points that peel off the line in the tail mean the data departs from the reference distribution there. For right-skewed money the upper tail is heavier than normal, which is exactly why lognormal is the honest choice.

3 · Why can Lumen's daily orders be modeled as Poisson(128) when there are ~4,000 sessions at 3.2%?

When n is large and p is small, the binomial(n, p) is very well approximated by a Poisson with lambda = n x p. Here 4,000 x 0.032 = 128, so both pmfs center and overlay on ~128 orders/day.

Source material

What this session covers

This builder session covers ~80% of the named-distributions content in the sources below, using the shared scipy API on Lumen data. Sampling distributions and the central limit theorem - why sample means go normal even when the data does not - come next in b5.

✓scipy.stats API: pdf/pmf, cdf, ppf, rvs, fit across normal, binomial, Poisson, lognormalPart 1 + demo
✓Lognormal vs normal fit, Q-Q plot with probplot, ppf/cdf for business questionsdemo + live sampler
◐OpenIntro Statistics ch. 4 - The normal distribution and friendstheory spine; free PDF
◐Google "The Power of Statistics" - module 2, probability distributionsmodule 2
◐StatQuest - The main ideas behind probability distributionsvideo, distribution intuition

Builder Session 4 cheat sheet · pin this

One APIEvery dist: .pdf/.pmf, .cdf, .ppf, .rvs, .fit. Learn once, use everywhere.
cdf vs ppfcdf: value to probability (P below). ppf: probability to value (quantile). They are inverses.
Pick by shapeSymmetric measure = normal. Yes/no counts = binomial. Events/period = Poisson. Skewed money = lognormal.
Q-Q plotst.probplot(data, dist="norm", plot=ax). Tail bends off the line = not that distribution.
Poisson limitBinomial(4000, 0.032) ≈ Poisson(128). lambda = n x p when n big, p small.
Lumen truthOrder value is lognormal, not normal. Never apply "mean ± 2 SD" rules to it.