Retrofitting variable outputs to Gnuplot graphs blog home

Posted Thursday, 06-Feb-2025 by Ingo Karkat

Gnuplot can graph data via a powerful scripting language. As a free and portable command-line tool, it's ideal for visualization; output defaults to its own graphical viewer application, but it also supports the common bitmap and vector graphics formats, and can even render ASCII-charts directly in the terminal! I started using it to visualize by-author statistics on commit messages and branch / pull request lifetimes, then for showing my personal household expenditures over time. With its flexible graphing capabilities, I'm sure I'll find many more opportunities for its use over time.

moti­vation

While some graphing is ad-hoc, I usually want to preserve the report to put it in a blog post or send it around. Also, statistics like the mentioned per-author ones depend on the repository; some have just a few authors, while others are huge, so the width required for the x-axis varies a lot. This means tweaking the size / aspect ratio of the graph to obtain a clear visualization; practically all output terminals (that's Gnuplot lingo for the rendering device) support a size parameter for that.

require­ments

However, as the same graphing script is reused for different repositories (so the same graph layout, but different data), or in case of my finances the data will grow over time, it's a bad idea to encode the graph size directly in the script itself. For separation of concerns, it should be possible to specify a custom size somewhere else, and also to change the output format. Gnuplot supports multiple input scripts (so we don't need to concatenate script fragments ourselves), but just establishing a convention of reading a ./output.gnuplot script with the set terminal <type> size <width>,<height> in it wouldn't be very user-friendly; even more so because many terminal types require additional parameters (like the output file name for graphics files). Therefore, I'd rather be able to pass the output file and/or graph size during the invocation, and have it derive the correct set terminal command from that. This also makes it easy to directly open the Gnuplot viewer while fiddling with the graphing script, and then change to a graphics file export at the end.

imple­menta­tion

My withGnuplotOutput script wraps any command and injects a Gnuplot wrapper that adds the set terminal command corresponding to the script's user-friendly output and/or size parameters:

Execute COMMAND (either gnuplot itself or an executable invoking gnuplot) while
every gnuplot invocation is modified to output to FILE / as characters / with
the GUI viewer instead of the default Gnuplot terminal.

Usage: withGnuplotOutput ""|FILE.(png,svg)|dumb|term|gui|fullscreen [-o|--open] [-v|--verbose] [-f|--fullscreen|-s|--size WIDTHxHEIGHT|WIDTH,HEIGHT]
       -c|--command "COMMANDLINE" [-c ...] | --exec SIMPLECOMMAND [...] ; [--exec ...] | [--] SIMPLECOMMAND [...] [-?|-h|--help]

    --open|-o		Open the output FILE after successful graphing.
    --verbose|-v	Print the (full absolute) filespec of the output FILE
			(only when successful and graphing into a file).
    fullscreen|--fullscreen|-f
			Use the (primary) display's resolution as the size.
    --size|-s WIDTHxHEIGHT|WIDTH,HEIGHT
			Set the output terminal size to WIDTHxHEIGHT pixels
			(characters for dumb|term).
			You can preset the default size via
			$WITHGNUPLOTOUTPUT_{OUTPUTTYPE}_GRAPH_SIZE or (as a
			generic fallback) $WITHGNUPLOTOUTPUT_GRAPH_SIZE

With this, if I want to persist my finance data that's plotted via $ gnuplot finances.gnuplot, I simply change this to $ withGnuplotOutput finances.svg gnuplot finances.gnuplot. The resulting gnuplot invocation becomes:

/usr/bin/gnuplot -e set\ terminal\ svg\ background\ \'white\'\;\ set\ output\ \'finances.svg\' finances.gnuplot

If I use $ withGnuplotOutput term -s 40x20 gnuplot finances.gnuplot instead, I get a (way too small) plot in the terminal:

/usr/bin/gnuplot -e 'set terminal dumb ansi size 40,20' finances.gnuplot

The main script translates the user-supplied parameters into the corresponding Gnuplot commands, exports them as environment variables, and then adds the wrapper script to the front of the PATH. Any gnuplot command, whether it's directly passed like in my finance example or through another script like in my Git tools is now intercepted by the wrapper script. The wrapper just reassembles the passed environment variables into the actual gnuplot command (which got resolved to its absolute filespec prior to the PATH modification) plus added set terminal command and invokes those. I've also implemented small usability enhancements there like printing the resulting output filespec (--verbose) or directly opening the rendered graphics file (--open).

invocation sequence
withGnuplotOutput → Gnuplot command / script → gnuplot-wrapper → /usr/bin/gnuplot
	

conclusion

Even a powerful API cannot anticipate all use cases and handle them with the best usability. But ideally its expressiveness enables simple extensions that make its use convenient for the users. Gnuplot itself already has some (very basic) metaprogramming capabilities, and possibly environment variables could have been used to pass desired output formats and sizes in a shorter form than the complex set terminal … command itself. But then that metaprogram would have to be somehow included, too. A custom command like my withGnuplotOutput avoids the need to modify any scripts that invoke Gnuplot, and can offer a convenient and discoverable command-line interface for users. It's mostly just command-line argument parsing and PATH augmentation, and very easy to extend.

Ingo Karkat, 06-Feb-2025

ingo's blog is licensed under Attribution-ShareAlike 4.0 International

blog comments powered by Disqus