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.
#tutorial
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import numpy as np
import pandas as pd
color_dict = {"Norway": "#2B314D", "Denmark": "#A54836", "Sweden": "#5375D4" }
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)
df['ctry_code'] = df.countries.astype(str).str[:2].astype(str).str.upper()
df['year_lbl'] ="'"+df['year'].astype(str).str[-2:].astype(str)
df = df.sort_values(['countries' ,'sites' ], ascending=False ).reset_index(drop=True)
#map the colors of a dict to a dataframe
df['color']= df.countries.map(color_dict)
df
| year | countries | sites | ctry_code | year_lbl | color | |
|---|---|---|---|---|---|---|
| 0 | 2022 | Sweden | 15 | SW | '22 | #5375D4 |
| 1 | 2004 | Sweden | 13 | SW | '04 | #5375D4 |
| 2 | 2022 | Norway | 8 | NO | '22 | #2B314D |
| 3 | 2004 | Norway | 5 | NO | '04 | #2B314D |
| 4 | 2022 | Denmark | 10 | DE | '22 | #A54836 |
| 5 | 2004 | Denmark | 4 | DE | '04 | #A54836 |
Get the flags:
img = [
plt.imread("../flags/sw-rd.png"),
plt.imread("../flags/no-rd.png"),
plt.imread("../flags/de-rd.png")
]
First we need to generate the axes. As they are in random positions, we will do it manually.
We will then put it in a pandas dataframe to loop over it easier.
# Generate 10 plots in a grid with 3 rows, 5 columns
x_coord = [-0.1,0.4,0.55]
y_coord = [0.6,0.3, 1.2]
width = [1,0.4,0.6]
height = [1,0.4,0.6]
direction_sites = [-1, 0, 0] #direction of the data labels
direction_lables = [1, -1, 1] #direction of the year labels
axes_coord = pd.DataFrame({
'x': x_coord,
'y': y_coord,
'w': width,
'h': height,
'dir_lbl': direction_lables,
'dir_sites': direction_sites
})
axes_coord
| x | y | w | h | dir_lbl | dir_sites | |
|---|---|---|---|---|---|---|
| 0 | -0.10 | 0.6 | 1.0 | 1.0 | 1 | -1 |
| 1 | 0.40 | 0.3 | 0.4 | 0.4 | -1 | 0 |
| 2 | 0.55 | 1.2 | 0.6 | 0.6 | 1 | 0 |
Plot the chart¶
fig = plt.figure(figsize=(5, 5))
for i, ((country, group), row) in enumerate(zip(df.groupby("countries", sort = False), axes_coord.itertuples())):
#add the axes
ax= fig.add_axes([row.x, row.y, row.w, row.h], polar=True)
max_sites = group.sites.max()
#add the data labels
ax.text(
np.pi * row.dir_sites,
max_sites + 2,
max_sites,
color = group['color'].iloc[0],
size = 12,
weight = "bold",
ha="center",
va= "center"
)
# add the flags
image_box = OffsetImage(img[i], zoom=0.02) # container for the image
ab = AnnotationBbox(
image_box,
(0, 0),
frameon=False
)
ax.add_artist(ab)
for g in group.itertuples():
#add the cirlces
ax.plot(
np.linspace(0, 2*np.pi, 100),
np.ones(100)*g.sites,
color=g.color,
linestyle='-',
lw=2
)
ax.set_rgrids(range(0,max_sites))
ax.xaxis.grid(False)
ax.set_frame_on(False)
ax.yaxis.grid(
True,
color=g.color,
linestyle='-'
)
ax.patch.set_alpha(0.1) #transparent axis
ax.tick_params(labelleft= False, labelbottom = False)
#add the year lables
ax.annotate(
g.year_lbl,
xy = (np.pi/2 * row.dir_lbl, g.sites-0.1),
color = g.color,
ha="center",
va= "center" ,
bbox=dict(
facecolor='w',
edgecolor='w',
boxstyle='round,pad=0.2'
)
)