1

I am having a strange issue when setting up pie charts using matplotlib. For some reason, it does not seem to be handling my labels argument correctly.

A little background...I am working on a tool that will allow us to create tables that summarize the hits on our web map services. This basically just loops through all the log files and and grabs username, site name, and date\time info. I have got this first part working nicely and it creates some good summary tables. However, it would be a nice cherry on top to generate pie charts showing the who uses each site the most (by username). I have also got the pie chart creation to work where each chart is named after the site.

What is not working correctly is the labels.

I have admittedly not worked with matplotlib very much at all, but according to the code samples I have seen online, my code seems sound. Just to double check that the number of labels is matching the number of slices for the pie I print the results out for each chart and the input arguments seem to be correct, but maybe I am missing something?

The input table I am using looks good and that is the data being used for the pie chart. Here is the particular function I am working with:

#! /usr/local/bin/python

import arcpy, os
from pylab import *
import numpy as np

def CreatePieChart(table, out, case_field, name_field, data_field):

    # Make new folder
    _dir = os.path.join(out, 'Figures')
    if not os.path.exists(_dir):
        os.makedirs(_dir)

    # Grab unique values
    rows = arcpy.SearchCursor(table)
    cases = sorted(list(set(r.getValue(case_field) for r in rows)))
    del rows

    # vals dictionary
    vals_dict = {}

    # Make table views
    tv = arcpy.MakeTableView_management(table, 'temp_table')
    for case in cases:
        query = ''' {0} = '{1}' '''.format(arcpy.AddFieldDelimiters(tv, case_field), case)
        arcpy.SelectLayerByAttribute_management(tv, 'NEW_SELECTION', query)

        # Get total number for pie slice
        rows = arcpy.SearchCursor(tv)
        vals_dict[case] = [[r.getValue(name_field),r.getValue(data_field)] for r in rows]
        del rows

    # Populate Pie Chart
    for case,vals in vals_dict.iteritems():
        the_fig = figure(1, figsize=(8,8))
        axes([0.1, 0.1, 0.8, 0.8])
        fig = os.path.join(_dir, '{0}.png'.format(case.replace(' ','_')))
        ax = the_fig.add_subplot(111)
        label = [v[0] for v in vals]
        fracs = [v[1] for v in vals]
        print '%s %s %s' %(case,label,fracs)
        if len(label) == len(fracs):  # to make sure same number of labels as slices
            cmap = plt.cm.prism
            color = cmap(np.linspace(0., 1., len(fracs)))
            pie_wedges = pie(fracs,colors=color,labels=label,pctdistance=0.5, labeldistance=1.05)
            for wedge in pie_wedges[0]:
                wedge.set_edgecolor('white')
            ax.set_title(case)
            savefig(fig)
            print 'Created: %s' %fig
        del the_fig,label,pie_wedges,fracs
    return fig

if __name__ == '__main__':

    table = r'C:\TEMP\Usage_Summary.dbf'
    out = r'C:\TEMP'
    case = 'Site_ID'
    name = 'Username'
    data = 'Count'

    CreatePieChart(table, out, case, name, data)

And here is what is printed out to my Python window (slice count does indeed match the number of labels):

