4

I have a problem about drawing a nested pie graph in Matplotlib in Python. I wrote some codes to handle with this process but I have an issue related with design and label

I'd like to draw a kind of this nested pie graph. (from the uppermost layer of the nested to its innermost is SEX, ALIGN with covering their counts)

Here is my dataframe which is shown below.

ALIGN   SEX count
2   Bad Characters  Male Characters 1542
5   Good Characters Male Characters 1419
3   Good Characters Female Characters   714
0   Bad Characters  Female Characters   419
8   Neutral Characters  Male Characters 254
6   Neutral Characters  Female Characters   138
1   Bad Characters  Genderless Characters   9
4   Good Characters Genderless Characters   4
7   Neutral Characters  Genderless Characters   3
9   Reformed Criminals  Male Characters 2

Here is my code snippets related with showing nested pie graph which is shown below.

fig, ax = plt.subplots(figsize=(24,12))
size = 0.3

ax.pie(dc_df_ALIGN_SEX.groupby('SEX')['count'].sum(), radius=1,
       labels=dc_df_ALIGN_SEX['SEX'].drop_duplicates(),
       autopct='%1.1f%%',
       wedgeprops=dict(width=size, edgecolor='w'))

ax.pie(dc_df_ALIGN_SEX['count'], radius=1-size, labels = dc_df_ALIGN_SEX["ALIGN"],
       wedgeprops=dict(width=size, edgecolor='w'))

ax.set(aspect="equal", title='Pie plot with `ax.pie`')
plt.show()

How can I design 4 row and 4 column and put each one in each slot and showing labels in legend area?

3 Answers 3

7

Since the question has been changed, I'm posting a new answer.

First, I slightly simplified your DataFrame:

import pandas as pd

df = pd.DataFrame([['Bad', 'Male', 1542],
                   ['Good', 'Male', 1419],
                   ['Good', 'Female', 714],
                   ['Bad', 'Female', 419],
                   ['Neutral', 'Male', 254],
                   ['Neutral', 'Female', 138], 
                   ['Bad', 'Genderless', 9], 
                   ['Good', 'Genderless', 4],
                   ['Neutral', 'Genderless', 3], 
                   ['Reformed', 'Male', 2]])
df.columns = ['ALIGN', 'SEX', 'n']

For the numbers in the outer ring, we can use a simple groupby, as you did:

outer = df.groupby('SEX').sum()

But for the numbers in the inner ring, we need to group by both categorical variables, which results in a MultiIndex:

inner = df.groupby(['SEX', 'ALIGN']).sum()
inner
                     n
SEX         ALIGN   
Female      Bad      419
            Good     714
            Neutral  138
Genderless  Bad        9
            Good       4
            Neutral    3
Male        Bad     1542
            Good    1419
            Neutral  254
            Reformed   2

We can extract the appropriate labels from the MultiIndex with its get_level_values() method:

inner_labels = inner.index.get_level_values(1)

Now you can turn the above values into one-dimensional arrays and plug them into your plot calls:

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(figsize=(24,12))
size = 0.3

ax.pie(outer.values.flatten(), radius=1,
       labels=outer.index,
       autopct='%1.1f%%',
       wedgeprops=dict(width=size, edgecolor='w'))

ax.pie(inner.values.flatten(), radius=1-size, 
       labels = inner_labels,
       wedgeprops=dict(width=size, edgecolor='w'))

ax.set(aspect="equal", title='Pie plot with `ax.pie`')
plt.show()

nested pie chart

Sign up to request clarification or add additional context in comments.

1 Comment

I also put each graph into each slot of 4 rows and 4 columns in subplots except for not overlapping label. How can I do that?
0

You define the function percentage_growth(l) in a way that supposes its argument l to be a list (or some other one-dimensional object). But then (to assign colors) you call this function on dc_df_ALIGN_SEX, which is apparently your DataFrame. So the function (in the first iteration of its loop) tries to evaluate dc_df_ALIGN_SEX[0], which throws the key error, because that is not a proper way to index the DataFrame.

Perhaps you want to do something like percentage_growth(dc_df_ALIGN_SEX['count']) instead?

3 Comments

I got this type of an error. TypeError: 'method' object is not iterable
Ah, I see: count is an unfortunate column name, because it is also the name of a pandas method. I edited the suggested function call, but it would perhaps be safest to change the column name. Also, in case there are more issues, it would be good to know what frame is in your code above.
I updated my question. Can you look through it?
0

Could you try a python module, named omniplot. It uses matplotlib. It has a method, named "nested_piechart" to draw a nested pie chart from pandas dataframe as shown below.

import pandas as pd
import omniplot.plot as op
import matplotlib.pyplot as plt

df = pd.DataFrame([['Bad', 'Female', 419],
                   ['Good', 'Male', 1419],
                   ['Bad', 'Male', 1542],
                   ['Good', 'Genderless', 4],
                   ['Good', 'Female', 714],
                   ['Neutral', 'Female', 138], 
                   ['Neutral', 'Male', 254],
                   
                   ['Bad', 'Genderless', 9], 
                   
                   ['Neutral', 'Genderless', 3], 
                   ['Reformed', 'Male', 2]])
df.columns = ['ALIGN', 'SEX', 'n']
op.nested_piechart(df=df, category=['ALIGN', 'SEX'], variable="n")
# optionally plt.savefig("test.png") or plt.show()

The result

It can sort the labels too.

op.nested_piechart(df=df, category=['ALIGN', 'SEX'], variable="n", show_percentage=True, order="largest")

The result2

4 Comments

Thank you for your response. I also want to see the percentage of each part. One female part is missing in the Neutral part. Can you revise the code if possible?
yes, if you give "igonore=0" to the function, then it may show all percentages.
You already added "show_percentage=True" and I thought it was enough to get the figure. Why did I add "igonore=0" to op.nested_piechart.
Because labels with too small farctions often overlap each other, making them unreadable. so i set default to ignore labels with less than 0.05 ( 5%).

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.