Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
bash script that generates an ASCII-art histogram (bar chart) from input data.
#!/bin/bash
#-------------------------------------------------------------------------------
# draw-histogram - generates an ASCII-art histogram (bar chart) from input data.
#-------------------------------------------------------------------------------
# Expects a series of lines of the form `<category-name> <count>` to passed
# via stdin.
#
# Generates a table the looks something like the following:
#
# +-------+----+
# | foo | 10 | ##########
# | bar | 21 | #####################
# | xyzzy | 12 | ############
# +-------+----+
#
# Where the width of the category and count fields is automatically determined
# and the number of hash-marks (`#`) is scaled to fit with the current width
# of the terminal.
#
# Requires `tput` and `bc`
#
# Example:
#
# echo -e "foo 10\nbar 21\nxyzzy 12" | draw-histogram
#
#-------------------------------------------------------------------------------
# rodw <https://gist.github.com/rodw> rev. 1 / 2018-02-26
#-------------------------------------------------------------------------------
INPUT=$(cat)
# determine the largest count in the input data
MAX_VALUE=$(echo "$INPUT" | sort -k2 -n | tail -1 | awk '{print $2}')
# determine the width of the largest count in the input data
MAX_VALUE_WIDTH=$(echo "$MAX_VALUE" | wc -c)
MAX_VALUE_WIDTH=$(($MAX_VALUE_WIDTH-1))
# determine the width of the largest category name in the input data
MAX_CAT_WIDTH=$(echo "$INPUT" | awk '{printf("%s\n",$1)}' | wc -L)
# calculate the width of the label section ('| <CAT> | <CNT> |`)
LABEL_WIDTH=$((MAX_VALUE_WIDTH + MAX_CAT_WIDTH + 7))
# determine how many columns are available for the bar chart (with some extra padding)
NUM_COLS=$(($(tput cols) - LABEL_WIDTH - 5 ))
# calculate the number of elements each character in the chart represents, with a minimum value of 1
STEP=$( echo -e "1\n`bc -l <<< "($MAX_VALUE / $NUM_COLS)"`" | sort -n | tail -1)
# print the table header
echo "" | awk -v VW=$MAX_VALUE_WIDTH -v CW=$MAX_CAT_WIDTH '{printf("+ %*s + %*s +", CW, "", VW, "")}' | tr ' ' '-'
# print the table itself
echo "$INPUT" | awk -v VW=$MAX_VALUE_WIDTH -v CW=$MAX_CAT_WIDTH '{printf("\n| %*s | %*d |", CW, $1, VW, $2)}' | awk -v STEP=$STEP '{printf("\n%s ", $0) ; for (i = 0; i<$4; i+=STEP) { printf("#")};}' | grep -v "^\$"
# print the table footer
echo "" | awk -v VW=$MAX_VALUE_WIDTH -v CW=$MAX_CAT_WIDTH '{printf("+ %*s + %*s +", CW, "", VW, "")}' | tr ' ' '-'
# print a trailing newline
echo ""
@rodw

This comment has been minimized.

Copy link
Owner Author

@rodw rodw commented May 29, 2019

NOTE: On MacOS (where wc -L is not supported by default) you will want to:

  1. brew install coreutils
  2. Replace wc -L in line 36 with gwc -L (where gwc is the GNU-based wc that was installed with coreutils).

otherwise you'll see "wc: illegal option -- L" in stderr and the category names won't be padded properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment