Last active
August 20, 2022 08:05
-
-
Save nathants/2732aae8b0e41d83b066e4a2e5d69ecf to your computer and use it in GitHub Desktop.
>> echo 1 1 1 2 2 2 4 4 4 4 8 8 8 8 16 16 16 16 | tr ' ' '\n' | plot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# type: ignore | |
# The MIT License (MIT) | |
# Copyright (c) 2022-present Nathan Todd-Stone | |
# https://en.wikipedia.org/wiki/MIT_License#License_terms | |
import math | |
import argh # pip install argh | |
import sys | |
import blessed # pip install blessed | |
term = blessed.Terminal() | |
def avg(xs): | |
return sum(xs) / len(xs) | |
def avg_window(vals, size): | |
for i in range(len(vals)): | |
xs = [vals[i]] | |
for j in range(1, size + 1): | |
try: | |
xs.append(vals[i + j]) | |
except IndexError: | |
pass | |
if i - j >= 0: | |
try: | |
xs.append(vals[i - j]) | |
except IndexError: | |
pass | |
vals[i] = avg(xs) | |
return vals | |
def chunks(val, num_chunks): | |
size = len(val) | |
step = math.ceil(size / num_chunks) | |
return (t | |
for i in range(num_chunks) | |
for t in [tuple(val[step * i:step * (i + 1)])] | |
if t) | |
def main(offset_height=20, smoothing=0, char='#', discard_extremes_chunks=0): | |
""" | |
usage: | |
>> echo 1 1 1 2 2 2 4 4 4 4 8 8 8 8 16 16 16 16 | tr ' ' '\n' | plot | |
""" | |
width = term.width | |
height = term.height - offset_height | |
# parse input | |
data = [] | |
for line in sys.stdin: | |
data.append(float(line)) | |
if len(data) < width: | |
width = len(data) | |
# screen is array of columns | |
screen = [[' ' for _ in range(height)] | |
for _ in range(width)] | |
# find bounds and scale to put data on term height range | |
xs = list(sorted(avg(xs) for xs in chunks(data, width))) | |
if discard_extremes_chunks: | |
slice = int(width / discard_extremes_chunks) | |
xs = xs[slice:-slice] | |
val_min = min(xs) | |
val_max = max(xs) | |
try: | |
scale = height / (val_max - val_min) | |
except ZeroDivisionError: | |
print('fatal: not enough data') | |
sys.exit(1) | |
# chunk, avg, and avg_window data to term width range | |
vals = chunks(data, width) | |
vals = [avg(val) for val in vals] | |
vals = avg_window(vals, smoothing) | |
# scale data to term height range | |
for i, val in enumerate(vals): | |
val -= val_min | |
val *= scale | |
val = int(val - 1) | |
try: | |
screen[i][val] = char | |
except IndexError: | |
pass | |
# transpose columns to rows and print | |
print('_' * width) | |
screen = list(reversed(list(zip(*screen)))) | |
for i, row in enumerate(screen): | |
row = ''.join(row) | |
if i == 0: | |
header = str(int(val_max)) + ' ' | |
row = header + row[len(header):] | |
if i == len(screen) - 1: | |
header = str(int(val_min)) + ' ' | |
row = header + row[len(header):] | |
print(row) | |
print('_' * width) | |
if __name__ == '__main__': | |
argh.dispatch_command(main) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment