views:

115

answers:

2

Hi guys!

I want to do a pie chart in matplotlib.
This pie chart will be a representation of two variables: male and female.

That's easy to do :)

What I would like to do next, I'm not even sure if it's possible to do with matplotlib, I would like to make these two variables clickable so if I click on male, I would see another page with information about this, same thing with female.

Image map isn't a solution since this variables may change in the future.

Anyone has any idea how to do this? If it's possible with matplotlib or what program would you recommend.

Thank you!

+1  A: 

You can do this with an imagemap or HTML element overlay controlled by JavaScript/jQuery.

Essentially, send your chart data to the page along with the chart image, and use JS to create the elements with the links according to the specification of the data.

It's a bit harder than the bar graphs I've done this to before, but should work fine.

Mark Snidovich
if the data was always the same it would be really easy, an image map would do the trick. But the data may vary, so it has to calculate alone. js or jq would do this?
Pat
Yes, you can create and modify elements in any way imaginable using javascript/jquery. I'll see if I can come up with a code sample later on.
Mark Snidovich
+3  A: 

While it's not really in a workably stable state yet, have a look at the html5 canvas backend for matplotlib. It looks interesting, anyway, and will probably be the best way to do this sort of thing (interactive webpage with a matplotlib plot) in the future.

In the meantime, as @Mark suggested, it's not too hard to dynamically generate an imagemap for the wedges of a pie plot.

Here's a rough example, that I'm sure you could adapt to whatever web framework you're using.

import matplotlib.pyplot as plt

def main():
    # Make an example pie plot
    fig = plt.figure()
    ax = fig.add_subplot(111)

    labels = ['Beans', 'Squash', 'Corn']
    wedges, plt_labels = ax.pie([20, 40, 60], labels=labels)
    ax.axis('equal')

    make_image_map(fig, wedges, labels, 'temp.html')

def make_image_map(fig, wedges, labels, html_filename):
    """Makes an example static html page with a image map of a pie chart.."""
    #-- Save the figure as an image and get image size ------------------------
    # Be sure to explictly set the dpi when saving the figure
    im_filename = 'temp.png'
    fig.savefig(im_filename, dpi=fig.dpi)

    # Get figure size...
    _, _, fig_width, fig_height = fig.bbox.bounds

    #-- Get the coordinates of each wedge as a string of x1,y2,x2,y2... -------
    coords = []
    for wedge in wedges:
        xy = wedge.get_verts() 

        # Transform to pixel coords
        xy = fig.get_transform().transform(xy) 

        # Format into coord string and convert to <0,0> in top left...
        xy = ', '.join(['%0.2f,%0.2f' % (x, fig_height - y) for x, y in xy])
        coords.append(xy)

    #-- Build web page --------------------------------------------------------
    header = """
    <html>
    <body>
    <img src="{0}" alt="Pie Chart" usemap="#pie_map" width="{1}" height="{2}" />
    """.format(im_filename, fig_width, fig_height)

    # Make the image map
    map = '<map name="pie_map">\n'
    for label, xy in zip(labels, coords):
        href = 'http://images.google.com/images?q={0}'.format(label)
        area = '<area shape="poly" coords="{0}" href="{1}" alt="{2}" />'
        area = area.format(xy, href, label)
        map += '    ' + area + '\n'
    map += '</map>\n'

    footer = """
    </body>
    </html>"""

    # Write to a file...
    with file(html_filename, 'w') as outfile:
        outfile.write(header + map + footer)

if __name__ == '__main__':
    main()

Edit: I just realized that you might not be referring to embedding the plot into a web page... (I assumed that you were from the "display another page" bit in your question.) If you want more of a desktop app, without having to mess with a "full" gui toolkit, you can do something like this:

import matplotlib.pyplot as plt

def main():
    # Make an example pie plot
    fig = plt.figure()
    ax = fig.add_subplot(111)

    labels = ['Beans', 'Squash', 'Corn']
    wedges, plt_labels = ax.pie([20, 40, 60], labels=labels)
    ax.axis('equal')

    make_picker(fig, wedges)
    plt.show()

def make_picker(fig, wedges):
    import webbrowser
    def on_pick(event):
        wedge = event.artist
        label = wedge.get_label()
        webbrowser.open('http://images.google.com/images?q={0}'.format(label))

    # Make wedges selectable
    for wedge in wedges:
        wedge.set_picker(True)

    fig.canvas.mpl_connect('pick_event', on_pick)

if __name__ == '__main__':
    main()

Which opens a browser window for a google image search of whatever the wedge is labeled as...

Joe Kington
Thank you Joe, lots of details and example. Very nice :) I'm trying to implement this with django and html. But I don't really understand your html. Normally I put the link to the image or the location where it's saved
Pat
@Patricia - Well, that's all I'm doing in the first example... It just saves the image to disk, inserts an `<img src="filename.png" />`, and then generates an image map for the saved image based on the wedges in the pie chart. (Stackoverflow's syntax highlighting is going a little wonky with all the strings with nested `"`, `'`, and `"""`) The key is for loop beneath the "`#-- Get the coords of each wedge...`" bit. I'm just grabbing the vertices for each wedge, converting them to pixel coordinates for the saved image, and generating a string with these coords. Hope that helps!
Joe Kington
well I think my mistake is somewhere there because when I pass the variables to the template, it doesn't do anything
Pat
@Patricia - The first example should create two files... "temp.png", and whatever you passed as a filename to the function ("temp.html" in the example)...
Joe Kington
Thank you Joe. Still not able to do it, but I gonna try :)
Pat
@Patricia - Well, good luck, at any rate! I'm not sure why it isn't working!
Joe Kington
Well I'm trying again to implement this pie chart, I've been doing others things and left this one for a while. I'm getting an error in im_filename = "../media/gender.png". [Errno 13] Permission denied: '../media/gender.png'. Do you have any idea?
Pat
@Pat - Well, it sounds like the user running the python process doesn't write have access to `../media/`. Does the same thing happen if you just try to create an empty file there? (e.g. try putting `file('../media/test.txt', 'w')` in the script) Keep in mind that `..` will refer to the directory above wherever you _run_ the script from, _not_ where the `whatever.py` is located!
Joe Kington
the same thing happen. but is kind of weird, since I'm the only person working on this workstation and on this project. Perhaps it's because I save the image to /var/django/(..) Well I have no idea
Pat
Well, there's no reason why a normal user would ever have write access to `/var/`... (In a unix-y world, you avoid _ever_ running things as root (administrator) unless you're doing system maintenance.) Try saving it to somewhere where you would expect to have write access (say, your home directory, for example...)
Joe Kington
I have write access to /var/ but anyway I follow your suggestion and save it in another file and the same happen. Seems I have to start thinking in another approach..
Pat
it's working! I had to make some changes but got it! Thank you for your help Joe, it was very kind of you :)
Pat
@Pat - Glad to hear it worked! :)
Joe Kington