ElkRiver [u'elkriver', u'jasonco', u'jenibr', u'johnsh', u'nickme'] [731, 1, 2, 55, 58]
Created: C:\TEMP\Figures\ElkRiver.png
LongPrairie [u'brianya', u'chuckde', u'johnsh', u'longprairie', u'nickme', u'scottja'] [6, 7, 61, 129, 25, 3]
Created: C:\TEMP\Figures\LongPrairie.png
Madison [u'deanhe', u'johnsh', u'kathrynst', u'madison', u'scottja'] [7, 9, 1, 39, 1]
Created: C:\TEMP\Figures\Madison.png
NorthMankato [u' ', u'adamja', u'brianma', u'johnsh', u'johnvo', u'marksc', u'mattme', u'nickme', u'nmankato', u'scottja'] [20, 13, 65, 64, 8, 2, 4, 63, 64, 1]
Created: C:\TEMP\Figures\NorthMankato.png
Orono [u'arroned', u'davidma', u'dionsc', u'dougkl', u'gis_guest', u'jenibr', u'johnsh', u'kenad', u'kevinfi', u'kylele', u'marksc', u'natest', u'nickme', u'orono', u'samel', u'scottja', u'sethpe', u'sueda'] [2, 11, 1, 3, 5, 1, 40, 6, 1, 1, 5, 1, 37, 819, 8, 5, 2, 2]
Created: C:\TEMP\Figures\Orono.png
BellePlaine [u'billhe', u'billsc', u'bplaine', u'christopherga', u'craigla', u'dennisst', u'elkriver', u'empire', u'gis_guest', u'jasonfe', u'joedu', u'johnsh', u'joshst', u'lancefr', u'nickme', u'richardde', u'scottja', u'teresabu', u'travisje', u'wadena'] [3, 1, 331, 1, 1, 40, 1, 1, 12, 1, 27, 61, 4, 1, 47, 3, 12, 2, 2, 1]
Created: C:\TEMP\Figures\BellePlaine.png
Osseo [u'johnsh', u'karlan', u'kevinbi', u'marcusth', u'nickme', u'osseo', u'scottja'] [22, 2, 23, 11, 66, 54, 3]
Created: C:\TEMP\Figures\Osseo.png
EmpireTownship [u'empire', u'johnsh', u'lancefr', u'lanile', u'marksc', u'nickme', u'richardde', u'scottja', u'travisje'] [96, 10, 1, 14, 2, 224, 1, 1, 3]
Created: C:\TEMP\Figures\EmpireTownship.png
Courtland [u'courtland', u'empire', u'joedu', u'johnsh', u'nickme', u'scottja'] [24, 3, 3, 10, 27, 2]
Created: C:\TEMP\Figures\Courtland.png
LeSueur [u' ', u'johnsh', u'marksc', u'nickme'] [8, 6, 1, 98]
Created: C:\TEMP\Figures\LeSueur.png
Stratford [u'johnsh', u'neilgu', u'scottja', u'stratford'] [9, 3, 2, 47]
Created: C:\TEMP\Figures\Stratford.png
>>> 

Something funky is happening behind the scenes because the charts come out with way more labels than what I pass into the pie() function.

I do not yet have a reputation to post pictures, but I have posted a picture here on gis stack exchange. Here is the link where the picture can be viewed. The picture is from the "Osseo" Chart that is created and you can see the from my print statements that these are the slices and lables:

Osseo [u'johnsh', u'karlan', u'kevinbi', u'marcusth', u'nickme', u'osseo', u'scottja'] [22, 2, 23, 11, 66, 54, 3]

I am not sure why so many extra labels are being created. Am I missing something here?

Here is a clean version of the code so others can test:

#! /usr/local/bin/python

import os
from pylab import *
import numpy as np

_dir = os.path.join(os.getcwd(), 'Figures')
if not os.path.exists(_dir):
    os.makedirs(_dir)


vals_dict = {u'ElkRiver': [[u'elkriver', 731], [u'jasonco', 1], [u'jenibr', 2], [u'johnsh', 55], [u'nickme', 58]],
             u'LongPrairie': [[u'brianya', 6], [u'chuckde', 7], [u'johnsh', 61], [u'longprairie', 129], [u'nickme', 25],
            [u'scottja', 3]], u'Madison': [[u'deanhe', 7], [u'johnsh', 9], [u'kathrynst', 1], [u'madison', 39],
            [u'scottja', 1]], u'NorthMankato': [[u' ', 20], [u'adamja', 13],[u'brianma', 65], [u'johnsh', 64],
            [u'johnvo', 8], [u'marksc', 2], [u'mattme', 4], [u'nickme', 63], [u'nmankato', 64], [u'scottja', 1]],
            u'Orono': [[u'arroned', 2], [u'davidma', 11], [u'dionsc', 1], [u'dougkl', 3], [u'gis_guest', 5], [u'jenibr', 1],
            [u'johnsh', 40], [u'kenad', 6], [u'kevinfi', 1], [u'kylele', 1], [u'marksc', 5], [u'natest', 1], [u'nickme', 37],
            [u'orono', 819], [u'samel', 8], [u'scottja', 5], [u'sethpe', 2], [u'sueda', 2]], u'BellePlaine': [[u'billhe', 3],
            [u'billsc', 1], [u'bplaine', 331], [u'christopherga', 1], [u'craigla', 1], [u'dennisst', 40], [u'elkriver', 1],
            [u'empire', 1], [u'gis_guest', 12], [u'jasonfe', 1], [u'joedu', 27], [u'johnsh', 61], [u'joshst', 4], [u'lancefr', 1],
            [u'nickme', 47], [u'richardde', 3], [u'scottja', 12], [u'teresabu', 2], [u'travisje', 2], [u'wadena', 1]],
             u'Osseo': [[u'johnsh', 22], [u'karlan', 2], [u'kevinbi', 23], [u'marcusth', 11], [u'nickme', 66], [u'osseo', 54],
            [u'scottja', 3]], u'EmpireTownship': [[u'empire', 96], [u'johnsh', 10], [u'lancefr', 1], [u'lanile', 14], [u'marksc', 2],
            [u'nickme', 224], [u'richardde', 1], [u'scottja', 1], [u'travisje', 3]], u'Courtland': [[u'courtland', 24], [u'empire', 3],
            [u'joedu', 3], [u'johnsh', 10], [u'nickme', 27], [u'scottja', 2]], u'LeSueur': [[u' ', 8], [u'johnsh', 6], [u'marksc', 1],
            [u'nickme', 98]], u'Stratford': [[u'johnsh', 9], [u'neilgu', 3], [u'scottja', 2], [u'stratford', 47]]}


