5

I want to create a simple infographic in python. Matplotlib seems to have a lot of features but nothing that covers off my simple heatmap grid example.

The infographic is a simple 5 x 5 grid with numbers inside ranging from 0 to 1. The grid squares would then be coloured in 0=white 1=blue 0.5 being a pale blue.

Matplotlib could probably be used but I couldn't find or combine any examples that offered insight into generating this.

Any insight, example code or library direction would really help

Regards Matt

SilentGhost
  • 307,395
  • 66
  • 306
  • 293
Matt Alcock
  • 12,399
  • 14
  • 45
  • 61
  • How is the graphic going to be used? Are you going to put it in a document? On a web page? – dan-gph Nov 02 '09 at 15:03
  • Intally it will just be crated in to go in a document. In the future idealy this would be generated dynamically to go on a web page. Does the distribution mechasim make a big difference to the approach? – Matt Alcock Nov 02 '09 at 16:19
  • Yes it makes a difference. If were to be viewed only on a screen, then a bitmap format might be fine. If it's going to be put into a PDF, then a vector format would be better. Etc. – dan-gph Nov 02 '09 at 17:11
  • By now they do have such a heatmap with annotated cells that can contain numbers in range [0,1]. Here is an example that could be converted into an answer: https://matplotlib.org/3.1.1/gallery/images_contours_and_fields/image_annotated_heatmap.html#sphx-glr-gallery-images-contours-and-fields-image-annotated-heatmap-py – a.t. Dec 31 '20 at 14:12

3 Answers3

4

It depends what you need to do with the graph once you have it, Matplotlib allows you to interactively show the graph on the screen, save it in either vector, pdf or bitmap format, and more.

If you opt for this framework, imshow will do what you need, here is an example:

# Just some data to test:
from random import gauss
a = [[gauss(0, 10) for i in xrange(0, 5)] for j in xrange(0,5)]

from pylab import * # or just launch "IPython -pylab" from the command line

# We create a custom colormap:
myblue = cm.colors.LinearSegmentedColormap("myblue", {
    'red':   [(0, 1, 1), (1, 0, 0)], 
    'green': [(0, 1, 1), (1, 0, 0)],
    'blue':  [(0, 1, 1), (1, 1, 1)]})

# Plotting the graph:
imshow(a, cmap=myblue)

For further details on the colormap check this link, and here is the link for imshow - or simply use help(colors.LinearSegmentedColormap) and help(imshow).

alt text http://img522.imageshack.us/img522/6230/bluep.png

(note that this is the result with the standard options, you can add a grid, change the filtering and so on).


Edit

however I'm looking to display the numbers in the grid

To keep it simple:

for i in xrange(0,5):
    for j in xrange(0,5):
        text(i, j,
             "{0:5.2f}".format(a[i][j]),
             horizontalalignment="center",
             verticalalignment="center")
RedGlyph
  • 11,309
  • 6
  • 37
  • 49
  • This is great however I'm looking to display the numbers in the grid. The coloring is secondary. Also the coloring here is grandual rather than fixed for each gird square – Matt Alcock Nov 02 '09 at 16:15
  • Here you are, of course you have some flexibility in the way the grid is displayed, the format of the numbers, and so on. It would just be too long to explain here, there's a little show-off on their website: http://matplotlib.sourceforge.net/gallery.html – RedGlyph Nov 02 '09 at 16:32
  • One more comment, Jonathan's suggestion of using PyCairo could be more appropriate if you need to process images (great library by the way!). Matplotlib is more math-oriented as you might have guessed ;-) It also needs some getting used to, using IPython is a good way to try it out in a console - though not mandatory: http://ipython.scipy.org/moin/FrontPage – RedGlyph Nov 02 '09 at 16:37
  • Thanks Jonathan/Red. I'm torn between each I think I'll use the matplotlib method initally (i have that installed) and move to cario if I need to start adding in extra details (like annotations ets). Thanks to all! – Matt Alcock Nov 02 '09 at 17:48
  • I used this setting the interpolation to 'nearest' to ensure I got a heatmap feel rather than a granual effect. imshow(a, cmap=myblue, interpolation='nearest') Thanks again all! – Matt Alcock Nov 03 '09 at 16:30
2

PyCairo is your friend. Simple example:

from __future__ import with_statement
import cairo
img = cairo.ImageSurface(cairo.FORMAT_ARGB32,100,100)
g = cairo.Context(img)
for x in range(0,100,10):
    for y in range(0,100,10):
        g.set_source_rgb(.1 + x/100.0, 0, .1 + y/100.0)
        g.rectangle(x,y,10,10)
        g.fill()
with open('test.png','wb') as f:
    img.write_to_png(f)

output

You might find this tutorial helpful.

Jonathan Feinberg
  • 44,698
  • 7
  • 80
  • 103
  • This is great however I'm looking to display the numbers in the grid. The coloring is secondary. – Matt Alcock Nov 02 '09 at 16:16
  • 2
    @Matt: So ... Read the above code, check out the Cairo API, and adjust it to render the text rather than a filled rectangle. How hard could that be? – unwind Nov 02 '09 at 16:30
  • 1
    I concur with unwind, the best way is to try and make sure you'd feel at ease with whatever you are going to use for your project! – RedGlyph Nov 02 '09 at 16:39
  • Good call, sounds like cario has the power I need! I'll start training up! – Matt Alcock Nov 02 '09 at 17:38
2

One possibility would be to generate SVG from python. You can view SVG in Firefox or Inkscape.

Here's a quick-and-dirty example:

import random

def square(x, y, value):
    r, g, b = value * 255, value * 255, 255
    s = '<rect x="%d" y="%d" width="1" height="1" style="fill:rgb(%d,%d,%d);"/>' % (x, y, r, g, b)
    t = '<text x="%d" y="%d" font-size=".2" fill="yellow">%f</text>' % (x, y + 1, value)
    return s + '\n' + t

print('''
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg width="100%" height="100%" version="1.1" viewBox="0 0 5 5"
xmlns="http://www.w3.org/2000/svg">
''')
for x in range(0, 5):
    for y in range(0, 5):
        print(square(x, y, random.random()))

print('</svg>')

alt text http://www.imagechicken.com/uploads/1257184721026098800.png

dan-gph
  • 16,301
  • 12
  • 61
  • 79
  • This is a hack altough it generates exactly what I was after! I don't know what my distribution mech will ultimatly be so tieing this to html doesn't really help. Thanks all the same dangph – Matt Alcock Nov 02 '09 at 17:46
  • Nice! I'll point out that the Cairo library I mention in my answer has PDF and SVG "backends", so that the same code can be used to generate these different formats. But I like the didactic simplicity of your answer. +1 – Jonathan Feinberg Nov 02 '09 at 18:52