Functional Weave
Code in Python

stats.standard-deviation@2.0.0

impl/python.py

1,972 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import math
from typing import Sequence

from .math_round_float import round_float  ← from math.round-float ^1.0.0 · built alongside by fune
from .stats_standard_deviation_types import DeviationKind


def 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)) or not isinstance(values, (list, tuple)):
        raise TypeError("values must be a list of numbers")
    for v in values:
        if isinstance(v, bool) or not isinstance(v, (int, float)) or not math.isfinite(v):
            raise TypeError("values must be finite numbers, received %r" % (v,))
    if kind not in ("population", "sample"):
        raise ValueError('unknown standard deviation kind "%s"' % (kind,))
    # Checked up front as well as in round_float (same wording), so a bad
    # decimals is reported before an empty list, as in 1.x.
    if isinstance(decimals, bool) or not isinstance(decimals, int) or decimals < 0 or 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.0
    for v in values:
        total = total + float(v)
    mean = total / n
    squares = 0.0
    for v in values:
        d = float(v) - mean
        squares = squares + d * d
    divisor = n - 1 if kind == "sample" else n
    # math.round-float rounds on the exact value of the double: 2.675 gives 2.67.
    return round_float(math.sqrt(squares / divisor), decimals)