Search code examples
pythonmatplotlibpie-chartlegend-propertiesplot-annotations

Adjust text positions and remove some part of the pie chart


I try to produce a nested pie chart as an example below. However, the text position looks not as beautiful as it should be. I want to adjust the text positions in the inner pie chart as fit inside the pie part (best along in red line, but if it is inside and looks good just okay). Also, I want to remove one part of pie chart as outline in the picture below. Any help would be very helpful.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt    
data=pd.read_csv(r"https://raw.githubusercontent.com/tuyenhavan/Course_Data/main/test_pie_chart.csv")
# Get outer and inner counts
inner=data.groupby(["Col1"]).size()
outer=data.groupby(["Col1","Col2"]).size()
# Get outer and inner labels
inner_label=[f"{idx} ({val/sum(inner.values.flatten())*100:.2f}%)" for idx, val in zip(inner.index.get_level_values(0),inner.values.flatten())]
outer_label=[f"{idx} ({val/sum(outer.values.flatten())*100:.2f}%)" for idx, val in zip(outer.index.get_level_values(1), outer.values.flatten())]
# Inner and outer colors
inner_color=["#16A085","#808000","#BA4A00"]
outer_color=["#808080","#229954","#34495E","#2E86C1","#CA6F1E","white","#808080","#229954","#CA6F1E","#00FFFF"]
# Set figure size and radius adjustment
fig, ax=plt.subplots(figsize=(10,8))
size=0.4
# Plot the inner pie chart
ax.pie(inner.values.flatten(), labels=inner_label, radius=1-size,wedgeprops=dict(width=size, edgecolor='w'),\
      colors=inner_color,textprops ={"fontsize":12},labeldistance=0.4) # labels=inner_label,autopct ='%1.1f%%', 
# Plot the outer pie chart
ax.pie(outer.values.flatten(), radius=1, labels=outer_label,wedgeprops=dict(width=size, edgecolor='w'),\
      colors=outer_color, textprops={"fontsize":12},labeldistance=1.1)
ax.set(xlim=(-1,1), ylim=(-1,1))
ax.set(aspect="equal")
plt.show()

enter image description here


Solution

  • To achieve what You want, You have to play a bit with text labels. You can position each individual wedge and label. List of wedges and labels are returned from ax.pie() method.

    To remove label, just set its text to empty string.

    To have nicer inner labels, I would suggest to move percentage to new line and then rotate each label accordingly.

    Here is code snippet doing so:

    import matplotlib.pyplot as plt
    import pandas as pd
    
    data = pd.read_csv(r"https://raw.githubusercontent.com/tuyenhavan/Course_Data/main/test_pie_chart.csv")
    # Get outer and inner counts
    inner = data.groupby(["Col1"]).size()
    outer = data.groupby(["Col1", "Col2"]).size()
    # Get outer and inner labels
    inner_label = [f"{idx} \n({val / sum(inner.values.flatten()) * 100:.2f}%)" for idx, val in
                   zip(inner.index.get_level_values(0), inner.values.flatten())]
    outer_label = [f"{idx} ({val / sum(outer.values.flatten()) * 100:.2f}%)" for idx, val in
                   zip(outer.index.get_level_values(1), outer.values.flatten())]
    # Inner and outer colors
    inner_color = ["#16A085", "#808000", "#BA4A00"]
    outer_color = ["#808080", "#229954", "#34495E", "#2E86C1", "#CA6F1E", "white", "#808080", "#229954", "#CA6F1E",
                   "#00FFFF"]
    # Set figure size and radius adjustment
    fig, ax = plt.subplots(figsize=(10, 8))
    size = 0.4
    # Plot the inner pie chart
    wedges_lst, labels_lst = ax.pie(inner.values.flatten(), labels=inner_label, radius=1 - size, wedgeprops=dict(width=size, edgecolor='w'), \
           colors=inner_color, textprops={"fontsize": 12}, labeldistance=0.6)  # labels=inner_label,autopct ='%1.1f%%',
    labels_lst[0].update({"rotation": 0, "horizontalalignment": "center", "verticalalignment": "center"})
    labels_lst[1].update({"rotation": 135, "horizontalalignment": "center", "verticalalignment": "center"})
    labels_lst[2].update({"rotation": 225, "horizontalalignment": "center", "verticalalignment": "center"})
    
    # Plot the outer pie chart
    wedges_lst, labels_lst = ax.pie(outer.values.flatten(), radius=1, labels=outer_label, wedgeprops=dict(width=size, edgecolor='w'), \
           colors=outer_color, textprops={"fontsize": 12}, labeldistance=1.1)
    
    # Remove # No text label
    labels_lst[5].update({"text": ""})
    
    ax.set(xlim=(-1, 1), ylim=(-1, 1))
    ax.set(aspect="equal")
    plt.show()
    

    Resulting in: Pie Chart