Create Eye-Catching Radial Bar Charts With Matplotlib
Radial bar charts are a visually attractive alternative to traditional bar charts. Data are plotted on a polar coordinate system instead of the conventional cartesian coordinate system. This allows the bars to be represented by rings rather than as vertical bars.
Radial bar charts can make a great and visually appealing graphic to include in a presentation or poster if you are looking to catch the reader’s attention. However, as with many data visualisations, they have their own drawbacks. One issue that can arise is that they can be challenging to interpret. This is because our visual system is much better at interpreting straight lines when comparing values between categories. Additionally, rings towards the centre of the radial bar chart become harder to read and compare with those further out.
This article will show how to create a visually appealing radial bar plot using the matplotlib library from Python.
When matplotlib is mentioned, most people think about basic charts that are poorly formatted and need multiple lines of awkward or confusing code to display a great chart.
Creating a Radial Bar Plot With Matplotlib
Importing Libraries and Loading Data
The first step in creating our radial bar chart is to import the libraries we are going to use. For this tutorial, we will need to import matplotlib, pandas and numpy.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
After the libraries are imported, we can load or create some data. One of the most common formats for storing data in Python is the pandas dataframe.
Data can be loaded into a dataframe from a CSV file using the pd.read_csv()
function or created manually from lists or dictionaries.
For this tutorial, we will create a dataframe using dummy lithology data. We will have multiple lithologies and a count of how often that lithology appears within a specific interval of a well.
To create our dataframe, we first create a dictionary with two keys: LITH
and COUNT
, and the values for each item will be lists containing the lithology name and the count of each lithology.
lith_dict = {'LITH': ['Shale', 'Sandstone',
'Sandstone/Shale', 'Chalk',
'Limestone', 'Marl', 'Tuff'],
'COUNT': [40,65, 40, 35,
40, 70, 50]}
df = pd.DataFrame.from_dict(lith_dict)
Once the data has been loaded, we need to find out two things:
- The maximum value of our
COUNT
column. This will give us the maximum value for our rings. - The total number of entries in the dataframe
max_value_full_ring = max(df['COUNT'])
data_len = len(df)
Choosing Colours and Setting Labels
The next step in creating the radial bar chart is to define the colours and the labels that will be displayed.
ring_colours = ['#2f4b7c', '#665191', '#a05195','#d45087',
'#f95d6a','#ff7c43','#ffa600']
Choosing colours for plots can be very subjective.
There are numerous tools and articles out there that can help when choosing an effective colour palette for a chart. Most of which is based on colour theory.
For this plot, I have based the colours on the following website:
https://www.learnui.design/tools/data-color-picker.html
The tool allows you to select up to 8 colours when building a palette, and also allows you to copy the hex codes, which can then be pasted directly into your code.
To set up the labels, we will need to create a list comprehension that loops over the lithology name and the count. This is then joined together within a formatted string in the form of Lithology (Count)
.
Also, at the start of the string, we can add a few spaces. This is an easy way to add some padding between the labels and the start of the rings.
ring_labels = [f' {x} ({v}) ' for x, v in zip(list(df['LITH']),
list(df['COUNT']))]
When we check on the ring_labels
variable, we get back the following list:
[' Shale (40) ',
' Sandstone (65) ',
' Sandstone/Shale (40) ',
' Chalk (35) ',
' Limestone (40) ',
' Marl (70) ',
' Tuff (50) ']
Creating the Radial Bar Chart Figure With Matplotlib
Now that the data has been loaded, we can focus on building the plot.
To create the plot, we first begin by creating the figure as we would with any other matplotlib chart. We will also pass in a few parameters to the plt.figure()
call. The first is the figure size, which we will set to 10 x 10. This can be set to any size you want, but this size gives a good plot for viewing on screen.
Next, we will pass in the linewidth
and edgecolor
parameters. These will add a 10 px border around our plot which will have a slightly different colour to the plot background.
Finally, we will set the facecolor
to a very dark blue.
fig = plt.figure(figsize=(10,10), linewidth=10,
edgecolor='#393d5c',
facecolor='#25253c')
If we run the code above, we will get the following square figure displayed, which has our border and main figure background.
Adding a Polar Axis
The next step is to add an axis to our chart that uses polar coordinates rather than cartesian coordinates.
But, before we add the axis, we first have to create the shape of the axis.
This is done by creating a variable called rect
and assigning it to a list containing 4 numbers. These numbers represent the starting position (x and y coordinates), the height and the width.
If we want to add some padding to our radial bar chart, we need to set the width and height parameters to a value less than 1. In this case, we will set it to 0.8, representing 80% of the available width and height.
rect = [0.1,0.1,0.8,0.8]
We can then create a new axis using fig.add_axes
and pass in the rect
list, along with setting the polar argument to true and removing the axis frame.
Following that, we need to set the radial bars to start from the top of the plot, which is North (N), and we will set the bars to radiate in a counter-clockwise direction. If we wanted the bars to go clockwise, then we would change the set_theta_direction()
to -1.
# Add axis for radial backgrounds
ax_polar_bg = fig.add_axes(rect, polar=True, frameon=False)
# Start bars at top of plot
ax_polar_bg.set_theta_zero_location('N')
# Make bars go counter-clockwise.
ax_polar_bg.set_theta_direction(1)
Adding Faint Background Rings to the Radial Bar Plot
To add some visual interest to our plot, we will add some faint bars that go from 0 to the maximum value. This gives the illusion of bar portions that have yet to be filled.
# Loop through each entry in the dataframe and plot a grey
# ring to create the background for each one
for i in range(data_len):
ax_polar_bg.barh(i, max_value_full_ring*1.5*np.pi/max_value_full_ring,
color='grey',
alpha=0.1)
As we want a space for our labels in the top right of our plot, we will set the bars only to complete 3/4 of a full circle. This is done with the following piece of code, which is passed into the call to ax_polar_bg.barh.
max_value_full_ring*1.5*np.pi/max_value_full_ring
If we run our code at this point, we will now see the plot below and the beginnings of our radial bar plot.
We can hide the polar axis simply by calling .axis()
and passing in the word 'off'
.
# Hide all axis items
ax_polar_bg.axis('off')
When we run the code again, we will see we have a clean plot with empty radial bars.
Adding Coloured Rings to the Radial Bar Plot
Now we are ready to add some colour to our plot.
To do this, we will add a new axis to our figure, which uses very similar code to adding the faint background bars.
Within the set_rgrids()
function, we will pass in a list with 0 to 6. These numbers represent the radii for the radial gridlines. Also, we will pass in the labels and set their appearance within this function. To have the labels appear in the top right of our plot and adjacent to the starting point of the rings, we need to set the angle to 0.
Once the radial grid has been set up, we need to loop through each category within the dataframe and add a ring to the chart using the barh()
function within matplotlib.
As with the previous section of code, we only want the rings to go 3/4 of the way around, so we need to multiply pi by 1.5 instead of 2.
# Add axis for radial chart for each entry in the dataframe
ax_polar = fig.add_axes(rect, polar=True, frameon=False)
ax_polar.set_theta_zero_location('N')
ax_polar.set_theta_direction(1)
ax_polar.set_rgrids([0, 1, 2, 3, 4, 5, 6],
labels=ring_labels,
angle=0,
fontsize=14, fontweight='bold',
color='white', verticalalignment='center')
# Loop through each entry in the dataframe and create a coloured
# ring for each entry
for i in range(data_len):
ax_polar.barh(i, list(df['COUNT'])[i]*1.5*np.pi/max_value_full_ring,
color=ring_colours[i])
When we run our code now, we will get the following plot which is 95% done.
Hiding Polar Grid Lines from the Matplotlib Polar Plot
Finally, we need to remove the polar gridlines, ticks, and associated labels. We can do that like so:
# Hide all grid elements for the
ax_polar.grid(False)
ax_polar.tick_params(axis='both', left=False, bottom=False,
labelbottom=False, labelleft=True)
plt.show()
Which then generates our final radial bar chart figure.
Full Python Code to Generate a Radial Bar Plot
Here is the complete python code for generating the above radial bar chart with matplotlib.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
#Load data into pandas dataframe
lith_dict = {'LITH': ['Shale', 'Sandstone',
'Sandstone/Shale', 'Chalk',
'Limestone', 'Marl', 'Tuff'],
'COUNT': [40,65, 40, 35,
40, 70, 50]}
df = pd.DataFrame.from_dict(lith_dict)
# Get key properties for colours and labels
max_value_full_ring = max(df['COUNT'])
ring_colours = ['#2f4b7c', '#665191', '#a05195','#d45087',
'#f95d6a','#ff7c43','#ffa600']
ring_labels = [f' {x} ({v}) ' for x, v in zip(list(df['LITH']),
list(df['COUNT']))]
data_len = len(df)
# Begin creating the figure
fig = plt.figure(figsize=(10,10), linewidth=10,
edgecolor='#393d5c',
facecolor='#25253c')
rect = [0.1,0.1,0.8,0.8]
# Add axis for radial backgrounds
ax_polar_bg = fig.add_axes(rect, polar=True, frameon=False)
ax_polar_bg.set_theta_zero_location('N')
ax_polar_bg.set_theta_direction(1)
# Loop through each entry in the dataframe and plot a grey
# ring to create the background for each one
for i in range(data_len):
ax_polar_bg.barh(i, max_value_full_ring*1.5*np.pi/max_value_full_ring,
color='grey',
alpha=0.1)
# Hide all axis items
ax_polar_bg.axis('off')
# Add axis for radial chart for each entry in the dataframe
ax_polar = fig.add_axes(rect, polar=True, frameon=False)
ax_polar.set_theta_zero_location('N')
ax_polar.set_theta_direction(1)
ax_polar.set_rgrids([0, 1, 2, 3, 4, 5, 6],
labels=ring_labels,
angle=0,
fontsize=14, fontweight='bold',
color='white', verticalalignment='center')
# Loop through each entry in the dataframe and create a coloured
# ring for each entry
for i in range(data_len):
ax_polar.barh(i, list(df['COUNT'])[i]*1.5*np.pi/max_value_full_ring,
color=ring_colours[i])
# Hide all grid elements for the
ax_polar.grid(False)
ax_polar.tick_params(axis='both', left=False, bottom=False,
labelbottom=False, labelleft=True)
plt.show()
Summary
Radial bar charts provide a visually appealing alternative to traditional bar charts and can easily be created using Python’s matplotlib library. Within this article, we have seen how to build up an eye-catching radial bar chart from scratch using matplotlib, which can easily be used in a poster presentation or infographic.
One Comment