Functional Weave
Code in Python

charts.stack@1.0.0

impl/python.py

2,395 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 List, Sequence

from .charts_stack_types import StackedValue, StackOffset
from .math_round_float import round_float  ← from math.round-float ^1.0.0 · built alongside by fune


def stack(series: Sequence[Sequence[float]], offset: StackOffset) -> List[List[StackedValue]]:
    """Stack series position by position, as d3.stack does with the series in
    the given order: stackOffsetNone (zero), stackOffsetExpand and
    stackOffsetDiverging. Running totals stay unrounded; only the returned
    edges are rounded."""
    if offset not in ("zero", "expand", "diverging"):
        raise ValueError("stack offset must be zero, expand or diverging, received %r" % (offset,))
    n = len(series)
    if n == 0:
        return []
    m = len(series[0])
    for s in series:
        if len(s) != m:
            raise ValueError("every series must have the same number of values")
        for v in s:
            if isinstance(v, bool) or not isinstance(v, (int, float)) or not math.isfinite(v):
                raise TypeError("stack values must be finite numbers, received %r" % (v,))
            if offset == "expand" and v < 0:
                raise ValueError("expand needs values of zero or more, received %r" % (v,))
    lower = [[0.0] * m for _ in range(n)]
    upper = [[0.0] * m for _ in range(n)]
    for j in range(m):
        if offset == "diverging":
            up = 0.0
            down = 0.0
            for i in range(n):
                v = float(series[i][j])
                if v > 0:
                    lower[i][j] = up
                    up += v
                    upper[i][j] = up
                elif v < 0:
                    upper[i][j] = down
                    down += v
                    lower[i][j] = down
                else:
                    lower[i][j] = 0.0
                    upper[i][j] = 0.0
        else:
            total = 0.0
            if offset == "expand":
                for i in range(n):
                    total += float(series[i][j])
            top = 0.0
            for i in range(n):
                v = float(series[i][j])
                if offset == "expand":
                    v = v / total if total > 0 else 0.0
                lower[i][j] = top
                top += v
                upper[i][j] = top
    return [
        [StackedValue(y0=round_float(lower[i][j], 6), y1=round_float(upper[i][j], 6)) for j in range(m)]
        for i in range(n)
    ]