Generate the data and import packages¶
First we need to create the data. I will do it using a dictionary and then converting it to a pandas dataframe as a lot projects use pandas to work with data.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
color_dict = {
"Norway": "#2B314D",
"Denmark": "#A54836",
"Sweden": "#5375D4", }
xy_ticklabel_color, xy_label_colors, grid_color, ='#9BA0A6',"#9BA0A6", "#C8C9C9",
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 = df.sort_values([ 'year'], 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 | color | |
|---|---|---|---|---|
| 0 | 2022 | Sweden | 15 | #5375D4 |
| 1 | 2022 | Denmark | 10 | #A54836 |
| 2 | 2022 | Norway | 8 | #2B314D |
| 3 | 2004 | Sweden | 13 | #5375D4 |
| 4 | 2004 | Denmark | 4 | #A54836 |
| 5 | 2004 | Norway | 5 | #2B314D |
We define some variables that we will use later:
Plot the chart¶
We will use ax.scatter() method and need the following parameters:
| Parameter | Description | Value |
|---|---|---|
| x | The x positions of each dot | 2002 values |
| y | The y positions of each dot | 2004 values |
| s | The area of each dot | adjust accordingly |
The easiest way to do this is to group using pandas and then extract the values from the group.
Example for Norway:
x = group['sites'].iloc[0] is 8 (the 2022 value )
and y = group['sites'].iloc[1] is 4 (the 2004 value)
groups = df.groupby("countries")
sites_max = df.sites.max()
fig, ax = plt.subplots(figsize=(5,5), facecolor = "#FFFFFF", zorder= 1)
for (_, group) in groups:
x = group['sites'].iloc[0]
y = group['sites'].iloc[1]
color = group['color'].iloc[0]
ax.scatter(
x,
y,
s = 100,
#color = color,
zorder = 2,
clip_on = False, )
Add the country labels and the arrow¶
To add the labels we will use ax.annotate() method because allows to add boxes areound the text. We need the following parameters:
| Parameter | Description | Value |
|---|---|---|
| text | The annotation | |
| xy | The x and y positions of the text | x,y |
| bbox | The box around the tex | |
| arrowprops | The arrow or connecting line |
The arrow down is created using \u25BC.
fig, ax = plt.subplots(figsize=(5,5), facecolor = "#FFFFFF", zorder= 1)
for (country, group) in groups:
x = group['sites'].iloc[0]
y = group['sites'].iloc[1]
color = group['color'].iloc[0]
ax.scatter(
x,
y,
s = 100,
color = color,
zorder = 2,
clip_on = False, )
#scatter labels
ax.annotate(
country,
xy = (x, y),
xytext = (x, y+3),
va = 'center',
ha = "center",
color = 'w',
weight = "bold",
bbox = dict(
ec = color,
boxstyle = 'round,pad=0.5',
fc = color
),
arrowprops=dict(
arrowstyle = '-',
color = color ,
)
)
ax.text(
x,
y + 2.3,
"\u25BC",
size = 12,
ha = "center",
va = "center",
color = color)
Style the chart¶
We just need to style it and we are done!
#hide ticks + other
ax.tick_params(
axis='both',
which='major',
length=0,
labelsize=12,
colors =xy_ticklabel_color
)
# Change x-axis ticklabels & tick spacing
ax.set(
xlim = (0, sites_max),
ylim = (0,sites_max)
)
ax.xaxis.set_ticks(np.arange(0, 20, 5))
ax.yaxis.set_ticks(np.arange(0, 20, 5))
#add axis labels
ax.set_xlabel(
df.year.max(),
size = 12,
color = xy_label_colors,
weight = "bold"
)
ax.set_ylabel(
df.year.min(),
size=12,
color = xy_label_colors,
weight= "bold"
)
#set the color and the position of the axis guidelines/spines
for axis in ['top', 'bottom', 'left', 'right']:
ax.spines[axis].set_color(grid_color)
ax.spines[axis].set_zorder(0)
#change grid color
ax.grid(True, color = grid_color)
fig
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
color_dict = {
"Norway": "#2B314D",
"Denmark": "#A54836",
"Sweden": "#5375D4", }
xy_ticklabel_color, xy_label_colors, grid_color, ='#9BA0A6',"#9BA0A6", "#C8C9C9",
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 = df.sort_values([ 'year'], ascending=False ).reset_index(drop=True)
#map the colors of a dict to a dataframe
df['color']= df.countries.map(color_dict)
groups = df.groupby("countries")
sites_max = df.sites.max()
fig, ax = plt.subplots(figsize=(5,5), facecolor = "#FFFFFF", zorder= 1)
for (country, group) in groups:
x = group['sites'].iloc[0]
y = group['sites'].iloc[1]
color = group['color'].iloc[0]
ax.scatter(
x,
y,
s = 100,
color = color,
zorder = 2,
clip_on = False, )
#scatter labels
ax.annotate(
country,
xy = (x, y),
xytext = (x, y+3),
va = 'center',
ha = "center",
color = 'w',
weight = "bold",
bbox = dict(
ec = color,
boxstyle = 'round,pad=0.5',
fc = color
),
arrowprops=dict(
arrowstyle = '-',
color = color ,
)
)
ax.text(
x,
y + 2.3,
"\u25BC",
size = 12,
ha = "center",
va = "center",
color = color)
#hide ticks + other
ax.tick_params(
axis='both',
which='major',
length=0,
labelsize=12,
colors =xy_ticklabel_color
)
# Change x-axis ticklabels & tick spacing
ax.set(
xlim = (0, sites_max),
ylim = (0,sites_max)
)
ax.xaxis.set_ticks(np.arange(0, 20, 5))
ax.yaxis.set_ticks(np.arange(0, 20, 5))
#add axis labels
ax.set_xlabel(
df.year.max(),
size = 12,
color = xy_label_colors,
weight = "bold"
)
ax.set_ylabel(
df.year.min(),
size=12,
color = xy_label_colors,
weight= "bold"
)
#set the color and the position of the axis guidelines/spines
for axis in ['top', 'bottom', 'left', 'right']:
ax.spines[axis].set_color(grid_color)
ax.spines[axis].set_zorder(0)
#change grid color
ax.grid(True, color = grid_color)