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",
}
xy_ticklabel_color, grid_color, datalabels_color = "#757C85", "#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)
df = df.sort_values(["year", "sites"], ascending=False).reset_index(drop=True)
df["rank"] = df.groupby("year")["sites"].rank().astype(int)
# map the colors of a dict to a dataframe
df["color"] = df.countries.map(color_dict)
df
| year | countries | sites | rank | color | |
|---|---|---|---|---|---|
| 0 | 2022 | Sweden | 15 | 3 | #5375D4 |
| 1 | 2022 | Denmark | 10 | 2 | #A54836 |
| 2 | 2022 | Norway | 8 | 1 | #2B314D |
| 3 | 2004 | Sweden | 13 | 3 | #5375D4 |
| 4 | 2004 | Norway | 5 | 2 | #2B314D |
| 5 | 2004 | Denmark | 4 | 1 | #A54836 |
unique_countries = df.countries.unique()
unique_years = df.year.unique()
groups = df.groupby("countries")
Plot the chart¶
We will use ax.plot() method and need the following parameters:
| Parameter | Description | Value |
|---|---|---|
| x | The x position of each dot | year |
| y | The y position of each dot | rank |
| marker | The marker for each dot | -o |
It is possible to use ax.scatter too, but with ax.plot we can draw the line and the dot at the same time by using the marker -o
fig, ax = plt.subplots(figsize=(4, 5), facecolor="#FFFFFF")
for country, group in groups:
ax.plot(
group.year,
group['rank'],
'-o',
ms=28,
mec = "w",
mew = 4,
color = group["color"].iloc[0],
zorder=1,
)
Add the data labels and legend¶
for row in df.itertuples():
year = row.year
rank = row.rank
ax.text(
year,
rank,
row.sites,
size = 10,
ha = "center",
va = "center",
color = datalabels_color
)
offset = 5
if year == df.year.max():
ax.text(
year + offset,
rank,
row.countries,
ha = "left",
va = "center",
weight = "bold",
color = row.color
)
fig
Style the chart¶
#set custom ticks
ax.yaxis.set_ticks(np.arange(1, len(unique_countries) + 1, 1))
ax.tick_params(
axis="both",
which="major",
labeltop=True,
labelbottom=False,
length=0,
labelsize=16,
colors=xy_ticklabel_color,
)
ax.set_frame_on(False)
ax.xaxis.set_ticks(unique_years, labels=unique_years)
# draws in the data coordinates hlines
ax.hlines(
np.linspace(0.5, len(unique_countries) + 0.5, num=len(unique_countries) + 1),
1995,
2034,
lw=0.5,
color=grid_color,
clip_on=False,
zorder=0,
)
fig