#!/usr/bin/env python3 """ reduce.py - process a stream, and find the min, max, sum or whatever else """ import sys from math import inf, sqrt from pathlib import Path def num(s): try: return int(s) except ValueError: return float(s) def num_or_str(s): try: return int(s) except ValueError: pass try: return float(s) except ValueError: pass return s def reduce(istream, fn, acc=None, strip=True, numbers=True): if acc is None: acc = next(istream) for s in istream: if strip: s = s.strip() v = s if numbers: v = num_or_str(v) try: acc = fn(acc, v) except TypeError: if acc in (inf, -inf): acc = v else: acc = fn(str(acc), s) return acc fn_min = min fn_max = max def min(istream): return reduce(istream, fn_min, inf) def max(istream): return reduce(istream, fn_max, -inf) def sum(istream): return reduce(istream, lambda acc,v: acc+v, 0) def mean(istream): n, tot = reduce(istream, lambda acc,v: (acc[0]+1, acc[1]+v), (0,0)) return tot/n def hypot(istream): tot_sq = reduce(istream, lambda acc,v: acc+v*v, 0) return sqrt(tot_sq) def rms(istream): n, tot_sq = reduce(istream, lambda acc,v: (acc[0]+1, acc[1]+v**2), (0,0)) return sqrt(tot_sq/n) def sd_sample(istream): n, tot, tot_sq = reduce(istream, lambda acc,v: (acc[0]+1, acc[1]+v, acc[2]+v**2), (0,0,0)) return sqrt((n * tot_sq - tot ** 2) / (n * (n - 1))) def sd(istream): n, tot, tot_sq = reduce(istream, lambda acc,v: (acc[0]+1, acc[1]+v, acc[2]+v**2), (0,0,0)) return sqrt((n * tot_sq - tot ** 2) / (n ** 2)) def main(): prog = Path(sys.argv[0]) name = prog.stem if name == "reduce" and len(sys.argv) > 1: name = sys.argv[1] if name == "min": print(min(sys.stdin)) elif name == "max": print(max(sys.stdin)) elif name == "sum": print(sum(sys.stdin)) elif name == "mean": print(mean(sys.stdin)) elif name == "hypot": print(hypot(sys.stdin)) elif name == "rms": print(rms(sys.stdin)) elif name == "sd": print(sd(sys.stdin)) elif name == "sd_sample": print(sd_sample(sys.stdin)) else: print(f"Usage: {prog.name} (min|max|sum|mean|hypot|rms|sd|sd_sample)", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()