How to avoid overlapping of labels & autopct in a matplotlib pie chart? How to avoid overlapping of labels & autopct in a matplotlib pie chart? numpy numpy

How to avoid overlapping of labels & autopct in a matplotlib pie chart?


Alternatively you can put the legends beside the pie graph:

import matplotlib.pyplot as pltimport numpy as npx = np.char.array(['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct', 'Nov','Dec'])y = np.array([234, 64, 54,10, 0, 1, 0, 9, 2, 1, 7, 7])colors = ['yellowgreen','red','gold','lightskyblue','white','lightcoral','blue','pink', 'darkgreen','yellow','grey','violet','magenta','cyan']porcent = 100.*y/y.sum()patches, texts = plt.pie(y, colors=colors, startangle=90, radius=1.2)labels = ['{0} - {1:1.2f} %'.format(i,j) for i,j in zip(x, porcent)]sort_legend = Trueif sort_legend:    patches, labels, dummy =  zip(*sorted(zip(patches, labels, y),                                          key=lambda x: x[2],                                          reverse=True))plt.legend(patches, labels, loc='left center', bbox_to_anchor=(-0.1, 1.),           fontsize=8)plt.savefig('piechart.png', bbox_inches='tight')

enter image description here


EDIT: if you want to keep the legend in the original order, as you mentioned in the comments, you can set sort_legend=False in the code above, giving:

enter image description here


If anyone just wants to offset the labels automatically, and not use a legend, I wrote this function that does it (yup I'm a real try-hard). It uses numpy but could easily be re-written in pure python.

import numpy as npdef fix_labels(mylabels, tooclose=0.1, sepfactor=2):    vecs = np.zeros((len(mylabels), len(mylabels), 2))    dists = np.zeros((len(mylabels), len(mylabels)))    for i in range(0, len(mylabels)-1):        for j in range(i+1, len(mylabels)):            a = np.array(mylabels[i].get_position())            b = np.array(mylabels[j].get_position())            dists[i,j] = np.linalg.norm(a-b)            vecs[i,j,:] = a-b            if dists[i,j] < tooclose:                mylabels[i].set_x(a[0] + sepfactor*vecs[i,j,0])                mylabels[i].set_y(a[1] + sepfactor*vecs[i,j,1])                mylabels[j].set_x(b[0] - sepfactor*vecs[i,j,0])                mylabels[j].set_y(b[1] - sepfactor*vecs[i,j,1])

So use it like:

wedges, labels, autopct = ax1.pie(sizes, labels=groups, autopct='%1.1f%%',                                  shadow=False, startangle=90)fix_labels(autopct, sepfactor=3)fix_labels(labels, sepfactor=2)

This works well as-written if you only have a few labels overlapping. If you have a whole bunch like OP, you might want to add a random direction vector to the vecs[i,j,:] = a-b line. That would probably work well.


Try tightlayout.

plt.tight_layout()

at the end of your code. It may prevent the overlap a little bit.