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
import numpy as np
import pandas as pd
color_dict = {"Norway": "#2B314D", "Denmark": "#A54836", "Sweden": "#5375D4" }
code_dict = {"Norway": "NO", "Denmark": "DK", "Sweden": "SE" }
xy_ticklabel_color, bar_color, datalabels_color ='#90979E', "#eeeeee", "#FFFFFF"
data = {
"year": [2004, 2022, 2004, 2022, 2004, 2022],
"countries" : ["Sweden", "Sweden", "Denmark", "Denmark", "Norway", "Norway"],
"sites": [13,15,4,10,5,8]
}
df= pd.DataFrame(data)
df['ctry_code'] = df.countries.map(code_dict)
df['pct_change'] = df.groupby('countries', sort=False)['sites'].apply(
lambda x: x.pct_change()).to_numpy()*100
df['pct_change'] = df['pct_change'].fillna(0.0).astype(int)
df['pct_change'] = np.where(df['pct_change']==0, "", df['pct_change'].astype(str)+"%")
#map the colors of a dict to a dataframe
df['color']= df.countries.map(color_dict)
df = df.sort_values([ 'year'], ascending=True )
df
| year | countries | sites | ctry_code | pct_change | color | |
|---|---|---|---|---|---|---|
| 0 | 2004 | Sweden | 13 | SE | #5375D4 | |
| 2 | 2004 | Denmark | 4 | DK | #A54836 | |
| 4 | 2004 | Norway | 5 | NO | #2B314D | |
| 1 | 2022 | Sweden | 15 | SE | 15% | #5375D4 |
| 3 | 2022 | Denmark | 10 | DK | 150% | #A54836 |
| 5 | 2022 | Norway | 8 | NO | 60% | #2B314D |
fig, axes = plt.subplots(nrows = len(df.year.unique()), figsize=(15,5))
fig.subplots_adjust(hspace=10)
lines = np.arange(0,21)
for i, (ax, (year, group)) in enumerate(zip(axes.ravel(), df.groupby("year", sort = False))):
ax.barh(
y = range(1),
width = 20,
height = .2,
color = bar_color
)
#Break the bars with lines
for line in zip(lines):
ax.axvline(
x=line,
ymin = 0,
ymax = .9,
color=xy_ticklabel_color,
linestyle= "-"
)
for row in group.itertuples():
color = row.color
site = row.sites
#add country codes
ax.annotate(
row.ctry_code,
xy = (site, .7),
color=datalabels_color,
weight= "bold",
ha="center",
bbox=dict(ec =color, boxstyle='round,pad=0.5', fc = color),
arrowprops=dict( arrowstyle='-',color = color ),
annotation_clip=False
)
ax.text(
site,
0.57,
"\u25BC",
size= 12,
color = color,
ha="center"
)
ax.axvline(
x=site,
ymin = 0,
ymax = 3.1,
color=color,
linestyle= "-",
clip_on = False
)
if i == 1:
ax.annotate(
row.pct_change,
xy = (site-0.3, 1.0),
color = color,
weight= "bold",
annotation_clip=False
)
#ax.set_ylim(0,1)
ax.set_xlim(0,20)
ax.xaxis.set_ticks(np.arange(0, 25, 5), labels = [0,5,10,15,20])
ax.yaxis.set_ticks(np.arange(0, 1, 1), labels = [])
ax.tick_params(axis='both', which='major',length=0, labelsize=12,colors =xy_ticklabel_color)
ax.set_frame_on(False)
#add the y lables
ax.set_ylabel(year, size = 16, rotation =0, va = "top", labelpad=50, color=xy_ticklabel_color)