Print

Radar charts in matplotlib

In this tutorial I will show you how to create Radar charts using Python and Matplotlib. For more matplotlib charts, check out the gallery:

1 dataset 100 matplotlib visualizations
Python dataviz gallery, matplotlib viz gallery

Important notes:

1. This are my personal notes, so apologies if some explanations and notations are missing.

Radar charts in matplotlib

Click on the links to go to the specific tutorials:

  1. Radar chart without filled areas (81_1)
  2. Radar chart with all areas filled (81_2)
  3. Radar chart with only one area filled (81_3)
  4. Curved radar chart (81 of 100)

Create a radar chart using matplotlib

We start by importing the libraries, creating the sample data and adding the necessary columns:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.lines import Line2D

color_dict = { 2022: "#A54836", 2004: "#5375D4", }

spines_color, legend_color, grid_color, datalabels_color ='#BABABA',"#101628", "#BABABA", "#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)
#custom sort
sort_order_dict = {"Denmark":2, "Sweden":3, "Norway":1, 2004:4, 2022:5}
df = df.sort_values(by=['year','countries',], key=lambda x: x.map(sort_order_dict))
#map the colors of a dict to a dataframe
df['color']= df.year.map(color_dict)
df
yearcountriessitescolor
42004Norway5#5375D4
22004Denmark4#5375D4
02004Sweden13#5375D4
52022Norway8#A54836
32022Denmark10#A54836
12022Sweden15#A54836

Define the variables

labels = df.countries.unique()
sites = df.sites
max_sites = df.sites.max()
years = df.year.unique()
unique_colors = df.color.unique()


#As we have 3 categories the radar chart shoud have 3 radial axis
# To find out the angle of each quadrant we divide 360/3 
#angles need to be converted to radian so we multiply by 2*pi and create the list of angles:
angle = np.linspace(0, 2*np.pi, len(labels), endpoint=False)
#add the first angle to the end of the list to close the radar
angles = np.concatenate((angle,[angle[0]]))

Plot the chart

fig, ax= plt.subplots(figsize=(5,5), facecolor = "#FFFFFF", subplot_kw=dict(polar=True) )

for year in years:
    sites = df[df.year == year].sites.to_list()
    color = df[df.year == year].color.to_list()
    sites.append(sites[0])
    color.append(color[0])
    for c,a, s in zip(color, angles, sites):
        ax.plot(angles, sites, 'o-', ms = 14, mec = "w", linewidth=1, color = c, clip_on= False, zorder =2)
        #bubble data labels
        ax.annotate(s, xy=(a,s), color ="w", size= 8, ha="center", va="center")

# Fix axis to go in the right order and start at 12 o'clock.
ax.set_theta_offset(np.pi / 2)
ax.set_theta_direction(-1)

ax.spines["polar"].set_color("none")
ax.set_rlim(0,max_sites)

# Change the color and linestyle of the circular gridlines.
ax.yaxis.grid(True,color=grid_color, ls= "dotted")

# Set the country labels for the angular axis (x)
ax.set_xticks(angle)
ax.set_xticklabels(labels, size=10)

# Go through labels and adjust alignment based on where
# it is in the circle.
for label, angle in zip(ax.get_xticklabels(), angles):
    if 0 < angle < np.pi:
        label.set_horizontalalignment('left')
    elif angle ==0:
        label.set_horizontalalignment('center')
    else:
        label.set_horizontalalignment('right')


#spine to the back
for k, spine in ax.spines.items():  
    spine.set_zorder(0)

#hide ticklabels 
ax.set_yticklabels([])
#add custom tick labels
half_angle = (2*np.pi)/len(labels)/2
for i in range(0, max_sites,2):
    ax.text(half_angle, i, i , size=8)

#add legends
lines = [Line2D([0], [0], color=c,  linestyle='-', markersize=6,) for c in unique_colors]
plt.figlegend( lines,years, labelcolor=legend_color,
           bbox_to_anchor=(0.5, -0.05), loc="lower center",
            ncols = 2,frameon=False, fontsize= 10)
81 of 100: Radar chart in matplotlib

Radar chart with both shaded areas

We start by importing the libraries, creating the sample data and adding the necessary columns:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.lines import Line2D

color_dict = { 2022: "#A54836", 2004: "#5375D4", }

spines_color, legend_color, grid_color, datalabels_color ='#BABABA',"#101628", "#BABABA", "#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)
#custom sort
sort_order_dict = {"Denmark":2, "Sweden":3, "Norway":1, 2004:4, 2022:5}
df = df.sort_values(by=['year','countries',], key=lambda x: x.map(sort_order_dict))
#map the colors of a dict to a dataframe
df['color']= df.year.map(color_dict)
df
yearcountriessitescolor
42004Norway5#5375D4
22004Denmark4#5375D4
02004Sweden13#5375D4
52022Norway8#A54836
32022Denmark10#A54836
12022Sweden15#A54836

Define the variables

labels = df.countries.unique()
sites = df.sites
max_sites = df.sites.max()
years = df.year.unique()
unique_colors = df.color.unique()


#As we have 3 categories the radar chart shoud have 3 radial axis
# To find out the angle of each quadrant we divide 360/3 
#angles need to be converted to radian so we multiply by 2*pi and create the list of angles:
angle = np.linspace(0, 2*np.pi, len(labels), endpoint=False)
#add the first angle to the end of the list to close the radar
angles = np.concatenate((angle,[angle[0]]))

Plot the chart

fig, ax= plt.subplots(figsize=(5,5), facecolor = "#FFFFFF", subplot_kw=dict(polar=True) )

