Sparkline is a term Edward Tufte invented 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.
It adds a bit of functionality not offered by the Bash script. The command-line arguments --min and --max let you set the scale of the chart.
Download with wiki-reaper: wiki-reaper -x 40990 0 > sparkline.tcl
#! /usr/bin/env tclsh
# Draw sparklines (small inline charts) with Unicode block characters.
# Copyright (c) 2014, 2021, 2023 D. Bohdan. License: MIT.
namespace eval sparkline {
namespace export create
variable version 1.0.0
variable ticks [list ▁ ▂ ▃ ▄ ▅ ▆ ▇ █]
variable tickMax [expr {[llength $ticks] - 1}]
}
proc sparkline::create {data {formalMin {}} {formalMax {}}} {
variable tickMax
set sorted [lsort -real $data]
set realMin [lindex $sorted 0]
set realMax [lindex $sorted end]
unset sorted
set min $formalMin
if {$min eq {}} {
set min $realMin
}
set max $formalMax
if {$max eq {}} {
set max $realMax
}
set repeatChar {}
if {$formalMin ne {}
&& ($formalMin > $realMax
|| ($formalMax ne {} && $formalMin > $formalMax))} {
set repeatChar { }
} elseif {$formalMax ne {} && $realMin > $formalMax} {
set repeatChar [tick 0 1 1]
} elseif {$min == $max} {
# Either the data are all the same or the formal min and max
# are.
set repeatChar [tick 0 $tickMax [expr { $tickMax / 2 }]]
}
if {$repeatChar ne {}} {
return [string repeat $repeatChar [llength $data]]
}
set result {}
foreach x $data {
append result [tick $min $max $x]
}
return $result
}
proc sparkline::tick {min max x} {
variable ticks
variable tickMax
set normalized [expr {
$x < $max
? int($tickMax * ($x - $min) / ($max - $min))
: $tickMax
}]
set tick [lindex $ticks $normalized]
if {$tick eq {}} {
set tick { }
}
return $tick
}
# The following code allows you to use this script from the command
# line.
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 in {/? -h -help --help help {}}} {
set minMax {[--min min] [--max max]}
set me [file tail [info script]]
puts "usage: $me $minMax value1 \[value2 ...\]"
puts " $me $minMax \"value1 \[value2 ...\]\""
puts " $me help"
puts " $me test"
puts " $me version"
exit 0
}
if {$argv eq {test}} {
proc assert {v1 v2} {
if {$v1 eq $v2} {
puts $v1
} else {
error [list assertion failed: $v1 ne $v2]
}
}
assert [sparkline::create {1 5 22 13 53}] {▁▁▃▂█}
assert [sparkline::create {0 30 55 80 33 150}] {▁▂▃▄▂█}
assert [sparkline::create {9 13 5 17 1}] {▄▆▂█▁}
assert [sparkline::create {1 1 1 1}] {▄▄▄▄}
assert [sparkline::create {1 1 1 1} -1 -1] {████}
assert [sparkline::create {1 1 1 1} 2 2] { }
assert [sparkline::create {1 2 3 4 5} 1 100] {▁▁▁▁▁}
assert [sparkline::create {1 2 3 4 5} 3 100] { ▁▁▁}
assert [sparkline::create {1 2 3 4 5} 0 2] {▄████}
assert [sparkline::create {1 2 3 4 5} -20 -10] {█████}
assert [sparkline::create {1 2 3 4 5} -10 -20] { }
assert [sparkline::create {1 2 3} {} {}] {▁▄█}
assert [sparkline::create {1 2 3} {} 3] {▁▄█}
assert [sparkline::create {1 2 3} 1 {}] {▁▄█}
assert [sparkline::create {1 2 3} 1 3] {▁▄█}
assert [sparkline::create {1 2 3} 5 {}] { }
puts ok
exit 0
}
if {$argv eq {version}} {
puts $sparkline::version
exit 0
}
set min {}
set max {}
while {[regexp -- {^--?(.*)$} [lindex $argv 0] flag flagTrimmed]} {
switch -- $flagTrimmed {
min {
set argv [lassign $argv _ min]
}
max {
set argv [lassign $argv _ max]
}
default {
puts [list unknown flag: $flag]
exit 1
}
}
}
if {[llength $argv] == 1} {
set argv [lindex $argv 0]
}
puts [sparkline::create $argv $min $max]
}