Generate the data and import packages¶
First, we need to create the data. I'll start by defining it as a dictionary and then convert it into a pandas DataFrame, since pandas is commonly used in many projects for data manipulation.
import matplotlib.pyplot as plt
from matplotlib.textpath import TextPath
from matplotlib.patches import PathPatch
import numpy as np
import pandas as pd
color_dict = {"Norway": "#2B314D", "Denmark": "#A54836", "Sweden": "#5375D4" }
xy_ticklabel_color, grand_totals_color, grid_color, datalabels_color ='#757C85',"#101628", "#C8C9C9", "#FFFFFF"
data = {
"year": [2004, 2022, 2004, 2022, 2004, 2022],
"countries" : [ "Denmark", "Denmark", "Norway", "Norway","Sweden", "Sweden"],
"sites": [4,10,5,8,13,15]
}
df= pd.DataFrame(data)
sort_order_dict = {"Denmark":3, "Sweden":2, "Norway":1, 2022:3, 2004:4,}
df = df.sort_values(by=['countries','year'], key=lambda x: x.map(sort_order_dict))
df['ctry_code'] = df.countries.astype(str).str[:2].astype(str).str.upper()
#map the colors of a dict to a dataframe
df['color']= df.countries.map(color_dict)
df
| year | countries | sites | ctry_code | color | |
|---|---|---|---|---|---|
| 3 | 2022 | Norway | 8 | NO | #2B314D |
| 2 | 2004 | Norway | 5 | NO | #2B314D |
| 5 | 2022 | Sweden | 15 | SW | #5375D4 |
| 4 | 2004 | Sweden | 13 | SW | #5375D4 |
| 1 | 2022 | Denmark | 10 | DE | #A54836 |
| 0 | 2004 | Denmark | 4 | DE | #A54836 |
Define the radius and distance to the center:
widths = [0, 0.4]
distances =[0.8, 0.65]
Plot the chart¶
fig, ax = plt.subplots(figsize=(5,5), facecolor = "#FFFFFF")
for i, (year, group) in enumerate(df.groupby("year", sort = False)):
sites = group['sites']
countries = group['countries'].unique()
color = group['color'].unique()
wedges, texts = ax.pie(
sites,
radius= 1-widths[i],
labels = sites,
labeldistance=distances[i],
startangle = 90,
pctdistance=distances[i],
wedgeprops=dict(width=0.35),
textprops=dict(
color =datalabels_color,
fontsize= 10
),
colors= group['color'])
ax.grid(True)
for j, wedge in enumerate(wedges):
angle_deg = (wedge.theta1 + wedge.theta2) / 2 # midpoint angle in degrees
angle_rad = np.radians(angle_deg)
radius = 1.2 # distance from the center to the text
angle_per_char = 3 # degrees per character
text_radius = 2.7
if i == 0:
for idx, char in enumerate(reversed(countries[j])):
rotation_angle = angle_deg - 90
# angle for each character
char_angle = angle_rad + (idx - len(countries[j]) / 2) * np.radians(angle_per_char)
# porlar to cartesian
x = radius * np.cos(char_angle)
y = radius * np.sin(char_angle)
ax.text(
x, y, char,
rotation=rotation_angle,
rotation_mode="anchor",
fontsize=8,
color=color[j],
va="center",
ha="center"
)