I have done a clustering algorithm and represented the results in a pie chart as shown below.

fig, ax = plt.subplots(figsize=(20, 10), subplot_kw=dict(aspect="equal"))
contents  = []

for k,v  in clusters.items():
    indi= str(len(clusters[k])) + " users " +  "Cluster_"+ str(k)

#contents = ['23 users Cluster_0', '21 users Cluster_1']

data = [float(x.split()[0]) for x in contents]
Cluster= [x.split()[-1] for x in contents]

def func(pct, allvals):
    absolute = int(pct/100.*np.sum(allvals))
    return "{:.0f}%\n({:d} users)".format(pct, absolute)

wedges, texts, autotexts = ax.pie(data, autopct=lambda pct: func(pct, data),

ax.legend(wedges, Cluster,
          loc="center left",
          bbox_to_anchor=(1, 0, 0.5, 1))

plt.setp(autotexts, size=10, weight="bold")

ax.set_title("Distribution of users: A pie chart")

Even though the users are 23 and 21 in each cluster, the piechart shows 22 and 20. This is due to the conversion to int and some float values are cut off in the function def func().

enter image description here

But, to fix this I wrote the below code and it works:

def func(percentage, allvals):
    absolute = int(np.sum(allvals))
    newV = (percentage/100)*absolute
    roundnewV = round(newV)
    intnewV = int(roundnewV)
    return "{:.0f}%\n({:d} users)".format(percentage, intnewV)

Is this a good way to save the original form of integer and not lose out any value?


You got yourself in trouble by using plt.pie, and especially the keyword argument autopct, beyond its intended use.

The basic idea of the pie chart is to have the wedge labels outside the pie and perhaps percentages inside.

You wanted the wedge label and percentage inside the pie and manipulated the autopct keyword with a function to achieve this. This involved cumbersome calculations from percentages to values you already know.

Another solution could be to use the more simple labels keyword argument and change the resulting texts properties to be inside the pie instead outside, see code changes below:

contents = ['23 users Cluster_0', '21 users Cluster_1']

data = [int(x.split()[0]) for x in contents]

def pie_chart_labels(data):
    total = int(np.sum(data))
    percentages = [100.0 * x / total for x in data]
    fmt_str = "{:.0f}%\n({:d} users)"
    return [fmt_str.format(p,i) for p,i in zip(percentages, data)]

wedges, texts,  = ax.pie(data, labels=pie_chart_labels(data))

# shrink label positions to be inside the pie
for t in texts:
    x,y = t.get_position()
    t.set_x(0.5 * x)
    t.set_y(0.5 * y)

plt.setp(texts, size=10, weight="bold", color="w", ha='center')
| improve this answer | |
  • \$\begingroup\$ Thank you very much. Really appreciate for your time and effort. This piece of code looks more efficient. I tried to correct it multiple ways using autopct but somehow ended up giving bad results. \$\endgroup\$ – P H Feb 20 at 10:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.