Poisson Distribution
The Poisson distribution is a simple way to model “how many times something happens” in a fixed window—like how many customers arrive in 10 minutes or how many typos appear on a page. It’s built for counts: 0, 1, 2, 3, and so on.
What it describes
The Poisson distribution gives the probability of observing k events in a fixed interval of time/space when events occur randomly but with a stable average rate. It has one parameter, λ (lambda), the expected number of events in that interval. If λ = 3, then the mean count is 3, and (importantly) the variance is also 3.
Key assumptions (when it fits well)
It’s a good match when:
- Events are counted in a fixed window (per minute, per day, per square meter).
- Events happen independently (one arrival doesn’t “cause” the next).
- The average rate is roughly constant within the window.
- Two events at exactly the same instant are unlikely (in continuous time).
Practical examples
Common, relatable uses include:
- Number of support tickets per hour.
- Defects per batch in manufacturing.
- Hospital arrivals per night.
- Clicks on an ad per minute (when traffic is steady).
Why it matters in AI/ML
Many ML problems predict counts, not categories or continuous values. Poisson regression (a GLM) models a count outcome with a log link, often used for demand forecasting, incident prediction, and event-rate modeling. It also appears in probabilistic models and likelihoods: choosing a Poisson likelihood can make training align with the data’s count nature. A common warning sign is overdispersion (variance > mean), where alternatives like the negative binomial may work better.
The Poisson Distribution models the probability of observing a nonnegative integer count of events in a fixed interval (time, space, or trials) when events occur independently at a constant average rate λ. It is important in ML for count data and as the likelihood in Poisson regression and generalized linear models. Example: modeling the number of clicks per minute on a webpage.
Imagine you’re counting how many buses pass your stop in 10 minutes, or how many emails arrive in an hour. You can’t predict the exact number, but you might know the average rate. The Poisson distribution is a way to describe the chances of getting 0, 1, 2, 3, and so on events in a fixed time or space when events happen randomly but at a steady average pace.
In statistics and AI/ML, it’s often used for “count data,” like clicks per minute or defects per batch, helping models make sensible predictions about event counts.
import numpy as np # numerical computing (arrays, random sampling)
import pandas as pd # tabular data handling
from scipy.stats import poisson # Poisson PMF/CDF and random variates
import statsmodels.api as sm # GLM for count data (Poisson regression)
# Simulate "support tickets per hour" with an exposure effect (hours observed)
rng = np.random.default_rng(7) # reproducible randomness
n = 300 # number of hourly observations
hours_observed = rng.uniform(0.5, 2.0, size=n) # exposure: partial to double-hour windows
is_weekend = rng.integers(0, 2, size=n) # 0=weekday, 1=weekend indicator
# Create a Poisson rate (lambda) that depends on weekend + exposure (hours)
base_rate_per_hour = 3.0 # baseline tickets/hour
rate_multiplier_weekend = 0.7 # fewer tickets on weekends
lam = base_rate_per_hour * (rate_multiplier_weekend ** is_weekend) * hours_observed # expected count per window
# *** KEY LINE ***: draw counts from a Poisson distribution with mean=lam for each observation
tickets = poisson.rvs(mu=lam, random_state=rng) # Poisson-distributed counts
# Put data into a DataFrame for modeling/analysis
df = pd.DataFrame({"tickets": tickets, "weekend": is_weekend, "hours": hours_observed})
# Fit a Poisson GLM with a log link and an offset for exposure (log(hours))
X = sm.add_constant(df["weekend"]) # intercept + weekend effect
model = sm.GLM(df["tickets"], X, family=sm.families.Poisson(), offset=np.log(df["hours"]))
result = model.fit()
# Use the fitted model to predict expected tickets for weekday vs weekend at 1 hour exposure
new_X = sm.add_constant(pd.Series([0, 1], name="weekend")) # weekday, weekend
pred_mean = result.predict(new_X, offset=np.log([1.0, 1.0])) # expected counts (means)
print("Poisson GLM coefficients:\n", result.params)
print("Predicted mean tickets/hour (weekday, weekend):", pred_mean.values)
print("P(tickets >= 8 in 1 hour | weekday):", 1 - poisson.cdf(7, mu=pred_mean.values[0]))
Code Notes
- The core output is the simulated count vector poisson.rvs and the fitted Poisson GLM parameters from statsmodels.GLM, which estimate how the mean count changes with the weekend indicator.
- The offset=np.log(hours) term adjusts for different exposure durations so the model learns a per-hour rate rather than conflating longer windows with higher intensity.
- A common mistake is forgetting the log offset (or using raw hours), which biases coefficients when observations have different exposure lengths.
- In ML workflows, the fitted mean predictions (via result.predict) are often used as calibrated expected counts; tail probabilities via poisson.cdf can drive alerting/anomaly thresholds.
set.seed(7) # reproducible randomness
# Simulate "support tickets per hour" with an exposure effect (hours observed)
n <- 300 # number of hourly observations
hours_observed <- runif(n, min = 0.5, max = 2.0) # exposure: partial to double-hour windows
is_weekend <- rbinom(n, size = 1, prob = 0.5) # 0=weekday, 1=weekend indicator
# Create a Poisson rate (lambda) that depends on weekend + exposure (hours)
base_rate_per_hour <- 3.0 # baseline tickets/hour
rate_multiplier_weekend <- 0.7 # fewer tickets on weekends
lambda <- base_rate_per_hour * (rate_multiplier_weekend ^ is_weekend) * hours_observed # expected count per window
# *** KEY LINE ***: draw counts from a Poisson distribution with mean=lambda for each observation
tickets <- rpois(n, lambda = lambda) # Poisson-distributed counts
# Put data into a data.frame for modeling/analysis
df <- data.frame(tickets = tickets, weekend = is_weekend, hours = hours_observed)
# Fit a Poisson GLM with a log link and an offset for exposure (log(hours))
fit <- glm(tickets ~ weekend + offset(log(hours)), family = poisson(link = "log"), data = df)
# Predict expected tickets for weekday vs weekend at 1 hour exposure
new_df <- data.frame(weekend = c(0, 1), hours = c(1.0, 1.0))
pred_mean <- predict(fit, newdata = new_df, type = "response") # mean counts on original scale
print(coef(fit))
print(pred_mean)
print(1 - ppois(7, lambda = pred_mean[1])) # P(tickets >= 8 in 1 hour | weekday)
Code Notes
- The key result is the count simulation via rpois and the fitted coefficients from glm, which quantify how the expected count changes with the weekend indicator.
- The offset(log(hours)) term is critical when observations have different exposure durations; it keeps the model’s mean aligned to a rate per unit time.
- A common pitfall is interpreting coef(fit) on the original count scale; with a log link, effects are multiplicative after exponentiation.
- In production ML, predict(..., type="response") provides expected counts for ranking/forecasting, while tail probabilities via ppois can be used for threshold-based alerts.