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()
CodePudding user response:
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()