2,117 bytes · the Python implementation · view raw
import math
from typing import Sequence
from .stats_standard_deviation_types import DeviationKind
POW10 = [1.0, 10.0, 100.0, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12]
def _round_to(x: float, decimals: int) -> float:
# Half away from zero on the binary64 value. Python's round() is# half-even and would give round(0.5) == 0; this does not.
scale = POW10[decimals]
y = x * scale
r = float(math.floor(y))
if y - r >= 0.5:
r += 1.0return r / scale + 0.0def standard_deviation(values: Sequence[float], kind: DeviationKind, decimals: int) -> float:
"""Population or sample standard deviation, two-pass. The one-pass "mean of squares minus square of mean" shortcut cancels catastrophically on large, close values; two passes do not. """if isinstance(values, (str, bytes)) ornot isinstance(values, (list, tuple)):
raise TypeError("values must be a list of numbers")
for v in values:
if isinstance(v, bool) ornot isinstance(v, (int, float)) ornot math.isfinite(v):
raise TypeError("values must be finite numbers, received %r" % (v,))
if kind notin ("population", "sample"):
raise ValueError('unknown standard deviation kind "%s"' % (kind,))
if isinstance(decimals, bool) ornot isinstance(decimals, int) or decimals < 0or decimals > 12:
raise ValueError("decimals must be a whole number from 0 to 12, received %r" % (decimals,))
n = len(values)
if n == 0:
raise ValueError("values must not be empty")
if kind == "sample"and n < 2:
raise ValueError("sample standard deviation needs at least 2 values")
# Plain left-to-right float additions; sum() on ints would be exact and# math.fsum compensated, and either would differ from the other languages.
total = 0.0for v in values:
total = total + float(v)
mean = total / n
squares = 0.0for v in values:
d = float(v) - mean
squares = squares + d * d
divisor = n - 1if kind == "sample"else n
return _round_to(math.sqrt(squares / divisor), decimals)