module  aeh  (asсii event histograms)

aeh.

A small Python utility for plotting streams of numbers as ASCII histograms — in pipes, in notebooks, in logs.

v 2.1.0 python 3.10+ pure-python, no deps MIT

aeh grew out of the habit of staring at log files and wishing the distribution of latencies were in front of you, right there, in the same terminal, without leaving for a notebook. It is not a plotting library. It is a histogram drawer.

Installation

# from PyPI
pip install aeh

# or for the impatient — single-file, vendorable
curl -O https://aeh.mectest.ru/dl/aeh.py

From the shell

The CLI reads one number per line on stdin and prints a histogram:

$ awk '{print $7}' nginx.log | aeh --bins 20

  count: 41,283   min: 0.4   max: 12.1   mean: 1.83   p50: 1.4   p99: 7.8

   0.4 ┤▏ 12
   0.9 ┤█▌ 1,484
   1.4 ┤████████████████▎ 18,221
   1.9 ┤█████████▎ 10,388
   2.4 ┤████▎ 4,761
   2.9 ┤██▏ 2,401
   3.4 ┤█▏ 1,210
   3.9 ┤▌ 612
   4.4 ┤▎ 387
   4.9 ┤▎ 281
   5.4 ┤▏ 187
   5.9 ┤▏ 142
   6.4 ┤▏ 96
   6.9 ┤▏ 78
   7.4 ┤▏ 62
   7.9 ┤▏ 41
   8.4 ┤▏ 28
   8.9 ┤  18
   9.4 ┤  9
   9.9 ┤  6
  10.4+ → 60 (clipped)

The output is plain UTF-8 — works in tmux, journald, less, pagers, copy-paste into a Slack message. No images, no escape sequences, no colour.

From Python

from aeh import histogram

durations = [r.duration_ms for r in records]
print(histogram(durations, bins=20, width=60))

The function returns a multi-line string. It does not write to stdout, does not touch any global state, and does not raise on empty input — empty input yields an empty string. The intent is to be quietly composable.

Logarithmic axis

print(histogram(durations, bins=30, scale="log"))

Switches to log10(1 + x) binning. Useful when the long tail of your distribution is, well, long.

"I keep aeh aliased on every server I touch. It is the smallest tool that has consistently made the right kind of difference." — a kind colleague, 2025

Reference

argumentdefaultnotes
bins20or pass a list of edges
widthautoterminal width minus margins
scale"linear""linear" / "log"
clip0.999quantile beyond which to collapse
statsTrueprint the count / min / max header

Why

Because matplotlib over SSH is silly. Because histogram(xs) is the question I ask of a sequence of numbers an unreasonable number of times per day. Because everything else either wants a notebook, or wants to be a "unified data visualisation framework", and neither of those is what fits in a pipe.

Changelog