Search code examples
pythonpandasmatplotlibpie-chart

How can I draw a nested pie graph in Matplotlib in Python?


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?


Solution

  • 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