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.
from math import sqrt, sin, cos, pi
import matplotlib.pyplot as plt
import matplotlib.patches as patches
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)
sort_order_dict = {"Denmark": 1, "Sweden": 2, "Norway": 3, 2004: 4, 2022: 5}
df = df.sort_values(by=["year","countries"], key=lambda x: x.map(sort_order_dict))
df["colors"] = df.countries.map(color_dict)
df['diff'] = df.groupby(['countries'])['sites'].diff()
df['diff'] = df['diff'].fillna(df.sites)
df
| year | countries | sites | colors | diff | |
|---|---|---|---|---|---|
| 0 | 2004 | Denmark | 4 | #A54836 | 4.0 |
| 4 | 2004 | Sweden | 13 | #5375D4 | 13.0 |
| 2 | 2004 | Norway | 5 | #2B314D | 5.0 |
| 1 | 2022 | Denmark | 10 | #A54836 | 6.0 |
| 5 | 2022 | Sweden | 15 | #5375D4 | 2.0 |
| 3 | 2022 | Norway | 8 | #2B314D | 3.0 |
Lets manually specify the position of the boxes and colors:
colors = [ '#929597', '#929597', '#929597', "#A54836", "#5375D4", "#2B314D" ]
text_above = ["All", "Before 2004", "After 2004", "Denmark", "Sweden", "Norway"]
#height and width of the squares
height = [30, 10, 10, 15, 15, 15]
width = [75, 52, 52, 17, 25, 16]
#the lower left corner of the square
all = [1, 1]
after = [10, 4]
before = [12,17]
dk = [6, 7]
sw = [25, 9]
no = [52, 8]
squares = [ all, after, before, dk, sw, no ]
Plot the chart¶
Add the squares:
fig, ax = plt.subplots(figsize=(6,6), facecolor = "#FFFFFF" )
nr_squares = 6
for i in range(nr_squares):
x, y = squares[i]
h = height[i]
# Add a rounded rectangle
rect = patches.FancyBboxPatch(
(x, y), width[i], h,
boxstyle="round,pad=0.1,rounding_size=1", # rounding_size controls how rounded
linewidth=1,
edgecolor=colors[i],
facecolor='none'
)
ax.add_patch(rect)
if i != 1:
ax.text(
x + 1.2,
y + h ,
text_above[i],
size = 10,
color = colors[i],
bbox=dict(facecolor='w', edgecolor='none', boxstyle='round,pad=0.2')
)
if i == 1:
ax.text(
x + 1.2,
y,
text_above[i],
size= 10,
color = colors[i],
bbox=dict(facecolor='w', edgecolor='none', boxstyle='round,pad=0.2')
)
# Adjust the view
ax.set_xlim(-1, 80)
ax.set_ylim(-1, 35)
ax.set_axis_off()
Generate 6 dot plots in the precise location with this function that creates a sunflower effect and uniformly distribute points inside a circle:
phi = (1 + sqrt(5)) / 2 # golden ratio
def sunflower(n, alpha=0, geodesic=False):
points = []
angle_stride = 360 * phi if geodesic else 2 * pi / phi ** 2
b = round(alpha * sqrt(n)) # number of boundary points
for k in range(1, n + 1):
r = radius(k, n, b)
theta = k * angle_stride
points.append((r * cos(theta), r * sin(theta)))
return points
def radius(k, n, b):
if k > n - b:
return 1.0
else:
return sqrt(k - 0.5) / sqrt(n - (b + 1) / 2)
We manually specify the coordinates of each axes where we will place the dots:
x_dot = [0.25, 0.375, 0.63, 0.23, 0.375, 0.63]
y_dot = [0.494, 0.494, 0.494, 0.28, 0.32, 0.30]
height_dot = [0.11, 0.152, 0.13, 0.155, 0.115, 0.135]
width_dot = [0.11, 0.24, 0.12, 0.125, 0.24, 0.1]
for i, row in enumerate(df.itertuples()):
#add the new axes
ax= fig.add_axes([x_dot[i], y_dot[i], width_dot[i], height_dot[i]])
ax.patch.set_facecolor(row.colors)
ax.patch.set_alpha(0.2)
ax.spines[['top', 'left', 'right', 'bottom']].set_visible(False)
ax.tick_params(labelleft = False, labelbottom= False, length = 0)
#add sunflower
points = sunflower(row.sites, alpha = 0.8, geodesic= False)
xs = [point[0] for point in points]
ys = [point[1] for point in points]
ax.scatter(xs, ys, s= 60, color = row.colors, clip_on= False)
fig