Conditional removal of labels in pie chart

Question:

I have the following code:

import matplotlib.pyplot as plt
import numpy as np
np.random.seed(123456)
import pandas as pd

df = pd.DataFrame(3 * np.random.rand(4, 4), index=['a', 'b', 'c', 'd'], 
                  columns=['x', 'y','z','w'])

plt.style.use('ggplot')
colors = plt.rcParams['axes.color_cycle']

fig, axes = plt.subplots(nrows=2, ncols=3)
for ax in axes.flat:
    ax.axis('off')

for ax, col in zip(axes.flat, df.columns):
    ax.pie(df[col], labels=df.index, autopct='%.2f', colors=colors)
    ax.set(ylabel='', title=col, aspect='equal')

axes[0, 0].legend(bbox_to_anchor=(0, 0.5))

fig.savefig('your_file.png') # Or whichever format you'd like
plt.show()

Which produce the following:

enter image description here

My question is, how can I remove the label based on a condition. For example I’d only want to display labels with percent > 20%. Such that the labels and value of a,c,d won’t be displayed in X, etc.

Asked By: neversaint

||

Answers:

The autopct argument from pie can be a callable, which will receive the current percentage. So you only would need to provide a function that returns an empty string for the values you want to omit the percentage.

  • Function
def my_autopct(pct):
    return ('%.2f' % pct) if pct > 20 else ''
  • Plot with matplotlib.axes.Axes.pie
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(8, 6))
for ax, col in zip(axes.flat, df.columns):
    ax.pie(df[col], labels=df.index, autopct=my_autopct)
    ax.set(ylabel='', title=col, aspect='equal')
fig.tight_layout()
  • Plot directly with the dataframe
axes = df.plot(kind='pie', autopct=my_autopct, figsize=(8, 6), subplots=True, layout=(2, 2), legend=False)

for ax in axes.flat:
    yl = ax.get_ylabel()
    ax.set(ylabel='', title=yl)

fig = axes[0, 0].get_figure()
fig.tight_layout()

enter image description here


If you need to parametrize the value on the autopct argument, you’ll need a function that returns a function, like:

def autopct_generator(limit):
    def inner_autopct(pct):
        return ('%.2f' % pct) if pct > limit else ''
    return inner_autopct

ax.pie(df[col], labels=df.index, autopct=autopct_generator(20), colors=colors)

For the labels, the best thing I can come up with is using list comprehension:

for ax, col in zip(axes.flat, df.columns):                                                             
    data = df[col]                                                                                     
    labels = [n if v > data.sum() * 0.2 else ''
              for n, v in zip(df.index, data)]                       
                                                                                                   
    ax.pie(data, autopct=my_autopct, colors=colors, labels=labels)

Note, however, that the legend by default is being generated from the first passed labels, so you’ll need to pass all values explicitly to keep it intact.

axes[0, 0].legend(df.index, bbox_to_anchor=(0, 0.5))
Answered By: memoselyk

For labels I have used:

def my_level_list(data):
   list = []
   for i in range(len(data)):
       if (data[i]*100/np.sum(data)) > 2 : #2%
           list.append('Label '+str(i+1))
       else:
           list.append('')
return list


patches, texts, autotexts = plt.pie(data, radius = 1, labels=my_level_list(data), autopct=my_autopct, shadow=True)
Answered By: JNey

You can make the labels function a little shorter using list comprehension:

def my_autopct(pct):
    return ('%1.1f' % pct) if pct > 1 else ''

def get_new_labels(sizes, labels):
    new_labels = [label if size > 1 else '' for size, label in zip(sizes, labels)]
    return new_labels

fig, ax = plt.subplots()
_,_,_ = ax.pie(sizes, labels=get_new_labels(sizes, labels), colors=colors, autopct=my_autopct, startangle=90, rotatelabels=False)
Answered By: Christie Smith
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.