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
color_dict = {"Norway": "#2B314D", "Denmark": "#A54836", "Sweden": "#5375D4"}
code_dict = {"Norway": "NO", "Denmark": "DK", "Sweden": "SE" }
xy_ticklabel_color, grid_color, datalabels_color ='#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)
sort_order_dict = {"Denmark":2, "Sweden":3, "Norway":1}
df = df.sort_values(by=['countries',], key=lambda x: x.map(sort_order_dict))
#map the colors of a dict to a dataframe
df['color']= df.countries.map(color_dict)
df['ctry_code'] = df.countries.map(code_dict)
df
| year | countries | sites | color | ctry_code | |
|---|---|---|---|---|---|
| 5 | 2022 | Norway | 8 | #2B314D | NO |
| 4 | 2004 | Norway | 5 | #2B314D | NO |
| 3 | 2022 | Denmark | 10 | #A54836 | DK |
| 2 | 2004 | Denmark | 4 | #A54836 | DK |
| 0 | 2004 | Sweden | 13 | #5375D4 | SE |
| 1 | 2022 | Sweden | 15 | #5375D4 | SE |
First we need the coordinates for the bars.
We will position:
- The x_coord at 2 and 4,
- The y_coord at 2, 4 and 6 and
- The z_coord are the site positions.
x = [x*2 for x in range(1, len(years)+1)]*len(countries)
y = np.repeat([x*2 for x in range(1, len(countries)+1)], len(years))
x = 0
dx = 0.8
dy = 1.2
dz = df.sites
Plot the chart¶
fig = plt.figure(figsize=(6,6))
ax = fig.add_subplot(1, 1, 1, projection="3d")
#move the z axis to the the left
ax.zaxis._axinfo['juggled'] = (1,2,0)
ax.bar3d(
x,
y,
z,
dx,
dy,
dz,
color=df.color
)
<mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x195022fcad0>
By expanding the chart limits we can change the perspective to make it similar to the original:
ax.set(xlim = [1,6], ylim = [1,8], zlim = [0,16])
fig
Add the labels and label styling:
ax.yaxis.set_ticks(np.arange(3, 8, 2), labels = code)
ax.xaxis.set_ticks(np.arange(2.5, 6, 2), labels = reversed(years))
ax.zaxis.set_ticks(np.arange(0, 16, 5), )
ax.tick_params(axis='x', which='major',length=0, labelsize=12,colors= xy_ticklabel_color)
ax.tick_params(axis='z', which='major',length=0, labelsize=12,colors= grid_color)
ax.tick_params(axis='y', which='major',length=0, labelsize=12)
for ytick, color in zip(ax.get_yticklabels(), colors_ticks):
ytick.set_color(color)
fig
and style the grid:
for axis in [ax.xaxis, ax.yaxis, ax.zaxis]:
axis._axinfo['tick']['inward_factor'] = 0
axis._axinfo['tick']['outward_factor'] = 0 #remove ticks
axis.set_pane_color("w") # Make panes transparent
ax.xaxis._axinfo["grid"].update({"linewidth":0, "color" : "w"}) #color gridline
ax.yaxis._axinfo["grid"].update({"linewidth":0, "color" : "w"})
# Transparent spines
ax.xaxis.line.set_color(grid_color)
ax.yaxis.line.set_color(grid_color)
ax.zaxis.line.set_color(grid_color)
ax.plot([6,6], [7.9,7.9],zs=[0,15], color = grid_color)
ax.plot([0.9,0.9], [8.1,8.1],zs=[0,15], color = grid_color)
fig