Erlang C in Python
Part of Erlang C, in every language — the same call-centre staffing formula, one language at a time. Here it is in Python: standard library only, nothing to pip install.
The job is the same as the Excel version: give it the offered call load and a service-level target, and it returns how many agents you need. The one wrinkle worth knowing is that a naïve factorial overflows for a large centre, so the Poisson terms are computed in log-space with a bundled Lanczos lgamma — which is why this leans on nothing but math.
"""Erlang C — call-centre staffing. Standard library only."""
import math
_G = 7
_C = [0.99999999999980993, 676.5203681218851, -1259.1392167224028,
771.32342877765313, -176.61502916214059, 12.507343278686905,
-0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7]
def lgamma(x):
"""ln Γ(x) — Lanczos approximation (g=7)."""
if x < 0.5:
return math.log(math.pi / math.sin(math.pi * x)) - lgamma(1 - x)
x -= 1
a = _C[0]
t = x + _G + 0.5
for i in range(1, 9):
a += _C[i] / (x + i)
return 0.5 * math.log(2 * math.pi) + (x + 0.5) * math.log(t) - t + math.log(a)
def _log_pois(k, u):
return k * math.log(u) - lgamma(k + 1) - u
def erlang_c(agents, traffic):
"""P(an arriving call has to wait)."""
rho = traffic / agents
if rho >= 1:
return 1.0
top = math.exp(_log_pois(agents, traffic))
cum = sum(math.exp(_log_pois(k, traffic)) for k in range(agents))
return top / (top + (1 - rho) * cum)
def service_level(agents, traffic, aht, target):
"""Fraction answered within `target` seconds."""
if agents <= traffic:
return 0.0
return 1 - erlang_c(agents, traffic) * math.exp(-(agents - traffic) * target / aht)
def asa(agents, traffic, aht):
"""Average speed of answer, seconds."""
if agents <= traffic:
return float("inf")
return erlang_c(agents, traffic) * aht / (agents * (1 - traffic / agents))
def agents_required(traffic, aht, sl_goal, asa_goal):
m = int(traffic) + 1
while m < 10000:
if service_level(m, traffic, aht, asa_goal) >= sl_goal \
and asa(m, traffic, aht) <= asa_goal:
return m
m += 1
return m
Using it
traffic is the offered load in Erlangs — calls aht / period, in consistent time units. For 100 calls in 30 minutes at a 180-second handle time, that’s 100 180 / (30 * 60) = 10. Then:
agents_required(traffic=10, aht=180, sl_goal=0.80, asa_goal=20) # -> 14
Thirteen agents miss the 80/20 target; fourteen clear it.
The maths, the worked example, and the same five functions in 29 other languages live on GitHub:
- Open the live calculator — type your numbers in.
- The code on GitHub — CC0, copy anything.