0

I am attempting to make a fig of 6 pie charts, each with embedded images. I have been able to produce the pie charts and also the subplots (empty). However, I don't understand the behavior of producing all the graphs in the bottom right corner. (which it currently does). In addition, when I run 6 graphs I get a memory error. (Which does not happen when I run two graphs)

Exception in Tkinter callback:

MemoryError

I am including my code, with enough data lines to produce two charts. The 5 missing png files can easily be constructed with paint and saving solid block png files.

I also realize that there are two questions here, but since they both come from the same code I am inquiring about both.

So 1) how can I get the subplots to distribute the pie charts in their own positions. In Matplotlib, what does the argument mean in fig.add_subplot(111)? Did not appear to help me either.

2) how can I keep from getting a memory error (I probably opened something and didn't know how to close it, but I don't know what or how) this Tkinter and pyplot running out of memory appeared to have a similar problem, but I could not understand how it was fixed

import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import sys
import scipy.stats

#import panda as pd
import numpy as np

size_distribution = np.array([
    [ 1 , 1 , 0.48 , 0.42 , 0.10 ],
    [ 1 , 1 , 0.52 , 0.38 , 0.10 ],
    [ 2 , 2 , 0.38 , 0.42 , 0.20 ],
    [ 2 , 2 , 0.42 , 0.38 , 0.20 ]])

def name_of_files(): # files named as such in directory with python script
    Variety = np.array(['_A_', '_B_']) #Variety of tested stuff
    Treatment = np.array(['_1_', '_2_', '_3_']) # Treatment of stuff
    Cut = np.array(['_i_', '_ii_']) # Cut of stuff
    return Variety, Treatment, Cut

def label(): # provides info on labelling in pie chart
    var = np.array(['A_', 'B_'])
    treat = np.array(['1_', '2_', '3_'])
    return var, treat

def size(xx, strt, end): #produces individual pie chart with plt.show() or sends wedges to main
    Variety, Treatment, Cut = name_of_files()
    var, treat = label()
    long = int(len(size_distribution))
    count = int(strt)
    cut = int(0)
    while count < (long - 1) and count < end:
        coarse = np.mean([xx[count][2], xx[count+1][2]])
        fine = np.mean([xx[count][3], xx[count+1][3]])
        residue = np.mean([xx[count][4], xx[count+1][4]])
        name = (str(Variety[int(xx[count][0])-1]) +
                str(Treatment[int(xx[count][1])-1]) +
                str(Cut[int(cut)]) +
                '.png')
        #print(name, cut)
        if cut == 0:
            name_coarse = name
            label_coarse = (str(var[int(xx[count][0])-1]) + '\n' +
                            str(treat[int(xx[count][1])-1]) + '\n' +
                            'coarse fraction:\n' + str(np.around(coarse, 3)*100) + '%')
            #print(label_coarse, coarse)
            cut = int(1)
        elif cut == 1:
            name_fine = name
            label_fine = (str(var[int(xx[count][0])-1]) + '\n' +
                            str(treat[int(xx[count][1])-1]) + '\n' +
                            'fine fraction:\n' + str(np.around(fine, 3)*100) + '%')
            label_residue = ('\n\n' + str(var[int(xx[count][0])-1]) + ', ' +
                            str(treat[int(xx[count][1])-1]) + '\n' +
                            'residue fraction: ' + str(np.around(residue, 3)*100) + '%')
            #print(label_fine, fine)
            #print(label_residue, residue)
            #print('\t\t\t\t', fine+coarse+residue)
            cut = int(0)
            #print('\n\n\t\t\tcount = ', count)
            count += 2
            #print('\n\n\t\t\tcount = ', count)


            file_index = np.array([name_coarse, 'Black.png', name_fine])
            labels = np.array([label_coarse, label_residue, label_fine])
            total = [coarse, residue, fine]
            plt.gca().axis('equal')
            wedges, texts = plt.pie(total, startangle=90, labels=labels,
                        wedgeprops = { 'linewidth': 3, "edgecolor" :"k",
                                       "fill":False,  })
            positions = [(-1,0.3),(0,-0.5),(0.5,0.5)]
            zooms = [0.4,0.4,0.4]
            for i in range(3):
                #print('\ti = ', i)
                fn = str(file_index[i]).format(labels[i].lower(), fontsize=20)
                img_to_pie(fn, wedges[i], xy=positions[i], zoom=zooms[i] )
                wedges[i].set_zorder(10)
            #plt.show() #shows the pie chart nicely
            #plt.close('all')
            #print('Wedges type: ',type(wedges),'\nLength: ', len(wedges))
            #print('Texts type: ',type(wedges),'\nTexts: ', len(wedges))
            #print(type(texts), len(texts))
            #print(locals())
    return wedges

