Version 17 of Sparkline

Updated 2021-08-14 10:44:31 by dbohdan

Sparkline is a term Edward Tufte invented with for "small, high resolution graphics embedded in a context of words, numbers, images". On the Internet, however, the term has come to mean rough text charts made with block characters, like this: ▁▂▃▅▂▇.

dbohdan 2014-12-29: This particular implementation was inspired by using one for Bash (sadly, incompatible with the POSIX sh). It works in both Tcl 8.4+ and recent versions of Jim Tcl.

Download with wiki-reaper: wiki-reaper -x 40990 0 > sparklines.tcl

#! /usr/bin/env tclsh

namespace eval sparklines {
    namespace export create

    variable version 0.0.6
    variable ticks [list ▁ ▂ ▃ ▄ ▅ ▆ ▇ █]
    variable tickMax [expr {[llength $ticks] - 1}]
}

proc sparklines::create data {
    variable ticks
    variable tickMax

    set sorted [lsort -real $data]
    set min [lindex $sorted 0]
    set max [lindex $sorted end]

    if {$min == $max} {
        # All data points are the same.
        return [string repeat \
            [lindex $ticks [expr { int($tickMax / 2) }]] \
            [llength $data]]
    }

    set result {}
    foreach x $data {
        set xNormalized [expr {
            int($tickMax * ($x - $min) / ($max - $min))
        }]
        append result [lindex $ticks $xNormalized]
    }

    return $result
}


# The following code allows you to use this script from the command line much
# like the Bash version mentioned above.
proc main-script? {} {
    # From https://wiki.tcl-lang.org/40097.
    global argv0

    if {[info exists argv0]
        && [file exists [info script]] && [file exists $argv0]} {
        file stat $argv0        argv0Info
        file stat [info script] scriptInfo

        return [expr {
            $argv0Info(dev) == $scriptInfo(dev)
            && $argv0Info(ino) == $scriptInfo(ino)
        }]
    }

    return 0
}

if {[main-script?]} {
    if {$argv eq {}} {
        puts "usage: $argv0 value ?value...?"
        puts "       $argv0 \"value ?value...?\""
        exit 0
    }

    if {$argv eq "test"} {
        proc assert {v1 v2} {
            if {$v1 eq $v2} {
                puts $v1
            } else {
                error "assertion failed: \"$v1\" ne \"$v2\""
            }
        }

        assert [sparklines::create {1 5 22 13 53}] {▁▁▃▂█}
        assert [sparklines::create {0 30 55 80 33 150}] {▁▂▃▄▂█}
        assert [sparklines::create {9 13 5 17 1}] {▄▆▂█▁}
        assert [sparklines::create {1 1 1 1}] {▄▄▄▄}

        puts ok
        exit 0
    }

    if {[llength $argv] == 1} {
        set argv [lindex $argv 0]
    }

    puts [sparklines::create $argv]
}

See also