Sunday, 30 January 2011

Talking to gnuplot by pipes

Drawing a graph shouldn't be difficult and Gnuplot indeed does make it simple to draw excellent graphs of all sorts of data. This little Python snippet shows how simple it is to talk to a Gnuplot subprocess from a Python program to draw a graph.

If you have an elaborate dataset that you want to explore in various ways it is probably easiest to run Gnuplot and refer to this file in the plot command. However in may other situations it is often more convenient to let Python talk to gnuplot via a pipe, writing commands and data directly to a gnuplot subprocess, with the possible added benefit that running Gnuplot as a complete separate process may utilize a multi-core processor better than Python can on its own.

Gnuplot is capable of interacting with another process over a pipe although on Windows you'll need not the main Gnuplot program but the executable pgnuplot.exe which is part of the distriubution.

Of course the wish to talk to Gnuplot this way is not a unique one. A fairly elaborate module exists but it doesn't look that actively maintained as it does not support Python 3.x. Moreover, it depends on the numpy package which is excellent but a bit heavy handed in many situations.

So what we are looking for is the simplest way to communicate with Gnuplot to draw a graph from an array of x,y values. The snippet below shows how this can be accomplished:

from subprocess import Popen,PIPE

gnuplot = r'C:\gp45-winbin\gp45-winbin\gnuplot\binary\pgnuplot'

data = [(x,x*x) for x in range(10)]

plot=Popen([gnuplot,'-persist'],stdin=PIPE,stdout=PIPE,stderr=PIPE)

plot.stdin.write(b"plot '-' with lines\n")
plot.stdin.write("\n".join("%f %f"%d for d in data).encode())
plot.stdin.write(b"\ne\n")
plot.stdin.flush()

The gnuplot variable points to the pipe capable gnuplot executable. On windows this is pgnuplot, on Unix-like operating systems the main executable will work just fine as well. The data variable hold a list of (x,y) tuples.

The important bit is in the Popen() call. The first argument is a list consisting of the program name and any options we wish to pass to this program. In this case we add the -persist option to prevent closing the window containing the graph. We also indicate that all input and output streams should be pipes.

The final statements show how we pass content to the input stream of the subprocess. Note that the write() method of such a file like object expects bytes, not a string, so we either provide bytes literals like b'' or use the encode() method of a string. The plot statement we send to Gnuplot is given '-' as the file name which will cause any subsequent lines sent to it to be interpreted as data, up to a line containing a single e.

2 comments:

  1. I've been using this, and it's great! One quick question though: I'm now using this code in a loop to make a couple hundred graphs, but it fails a lot. Is there a way to get the pipe to send the stderr somewhere I can read it, like a file?

    Thanks!

    ReplyDelete