def img_to_pie( fn, wedge, xy, zoom=1, ax = None):
    if ax==None: ax=plt.gca()
    im = plt.imread(fn, format='png')
    path = wedge.get_path()
    patch = PathPatch(path, facecolor='none')
    plt.gca().add_patch(patch)
    imagebox = OffsetImage(im, zoom=zoom, clip_path=patch, zorder=-10)
    ab = AnnotationBbox(imagebox, xy, xycoords='data', pad=0, frameon=False)
    ax.add_artist(ab)
    return ()

def main():
    fig = plt.figure()
    ho = fig.add_subplot(1, 2, 1)
    hm = fig.add_subplot(1, 2, 2)
    '''
    f, ((ho , hm, hs), (mo, mm, ms)) = (
        plt.subplots(nrows=2, ncols=3, sharex=True, sharey=True,
                 squeeze=False, subplot_kw=None, gridspec_kw=None))
    #plt.show() # six empty plots as expected'''
    j = [ho, hm]#, hs]#, mo, mm, ms]
    x = [0, 2]#, 4]#, 6, 8, 10]
    count = 0
    int(count)
    while count < len(x):
        j[count] = size(size_distribution, x[count], x[count]+2)
        print(x[count], '\n', j[count])
        count += 1
    print(ho, '\n', hm)
    plt.show()  

main()
halfer
  • 19,824
  • 17
  • 99
  • 186
CJD
  • 171
  • 1
  • 1
  • 13

1 Answers1

1

First of all, the function I wrote for you in an answer to this question takes an argument ax which you should use to set the axes to which you want to plot.

Also, you need to plot your pie chart to the axes ax at which you want to have it appear, i.e. use ax.pie(..) instead of plt.pie(..).

You may create the subplots using

f, ax_arr = plt.subplots(nrows=2, ncols=3)

as seen in the answers to your other recent question and supply the axes inside ax_arr to the size function, which you then need to write like size(xx, strt, end, ax).

The following code

import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import scipy.stats
import numpy as np

size_distribution = np.array([
    [ 1 , 1 , 0.48 , 0.42 , 0.10 ],
    [ 1 , 1 , 0.52 , 0.38 , 0.10 ],
    [ 2 , 2 , 0.38 , 0.42 , 0.20 ],
    [ 2 , 2 , 0.42 , 0.38 , 0.20 ]])

def name_of_files(): # files named as such in directory with python script
    Variety = np.array(['_A_', '_B_']) #Variety of tested stuff
    Treatment = np.array(['_1_', '_2_', '_3_']) # Treatment of stuff
    Cut = np.array(['_i_', '_ii_']) # Cut of stuff
    return Variety, Treatment, Cut

def label(): # provides info on labelling in pie chart
    var = np.array(['A_', 'B_'])
    treat = np.array(['1_', '2_', '3_'])
    return var, treat

filenames=["data/blueberries.png", "data/blackberries.png","data/raspberries.png"]