for year in years:
    sites = df[df.year == year].sites.to_list()
    color = df[df.year == year].color.to_list()
    sites.append(sites[0])
    color.append(color[0])

    for c,   a, s in zip(color,  angles, sites):
        ax.plot(angles, sites, '-', ms = 14, mec = "w", linewidth=2, color = c, clip_on= False, zorder =2)
        #bubble data labels
        ax.fill(angles, sites, alpha= .2, color = c)

# Fix axis to go in the right order and start at 12 o'clock.
ax.set_theta_offset(np.pi / 2)
ax.set_theta_direction(-1)

ax.spines["polar"].set_color("none")
ax.set_rlim(0,max_sites)

# Change the color and linestyle of the circular gridlines.
ax.yaxis.grid(True,color=grid_color, ls= "dotted", lw=1)

# Set the country labels for the angular axis (x)
ax.set_xticks(angle)
ax.set_xticklabels(labels, size=10)

# Go through labels and adjust alignment based on where
# it is in the circle.
for label, angle in zip(ax.get_xticklabels(), angles):
    if 0 < angle < np.pi:
        label.set_horizontalalignment('left')
    elif angle ==0:
        label.set_horizontalalignment('center')
    else:
        label.set_horizontalalignment('right')


#hide ticklabels 
ax.set_yticklabels([])
#add custom tick labels
half_angle = (2*np.pi)/len(labels)/2
for i in range(0, max_sites,2):
    ax.text(half_angle, i, i , size=8)

#add legends
lines = [Line2D([0], [0], color=c,  linestyle='-', markersize=6,) for c in unique_colors]
plt.figlegend( lines,years, labelcolor=legend_color,
           bbox_to_anchor=(0.5, -0.05), loc="lower center",
            ncols = 2,frameon=False, fontsize= 10)
81 of 100: Radar chart in matplotlib

Radar chart with one shaded area only

We start by importing the libraries, creating the sample data and adding the necessary columns:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.lines import Line2D

color_dict = { 2022: "#A54836", 2004: "#5375D4", }

spines_color, legend_color, grid_color, datalabels_color ='#BABABA',"#101628", "#BABABA", "#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)
#custom sort
sort_order_dict = {"Denmark":2, "Sweden":3, "Norway":1, 2004:4, 2022:5}
df = df.sort_values(by=['year','countries',], key=lambda x: x.map(sort_order_dict))
#map the colors of a dict to a dataframe
df['color']= df.year.map(color_dict)
df
yearcountriessitescolor
42004Norway5#5375D4
22004Denmark4#5375D4
02004Sweden13#5375D4
52022Norway8#A54836
32022Denmark10#A54836
12022Sweden15#A54836

Define the variables

labels = df.countries.unique()
sites = df.sites
max_sites = df.sites.max()
years = df.year.unique()
unique_colors = df.color.unique()


#As we have 3 categories the radar chart shoud have 3 radial axis
# To find out the angle of each quadrant we divide 360/3 
#angles need to be converted to radian so we multiply by 2*pi and create the list of angles:
angle = np.linspace(0, 2*np.pi, len(labels), endpoint=False)
#add the first angle to the end of the list to close the radar
angles = np.concatenate((angle,[angle[0]]))

Plot the chart

fig, ax= plt.subplots(figsize=(5,5), facecolor = "#FFFFFF", subplot_kw=dict(polar=True) )

for year, ap in zip(years,alpha):
    sites = df[df.year == year].sites.to_list()
    color = df[df.year == year].color.to_list()
    sites.append(sites[0])
    color.append(color[0])
    for c,   a, ap, s in zip(color,  angles, ap, sites):
        ax.plot(angles, sites, '-', ms = 14, mec = "w", linewidth=2, color = c, clip_on= False, zorder =2)
        #bubble data labels
        ax.fill(angles, sites, alpha= ap, color = c)

# Fix axis to go in the right order and start at 12 o'clock.
ax.set_theta_offset(np.pi / 2)
ax.set_theta_direction(-1)

ax.spines["polar"].set_color("none")
ax.set_rlim(0,max_sites)

# Change the color and linestyle of the circular gridlines.
ax.yaxis.grid(True,color=grid_color, ls= "dotted", lw=1)

# Set the country labels for the angular axis (x)
ax.set_xticks(angle)
ax.set_xticklabels(labels, size=10)

# Go through labels and adjust alignment based on where
# it is in the circle.
for label, angle in zip(ax.get_xticklabels(), angles):
    if 0 < angle < np.pi:
        label.set_horizontalalignment('left')
    elif angle ==0:
        label.set_horizontalalignment('center')
    else:
        label.set_horizontalalignment('right')

#hide ticklabels 
ax.set_yticklabels([])
#add custom tick labels
half_angle = (2*np.pi)/len(labels)/2
for i in range(0, max_sites,2):
    ax.text(half_angle, i, i , size=8)

#add legends
lines = [Line2D([0], [0], color=c,  linestyle='-', markersize=6,) for c in unique_colors]
plt.figlegend( lines,years, labelcolor=legend_color,
           bbox_to_anchor=(0.5, -0.05), loc="lower center",
            ncols = 2,frameon=False, fontsize= 10)
81 of 100: Radar chart in matplotlib

Looking for more matplotlib tutorials?

Check out the 1 dataset, 100 matplotlib visualizations project:

1 dataset 100 matplotlib visualizations
Python dataviz gallery, matplotlib viz gallery
Was this helpful?

Reader Interactions

Leave a Reply

Your email address will not be published. Required fields are marked *

Table of Contents