Mostly because it's fun, but the practical reasons are that you don't need matplotlib as a dependency, and it's useful to know how to do plots in the terminal.
Gnuplot has a dumb
terminal mode for ASCII plots. Note that I use 80x25 so this won't look right on smaller displays.
% seq 10 | awk '{print $1, $1*$1}' > square.txt # numbers 1-10 and their squares
% cat square.txt
1 1
2 4
3 9
4 16
5 25
6 36
7 49
8 64
9 81
10 100
% gnuplot -e 'set terminal dumb 80 25; plot "square.txt"'
100 +---------------------------------------------------------------------+
| + + + + + + + + |
90 |-+ "square.txt" A +-|
| |
80 |-+ A +-|
| |
70 |-+ +-|
| |
60 |-+ A +-|
| |
50 |-+ +-|
| A |
| |
40 |-+ A +-|
| |
30 |-+ +-|
| A |
20 |-+ +-|
| A |
10 |-+ A +-|
| A + + + + + + + |
0 +---------------------------------------------------------------------+
1 2 3 4 5 6 7 8 9 10
Gnuplot is meant to run scripts, which you can place inline with the -e
option. For 2D plots it wants to have the data in a space separated file, like above. To do it in a one-liner, I prefer to use process substitution:
% gnuplot -e "set terminal dumb 80 25; plot \"<(seq 10 | awk '{print \$1, \$1*\$1}')"\"
100 +---------------------------------------------------------------------+
| + + + + + + + + |
90 |-+ "<(seq 10 | awk '{print $1, $1*$1}')" A +-|
| |
80 |-+ A +-|
| |
70 |-+ +-|
...
Though escaping quotes etc becomes tricky very fast - choosing awk for the example made it even worse.
We can run it as a shell script as is:
!gnuplot -e "set terminal dumb 80 25; plot \"<(seq 10 | awk '{print \$1, \$1*\$1}')"\"
100 +---------------------------------------------------------------------+
| + + + + + + + + |
90 |-+ "<(seq 10 | awk '{print $1, $1*$1}')" A +-|
| |
80 |-+ A +-|
| |
70 |-+ +-|
| |
60 |-+ A +-|
| |
50 |-+ +-|
| A |
| |
40 |-+ A +-|
| |
30 |-+ +-|
| A |
20 |-+ +-|
| A |
10 |-+ A +-|
| A + + + + + + + |
0 +---------------------------------------------------------------------+
1 2 3 4 5 6 7 8 9 10
What would be more intersting is to plot a python variable. We could write a text file, or just turn it into a string and pass to gnuplot (note the f
string):
data = """
1 1
2 4
3 9
4 16
5 25
6 26 # on purpose
7 49
8 64
9 81
10 100
"""
q = f""" "set terminal dumb 80 25; plot '<(echo \\"{data}\\")'" """
!gnuplot -e {q}
100 +---------------------------------------------------------------------+
| + + + + + + + + |
90 |-+ A +-|
| |
80 |-+ A +-|
| |
70 |-+ +-|
| |
60 |-+ A +-|
| |
50 |-+ +-|
| A |
| |
40 |-+ +-|
| |
30 |-+ +-|
| A A |
20 |-+ +-|
| A |
10 |-+ A +-|
| A + + + + + + + |
0 +---------------------------------------------------------------------+
1 2 3 4 5 6 7 8 9 10
There ends up being lots of escaping because of how escape characters get "eaten"... for example the inner quotation marks get double espaped with backslashes.
From here, we can easily dress it up a bit to make a generic 2D plot. The difference here is that we've extended it to multiple series, allowed custom marker specification, and the option to pass extra commands to gnuplot.
import itertools
def gnuplot(series,markers=itertools.repeat("x"),extra=""):
pts = ["\n".join([f"{x} {y}" for (x,y) in s]) for s in series]
plots = ",".join([f" '<(echo \\\"{p}\\\")' pt \\\"{m}\\\" notitle " for p,m in zip(pts,markers)])
q = f""" "set terminal dumb 80 25; {extra}
plot {plots}"
"""
!gnuplot -e {q}
gnuplot([[(x,x*x) for x in range(1,11)]]) # has to be a list of lists
100 +---------------------------------------------------------------------+
| + + + + + + + + |
90 |-+ +-|
| |
80 |-+ x +-|
| |
70 |-+ +-|
| |
60 |-+ x +-|
| |
50 |-+ +-|
| x |
| |
40 |-+ x +-|
| |
30 |-+ +-|
| x |
20 |-+ +-|
| x |
10 |-+ x +-|
| x + + + + + + + |
0 +---------------------------------------------------------------------+
1 2 3 4 5 6 7 8 9 10
import numpy as np
outer = 10*np.random.randn(200,2)
inner = np.random.randn(50,2)
extra = "set xtics out; set ytics out; set yrange [-25:25]; set xrange [-25:25];"
gnuplot([outer,inner],markers=["o","x"], extra=extra)
+ + + + +
+--------------------------------------------------------------------+
| oo oo o |
20 +-| o o |-+
| o o o |
| o o oo o |
| o o o o ooo oo oo o |
10 +-|o o o o o o oo o |-+
| o o o o oo o oo o |
| o ooo o oo oo o ooo ooo o oo o |
| o o ooooo ooo xxxx o o o o |
0 +-| o oo o o xxxxxx o ooooooo o o o |-+
| o o o o o xx o o oo o o |
| o o oo o oo o oo oo o o o |
| o o o o ooo o o o o o o o |
-10 +-| oo o o o oo o o o |-+
| o o o o o o o |
| o o |
| o o o |
-20 +-| o o o |-+
| |
+--------------------------------------------------------------------+
+ + + + +
-20 -10 0 10 20
With no dependencies (other than gnuplot
and loosely itertools
) we can make cool ascii charts suitable for use in text files and display on a terminal, with the convenience of working in a notebook. The escaping gets messy, but haivng been sorted, I've found this good for basic 2D plots. Gnuplot has, .e.g bar plots as well that it can do in ASCII, which would be easy to add, though often don't look great.