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
import numpy as np
import pandas as pd
from svgpathtools import svg2paths
from svgpath2mpl import parse_path
import matplotlib as mpl
code_dict = {
"Norway": "NO",
"Denmark": "DK",
"Sweden": "SE"
}
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": ["Sweden", "Sweden", "Denmark", "Denmark", "Norway", "Norway"],
"sites": [13, 15, 4, 10, 5, 8],
}
df = pd.DataFrame(data)
df["year_lbl"] = "'" + df["year"].astype(str).str[-2:].astype(str)
df["sub_total"] = df.groupby("countries")["sites"].transform("sum")
df["ctry_code"] = df.countries.map(code_dict)
df = df.sort_values(["sub_total"], ascending=True).reset_index(drop=True)
df["data_label"] = df.groupby(["countries"])["sites"].diff()
# map the colors of a dict to a dataframe
df["color"] = df.countries.map(color_dict)
df
| year | countries | sites | year_lbl | sub_total | ctry_code | data_label | color | |
|---|---|---|---|---|---|---|---|---|
| 0 | 2022 | Norway | 8 | '22 | 13 | NO | NaN | #2B314D |
| 1 | 2004 | Norway | 5 | '04 | 13 | NO | -3.0 | #2B314D |
| 2 | 2022 | Denmark | 10 | '22 | 14 | DK | NaN | #A54836 |
| 3 | 2004 | Denmark | 4 | '04 | 14 | DK | -6.0 | #A54836 |
| 4 | 2004 | Sweden | 13 | '04 | 28 | SE | NaN | #5375D4 |
| 5 | 2022 | Sweden | 15 | '22 | 28 | SE | 2.0 | #5375D4 |
icon_path, attributes = svg2paths(
"../flags/Unesco_World_Heritage_logo_notext_transparent.svg"
)
# matplotlib path object of the icon
icon_marker = parse_path(attributes[0]["d"])
icon_marker.vertices -= icon_marker.vertices.mean(axis=0)
icon_marker = icon_marker.transformed(mpl.transforms.Affine2D().rotate_deg(180))
icon_marker = icon_marker.transformed(mpl.transforms.Affine2D().scale(-1, 1))
colors = [
[grid_color] * 4 + ["#2C324F"] + ["#7C8091"] * 2 + ["#2C324F"] + [grid_color] * 7,
[grid_color] * 3 + ["#CC5A43"] + ["#DD9A8D"] * 5 + ["#CC5A43"] + [grid_color] * 5,
[grid_color] * 12 + ["#5375D4"] + ["#90B0F3"] * 1 + ["#5375D4"],
]
groups = df.groupby("countries", sort = False)
fig, axes = plt.subplots(nrows=df.countries.nunique(), ncols=1, figsize=(10, 6), facecolor="w")
fig.subplots_adjust(hspace=0.8)
for i, ((country,group), ax) in enumerate(zip(groups, axes.ravel())):
x= np.arange(1, 16)
ax.bar(
x,
1,
color = colors[i]
)
# plot markers
ax.plot(
-1,
0.6,
marker = icon_marker,
color = group.color.iloc[0],
markersize = 32,
clip_on = False
)
# add data labels
for bar, lbl in zip(ax.patches, x):
ax.text(
bar.get_x() + bar.get_width() / 2,
0.12,
lbl,
ha="center",
va="center",
color="w",
size=12,
)
# add diff labels
ax.text(
group.sites.median(),
1.1,
f"+{int(abs(group.data_label.iloc[1]))}",
size = 12,
weight = "bold",
ha = "center"
)
ax.set_ylabel(
group.ctry_code.iloc[0],
size = 14,
weight = "light",
va = "baseline",
rotation = 0
)
ax.tick_params(
axis="both",
which="both",
length=0,
labelleft = False,
labelbottom = False
)
ax.set_frame_on(False)