# Populate Pie Chart
for case,vals in vals_dict.iteritems():
    the_fig = figure(1, figsize=(8,8))
    axes([0.1, 0.1, 0.8, 0.8])
    fig = os.path.join(_dir, '{0}.png'.format(case.replace(' ','_')))
    ax = the_fig.add_subplot(111)
    label = [v[0] for v in vals]
    fracs = [v[1] for v in vals]
    print '%s %s %s' %(case,label,fracs)
    if len(label) == len(fracs):  # to make sure same number of labels as slices
        cmap = plt.cm.prism
        color = cmap(np.linspace(0., 1., len(fracs)))
        pie_wedges = pie(fracs,colors=color,labels=label,pctdistance=0.5, labeldistance=1.1)
        for wedge in pie_wedges[0]:
            wedge.set_edgecolor('white')
        ax.set_title(case)
        savefig(fig)
        print 'Created: %s' %fig
    del label,pie_wedges,fracs
    the_fig.clf()
2
  • 2
    It would be easier to debug this if you could abstract out the arcpy stuff, and everything that is dependent on your machine (i.e. accessing that specific table file on your drive). This will allow other people to run your example, and make it possible to debug. Commented Nov 18, 2013 at 16:08
  • can you reduce this to a minimal example? There is for too much irrelevant code here which makes it difficult to sort out exactly what is going wrong Commented Nov 18, 2013 at 16:08

1 Answer 1

1

From what I can tell without being able to run your example, the problem is that you're using the same figure in your loop over the cases

for case,vals in vals_dict.iteritems():
    the_fig = figure(1, figsize=(8,8))

This gets figure 1 each time, and this figure never gets cleared. So try either making a new figure (call figure(figsize=(8,8)) instead of figure(1, figsize=(8,8))), or clearing the figure right after you create it (with the_fig.clf()). Let me know if that helps.

Edit: here's a version that doesn't show the axes, and uses legend instead of label so that you don't have labels being squished together. I also threw percentages on the labels, especially since with your color scheme you sometimes end up with some slices the same color (I tried to fix that but didn't have any luck). Matplotlib is actually doing a pretty decent job of putting small slices as far from each other as possible, the problem is in some of your charts, you just have too many small slices, so it's just not possible to get them all separated. That's why I switched to using a legend.

for case,vals in vals_dict.iteritems():
    the_fig = figure(figsize=(8,8))
    axes([0.1, 0.1, 0.8, 0.8])
    fig = os.path.join(_dir, '{0}.png'.format(case.replace(' ','_')))
    label = [v[0] for v in vals]
    fracs = [v[1] for v in vals]
    print '%s %s %s' %(case,label,fracs)
    if len(label) == len(fracs):  # to make sure same number of labels as slices
        cmap = plt.cm.prism
        color = cmap(np.linspace(0., 1., len(fracs)))
        pie_wedges = pie(fracs,colors=color,pctdistance=0.5, labeldistance=1.1)
        for wedge in pie_wedges[0]:
            wedge.set_edgecolor('white')
        legend(map(lambda x, f: '%s (%0.0f%%)' % (x, f), label, fracs), loc=4)
        title(case)
        savefig(fig)
        print 'Created: %s' %fig
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you hunse! A lot of my problem here is that I did not really take the time to look over the documentation (just looked at sample codes online). Clearing the figure did the trick. If I could pick your brain one more time, would you happen to know how to label each figure without having to add the subplot? I do not want to show the axis and boarders on the side.
Also, I added a clean version so that anyone can test. Is there a way to format the pie chart so that all the tiny slices are not all right next to eachother? There are several instances where a user has only accessed a site one or two times so there are many small slices. Thank you for the help so far! I will try to look into the documentation in the meantime to see if I can figure it out on my own.
@ hunse. You are awesome!!!!! Thank you so much! I was just looking at the legend stuff and trying to figure out how to get that to work, but you beat me to it (and did it much better than I could have done). I tried to vote up your answer to add extra points but my reputation is too low :( Thanks again for your help, the figures turned out beautifully!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.