← Files Marketing CouncilARCHIVED FILE

scripts/funnel_math.py

1.63 KB · Oct 2, 2026 · 00:31 UTC

↓ Download file

#!/usr/bin/env python3
"""Calculate stage and overall funnel conversion rates."""
from __future__ import annotations
import argparse
import json
from collections.abc import Mapping


def calculate_funnel(stages: Mapping[str, float]) -> dict:
    if len(stages) < 2:
        raise ValueError("at least two ordered funnel stages are required")
    items = [(str(k), float(v)) for k, v in stages.items()]
    for name, value in items:
        if value < 0:
            raise ValueError(f"stage {name} must be >= 0")
    rates: dict[str, float] = {}
    for (prev_name, prev), (name, value) in zip(items, items[1:]):
        if value > prev:
            raise ValueError(f"stage {name} cannot exceed prior stage {prev_name}")
        if prev == 0:
            rate = 0.0 if value == 0 else None
        else:
            rate = value / prev
        rates[f"{prev_name}_to_{name}"] = rate
    first = items[0][1]
    last = items[-1][1]
    overall = (last / first) if first else 0.0
    return {"stages": dict(items), "stage_rates": rates, "overall_conversion": overall}


def main() -> None:
    p = argparse.ArgumentParser(description="Calculate funnel conversion rates from ordered stage=count pairs.")
    p.add_argument("stage", nargs="+", help="Ordered items like visits=1000 leads=100 customers=20")
    args = p.parse_args()
    stages = {}
    try:
        for item in args.stage:
            name, raw = item.split("=", 1)
            stages[name] = float(raw)
        result = calculate_funnel(stages)
    except (ValueError, TypeError) as exc:
        p.error(str(exc))
    print(json.dumps(result, indent=2, sort_keys=True))


if __name__ == "__main__":
    main()

SHA-256: 1e873a94abdf44cb8f111ed7a64370c96125531cd1bdce2256733426ef9dc4a8