def size(xx, strt, end, ax=None): #produces individual pie chart with plt.show() or sends wedges to main
    if not ax: ax=plt.gca()
    Variety, Treatment, Cut = name_of_files()
    var, treat = label()
    long = int(len(size_distribution))
    count = int(strt)
    cut = int(0)
    while count < (long - 1) and count < end:
        coarse = np.mean([xx[count][2], xx[count+1][2]])
        fine = np.mean([xx[count][3], xx[count+1][3]])
        residue = np.mean([xx[count][4], xx[count+1][4]])
        name = (str(Variety[int(xx[count][0])-1]) +
                str(Treatment[int(xx[count][1])-1]) +
                str(Cut[int(cut)]) +
                '.png')

        if cut == 0:
            name_coarse = name
            label_coarse = (str(var[int(xx[count][0])-1]) + '\n' +
                            str(treat[int(xx[count][1])-1]) + '\n' +
                            'coarse fraction:\n' + str(np.around(coarse, 3)*100) + '%')

            cut = int(1)
        elif cut == 1:
            name_fine = name
            label_fine = (str(var[int(xx[count][0])-1]) + '\n' +
                            str(treat[int(xx[count][1])-1]) + '\n' +
                            'fine fraction:\n' + str(np.around(fine, 3)*100) + '%')
            label_residue = ('\n\n' + str(var[int(xx[count][0])-1]) + ', ' +
                            str(treat[int(xx[count][1])-1]) + '\n' +
                            'residue fraction: ' + str(np.around(residue, 3)*100) + '%')
            cut = int(0)
            count += 2

            file_index = np.array([name_coarse, 'Black.png', name_fine])
            labels = np.array([label_coarse, label_residue, label_fine])
            total = [coarse, residue, fine]
            ax.axis('equal')
            wedges, texts = ax.pie(total, startangle=90, labels=labels,
                        wedgeprops = { 'linewidth': 3, "edgecolor" :"k",
                                       "fill":False,  })
            positions = [(-1,0.3),(0,-0.5),(0.5,0.5)]
            zooms = [0.4,0.4,0.4]
            for i in range(3):

                #fn = str(file_index[i]).format(labels[i].lower(), fontsize=20)
                fn = filenames[i]
                img_to_pie(fn, wedges[i], xy=positions[i], zoom=zooms[i],ax=ax )
                wedges[i].set_zorder(10)

    return wedges

def img_to_pie( fn, wedge, xy, zoom=1, ax = None):
    if ax==None: ax=plt.gca()
    im = plt.imread(fn, format='png')
    path = wedge.get_path()
    patch = PathPatch(path, facecolor='none')
    ax.add_patch(patch)
    imagebox = OffsetImage(im, zoom=zoom, clip_path=patch, zorder=10)
    ab = AnnotationBbox(imagebox, xy, xycoords='data', pad=0, frameon=False)
    ax.add_artist(ab)
    return ()

def main():

    f, ax_arr = plt.subplots(nrows=2, ncols=3)

    x = [0, 2, 4, 6, 8, 10] 
    count = 0
    for i, ax in zip(x, ax_arr.flatten()):
        wedges = size(size_distribution, x[count], x[count]+2, ax=ax)

    plt.show()  

main()

then produces this plot

enter image description here

where I did not care about looking at the labels as this part of the code is big chaos (next time asking please create a minimal example without superfluous labels).

The error you get might be coming from the line I commented out, which does not make sense:

#fn = str(file_index[i]).format(labels[i].lower(), fontsize=20)

since str does not have a fontsize argument.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thanks. Answer accepted. My memory error in Tkinter still exists, but this has given me a much better understanding of subplots and how they work. I can get 4 pie charts to plot, but get the memory error when I try 5 or 6 plots. – CJD Jun 17 '17 at 19:08
  • That sounds like it's really the amount of memory taken that causes this. Are your images extremely large? Maybe you try with other images just to see if this changes anything. You may of course post the complete error traceback, even if for memory errors those are often not too useful. – ImportanceOfBeingErnest Jun 17 '17 at 19:12