In my recent work at the lab, I’ve been doing some simple datavis to make sense of the various biometrics I’ve been gathering. I’m committed to python for this sort of thing, but I’ve found it wanting for a dirt simple drawing library, absurd as it may seem given the number of available graphics packages. The power of Processing / processing.py, Nodebox, Field, matplotlib, etc, mean that they end up imposing on, if not dictating, the flow of a program. I wanted a single import that gives me basic, pythonic drawing commands and which can show me the result in as lightweight a way as possible.
I ended up putting together my own, which is a wrapper for PIL and aggdraw.
The idea certainly wasn’t to match the drawing capabilities of Processing et al, and the interface has some serious limitations (there’s no animation, no context stack, performance is questionable, etc). But it does succeed in letting me take a chunk of data from numpy, normalize it, and feed directly to drawing commands with only a single line of setup.
For instance, to just graph a time series and check it out:
data = normalized numpy array at a given sampling rate
ctx = drawing.Context(1024, 768, relative=True, flip=True)
for x, y in enumerate(data):
ctx.line(float(x) / len(data), 0, float(x+1) / len(data), y)
This is a work in progress, and I may develop it further as requirements arise, but it will remain simple! Minimal documentation and a few easy examples are present in the sourcecode.
Edit: example of the module in action, visualizing fitbit data.
Update (11-07-13): animation
Thanks to built-in event capabilities of OpenCV, I’ve grafted on the ability to work with animation. ctx.frame() outputs the current canvas to a window. ctx.clear() clears it. Put those in a loop and off you go.
Random accumulating squares:
ctx = Context(640, 480)
x, y = random() * ctx.width, random() * ctx.height
width, height = random() * 200, random() * 200
fill = random(), random(), random(), random()
ctx.rect(x, y, width, height, fill=fill)