random distributions: expovariate, triangular, …

Each is a few lines of Python on top of random(): an inverse transform (expovariate, paretovariate, weibullvariate, triangular) or a rejection loop (vonmisesvariate). Watch the parameter conventions: expovariate takes a RATE, triangular takes (low, high, mode).

random functionsAll Python 3 versions (expovariate default lambd=1.0 since 3.12)Live demo
Common call
random.expovariate(1 / 5)
Returns
float
Replaces
Hand-written inverse-CDF formulas
Watch out
expovariate(5) has mean 0.2, not 5
random.expovariate(lambdlambd — expovariate: the rate, 1 / desired mean; nonzero (a negative rate gives values <= 0). Default since 3.12.type: float · default: 1.0=1.0) / triangular(low=0.0, high=1.0, mode=None) / lognormvariate(mu, sigma) / paretovariate(alpha) / weibullvariate(alpha, beta) / vonmisesvariate(mu, kappa)
→ float

Demo

Live evaluation
Waiting times with rate lambd: the mean is 1 / lambd.
Try:
Inputs
seedint | stran int or a string
lambdfloatrate = 1 / mean
Code
import random
random.seed(42)
[round(random.expovariate(1.0), 4) for _ in range(5)]
Result
[1.0201, 0.0253, 0.3216, 0.2526, 1.3336]

expovariate(1.0) and expovariate(0.2) use the same random() values, so the second list is (up to rounding) the first times 5. A negative rate mirrors them below zero, and rate 0 divides by zero. Around mu = 0 the von Mises angles wrap: values near 2*pi (about 6.2832) are close to 0. weibullvariate with shape 1 is the exponential distribution. Results are rounded because log, exp, cos, acos and ** come from the platform C library and can differ in the last digit.

Parameters

NameTypeRequiredDescription
lambdfloatno (1.0)expovariate: the rate, 1 / desired mean; nonzero (a negative rate gives values <= 0). Default since 3.12.
low, high, modefloatno (0.0, 1.0, None)triangular: the bounds and the peak; mode=None means the midpoint.
mu, sigmafloatyeslognormvariate: mean and standard deviation of the underlying normal (of log(X)); sigma > 0.
alphafloatyesparetovariate: shape. weibullvariate: scale.
betafloatyesweibullvariate: shape.
mu, kappafloatyesvonmisesvariate: mean angle in radians and concentration >= 0; kappa <= 1e-6 gives a uniform angle in [0, 2*pi).

Return value

float — One draw from the distribution.

Common patterns

Arrival times of a Poisson process
Exponential gaps with mean 5.6 (the queue simulation from the random docs).
import random
arrival = 0.0
for _ in range(1000):
    arrival += random.expovariate(1.0 / 5.6)
Three-point estimate
Optimistic, pessimistic and most likely duration of a task.
import random
days = random.triangular(3, 15, 5)  # low, high, mode
Positive, skewed sizes
File sizes, incomes, response times: log-normal.
import random
size_kb = random.lognormvariate(4.0, 1.2)
Random wind direction
Mostly from the west (pi radians), with spread controlled by kappa.
import math
import random
direction = random.vonmisesvariate(math.pi, 2.0)

Examples

1. expovariate: default rate 1.0 (3.12+)
import random random.seed(42) random.expovariate()
Returns
1.020060287274801
2. Rate, not mean: mean 1 / lambd
import random random.seed(42) round(sum(random.expovariate(0.5) for _ in range(10000)) / 10000, 1)
Returns
2.0
3. triangular(low, high, mode)
import random random.seed(42) random.triangular(0, 10, 2)
Returns
4.6291661612586354
4. triangular defaults: 0, 1, midpoint
import random random.seed(42) random.triangular()
Returns
0.5753983033817951
5. lognormvariate
import random random.seed(42) random.lognormvariate(0, 0.5)
Returns
1.1305035702580866
6. paretovariate: never below 1.0
import random random.seed(42) random.paretovariate(3)
Returns
1.4049758243344401
7. weibullvariate
import random random.seed(42) random.weibullvariate(1, 1.5)
Returns
1.0133292060998442
8. vonmisesvariate: an angle in radians
import random random.seed(42) random.vonmisesvariate(0, 4)
Returns
5.535325158587151

Pitfalls

1. Passing the mean to expovariate
The parameter is the rate lambda = 1 / mean. For waits that average 5 seconds pass 1 / 5, not 5.
expovariate(5)
import random
random.seed(42)
round(sum(random.expovariate(5) for _ in range(10000)) / 10000, 1)
0.2
expovariate(1 / 5)
import random
random.seed(42)
round(sum(random.expovariate(1 / 5) for _ in range(10000)) / 10000, 1)
5.0
2. triangular(low, mode, high)
The order is (low, high, mode). With the peak passed second, mode lies outside the bounds and the results leave the range.
triangular(0, 2, 10)
import random
random.seed(42)
max(random.triangular(0, 2, 10) for _ in range(1000)) > 2
True
triangular(0, 10, 2)
import random
random.seed(42)
max(random.triangular(0, 10, 2) for _ in range(1000)) <= 10
True
3. lognormvariate mu is not the mean of the result
mu and sigma describe log(X). Large mu overflows math.exp.
mu=1000
import random
random.seed(42)
random.lognormvariate(1000, 1)
OverflowError: math range error
mu=log(1000)
import math
import random
random.seed(42)
round(random.lognormvariate(math.log(1000), 0.5))
1131

When to use

Use it
  • Simulation inputs: arrivals (expovariate), task durations (triangular), sizes (lognormvariate)
  • Reliability and lifetimes: weibullvariate; wealth and popularity tails: paretovariate
  • Directions and times of day on a circle: vonmisesvariate
Reach for something else
  • Normal noise → gauss / normalvariate
  • Proportions between 0 and 1 → betavariate; positive skewed with a shape → gammavariate
  • Counts of successes → binomialvariate

Notes

CPython impl
Lib/random.py: expovariate = -log(1.0 - random()) / lambd; triangular inverts the CDF with one sqrt; lognormvariate = exp(normalvariate(mu, sigma)); paretovariate = (1.0 - random()) ** (-1.0 / alpha); weibullvariate = alpha * (-log(1.0 - random())) ** (1.0 / beta); vonmisesvariate is a rejection loop after N. I. Fisher (Statistical Analysis of Circular Data) using cos, exp and acos
Platforms
triangular uses only + - * / and sqrt, so it is exact everywhere. The others call log, exp, cos, acos or pow from the C library, whose last digit can differ between Windows, Linux and macOS for a small fraction of inputs; the full-precision examples on this page were verified on Windows and Linux CPython
Errors
ZeroDivisionError: float division by zero for expovariate(0), paretovariate(0) and weibullvariate(alpha, 0). OverflowError when a result exceeds the float range: math range error from exp (lognormvariate); from ** (paretovariate or weibullvariate with tiny parameters) the text is platform-dependent, (34, 'Result too large') on Windows and (34, 'Numerical result out of range') on Linux

FAQ

The rate of the exponential distribution, 1 divided by the desired mean (the name avoids the keyword lambda). expovariate(0.5) has mean 2; since Python 3.12 the default is 1.0.