Create Stunning Radar Plots with Matplotlib | by Andy McDonald | Apr, 2023
Visualising geological data using Python’s most popular data visualisation library
Radar plots (also known as spider plots or radar charts) are a popular data visualisation tool that allows us to compare datasets by displaying multiple variables simultaneously on a 2-dimensional plot.
Each variable is represented by a spoke extending from the plot’s centre to the edge, and the magnitude of that variable is represented by how far along the spoke it is. Lines are then drawn between each of the variables to form a web-like shape.
Within geoscience and petrophysics, we can use radar plots to compare how lithologies vary between wells or display mineralogy variations between rock samples.
Within this tutorial, I will illustrate how we can create a radar plot using some synthetic lithology data. The data represents the average lithologies encountered within a well.
Let’s get started!
To get started, we will need to import two libraries: numpy and matplotlib.
import matplotlib.pyplot as plt
import numpy as np
Next, we can create some dummy data.
Traditionally, this data may already by contained within a pandas dataframe. If it is, then it is recommended to convert the required columns to lists before proceeding.
lithologies = ['Shale', 'Sandstone',
'Limestone', 'Dolomite',
'Anhydrite', 'Coal', 'Tuff']well1 = [47, 35, 10, 1, 0, 2, 5]
well2 = [7, 10, 51, 22, 10, 0, 0]
Before we can use the data, we need to ensure that the areas we plot on the radar plot create a closed polygon.
To do this, we need to take all of the elements in the list and then append the first element to the end of the list.
lithologies = [*lithologies, lithologies[0]]
well1 = [*well1, well1[0]]
well2 = [*well2, well2[0]]
If we call upon well2
, we will get back the following list of values and we will see that the first number is now also at the end of the list.
[7, 10, 51, 22, 10, 0, 0, 7]
To begin constructing the radar plot, we will first need to sort the positions of the labels from the lithologies
list. These will be equally spaced around the edge of the polar plot.
label_loc = np.linspace(start=0, stop=2 * np.pi, num=len(lithologies))
Next, we can move on to creating the radar plot figure.
First, we need to define the figure, which will be created using plt.subplots
. Within the subplot_kw
parameter, we need to specify that we want a polar plot.
Following that, we will add two axes to the plot and pass in the label_loc
variable along with the well1
and well2
lists.
Then, we will create the polar grid using a call to plt.thetagrids()
and here, we will pass in the label locations and the lithology labels.
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
plt.show()
When we run the above code, we will return the following radar plot.
What we get back looks good for a basic radar plot. However, we can improve it with a few tweaks.
One way we can improve the radar plot is by adding a transparent fill to the two areas.
This is done using ax.fill()
and passing in label_loc
, the relevant well list containing the values, and setting the opacity to 0.3 using the alpha
parameter.
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
plt.show()
When we run the above code, we now have the following radar plot with the areas filled in.
You will notice that the labels in the previous plot look small and overlap the lines.
We can fix this by setting the tick_params
and changing the padding and labelsize.
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
ax.tick_params(axis='both', which='major', pad=30, labelsize=15)
plt.show()
We get a plot with much more readable labels when we run the code.
The previous plot forms a good radar plot that can be used in reports or presentations. However, if we want to stylise it, we can use one of the many matplotlib theme libraries.
If you want to see what libraries are available, then I recommend checking out my previous article, which looks at 4 popular themes.
The cyberpunk theme is one of my favourite themes for creating stylish plots in matplotlib. However, it is purely for aesthetic reasons, and you should always consider your audience and ensure maximum readability.
We can use the cyberpunk theme by importing the library and adding a with
statement
import mplcyberpunkwith plt.style.context('cyberpunk'):
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))
ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
ax.tick_params(axis='both', which='major', pad=30, labelsize=15)
plt.show()
We can now see that the plot has changed dramatically by adding two lines of code. However, the grid lines are a little faint for my liking.
We can lighten the lines by adding a few additional pieces of code.
Changing the grid lines is done using ax.grid()
and setting the color
and alpha
parameters to white and 0.3, respectively.
In order to add the outer ring for the radar plot, we need to set up a custom tuple containing 4 values: red, green, and blue colour values and an alpha value.
We can then pass this into a call to ax.spines['polar'].set_color()
with plt.style.context('cyberpunk'):
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
ax.tick_params(axis='both', which='major', pad=30, labelsize=15)
ax.spines['polar'].set_linewidth(3)
edge_color = (1, 1, 1, 0.2)
ax.spines['polar'].set_color(edge_color)
ax.grid(color='white', alpha=0.3)
plt.show()
When we run the code, we now have the following radar plot with the radial grid lines. We can play around with the colour and transparency to get the effect we want.
However, bear in mind we do not want to have the lines too bright that they will distract the reader from the main data.
To finish the styling we can set the grid lines so that they range from and to a number that we want. This will make the outer ring feel more balanced with the rest of the rings.
We can do this by setting the ax.set_ylim()
and passing in our min and max values.
with plt.style.context('cyberpunk'):
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
ax.tick_params(axis='both', which='major', pad=30, labelsize=15)
ax.spines['polar'].set_linewidth(3)
edge_color = (1, 1, 1, 0.2)
ax.spines['polar'].set_color(edge_color)
ax.grid(color='white', alpha=0.3)
ax.set_ylim(0, 60)
plt.show()
We now have a much better-looking radar plot.
To finish off our plot, we can help the reader understand the plot better, by adding a legend. This will allow the reader to know what area is what.
We could simply add some labels in the ax.plot()
calls and then call upon ax.legend
, however, this would only add lines to our legend.
If we want to add small rectangles representing the shaded area, we must create some patches first.
This is done by importing Patch
from matplotlib.patches
.
The created patches are then passed into the ax.legend
call.
from matplotlib.patches import Patchwith plt.style.context('cyberpunk'):
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))
ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
ax.tick_params(axis='both', which='major', pad=30, labelsize=15)
ax.spines['polar'].set_linewidth(3)
edge_color = (1, 1, 1, 0.2)
ax.spines['polar'].set_color(edge_color)
ax.grid(color='white', alpha=0.3)
ax.set_ylim(0, 60)
# Create custom legend handles
well1_legend = Patch(facecolor='C0', alpha=0.5, label='Well 1')
well2_legend = Patch(facecolor='C1', alpha=0.5, label='Well 2')
# Add a legend with custom position and handles
ax.legend(handles=[well1_legend, well2_legend],
bbox_to_anchor=(1.3, 0.2), fontsize=20,
frameon=True)
plt.show()
And when we run the code we now have the finished plot.
Radar plots provide a nice data visualisation and can easily be created using matplotlib. With additional style from the Cyberpunk theme library, we can take our radar plot to the next level and make it stylish.
Why don’t you give radar plots a go on your next data visualisation project.
Visualising geological data using Python’s most popular data visualisation library
Radar plots (also known as spider plots or radar charts) are a popular data visualisation tool that allows us to compare datasets by displaying multiple variables simultaneously on a 2-dimensional plot.
Each variable is represented by a spoke extending from the plot’s centre to the edge, and the magnitude of that variable is represented by how far along the spoke it is. Lines are then drawn between each of the variables to form a web-like shape.
Within geoscience and petrophysics, we can use radar plots to compare how lithologies vary between wells or display mineralogy variations between rock samples.
Within this tutorial, I will illustrate how we can create a radar plot using some synthetic lithology data. The data represents the average lithologies encountered within a well.
Let’s get started!
To get started, we will need to import two libraries: numpy and matplotlib.
import matplotlib.pyplot as plt
import numpy as np
Next, we can create some dummy data.
Traditionally, this data may already by contained within a pandas dataframe. If it is, then it is recommended to convert the required columns to lists before proceeding.
lithologies = ['Shale', 'Sandstone',
'Limestone', 'Dolomite',
'Anhydrite', 'Coal', 'Tuff']well1 = [47, 35, 10, 1, 0, 2, 5]
well2 = [7, 10, 51, 22, 10, 0, 0]
Before we can use the data, we need to ensure that the areas we plot on the radar plot create a closed polygon.
To do this, we need to take all of the elements in the list and then append the first element to the end of the list.
lithologies = [*lithologies, lithologies[0]]
well1 = [*well1, well1[0]]
well2 = [*well2, well2[0]]
If we call upon well2
, we will get back the following list of values and we will see that the first number is now also at the end of the list.
[7, 10, 51, 22, 10, 0, 0, 7]
To begin constructing the radar plot, we will first need to sort the positions of the labels from the lithologies
list. These will be equally spaced around the edge of the polar plot.
label_loc = np.linspace(start=0, stop=2 * np.pi, num=len(lithologies))
Next, we can move on to creating the radar plot figure.
First, we need to define the figure, which will be created using plt.subplots
. Within the subplot_kw
parameter, we need to specify that we want a polar plot.
Following that, we will add two axes to the plot and pass in the label_loc
variable along with the well1
and well2
lists.
Then, we will create the polar grid using a call to plt.thetagrids()
and here, we will pass in the label locations and the lithology labels.
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
plt.show()
When we run the above code, we will return the following radar plot.
What we get back looks good for a basic radar plot. However, we can improve it with a few tweaks.
One way we can improve the radar plot is by adding a transparent fill to the two areas.
This is done using ax.fill()
and passing in label_loc
, the relevant well list containing the values, and setting the opacity to 0.3 using the alpha
parameter.
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
plt.show()
When we run the above code, we now have the following radar plot with the areas filled in.
You will notice that the labels in the previous plot look small and overlap the lines.
We can fix this by setting the tick_params
and changing the padding and labelsize.
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
ax.tick_params(axis='both', which='major', pad=30, labelsize=15)
plt.show()
We get a plot with much more readable labels when we run the code.
The previous plot forms a good radar plot that can be used in reports or presentations. However, if we want to stylise it, we can use one of the many matplotlib theme libraries.
If you want to see what libraries are available, then I recommend checking out my previous article, which looks at 4 popular themes.
The cyberpunk theme is one of my favourite themes for creating stylish plots in matplotlib. However, it is purely for aesthetic reasons, and you should always consider your audience and ensure maximum readability.
We can use the cyberpunk theme by importing the library and adding a with
statement
import mplcyberpunkwith plt.style.context('cyberpunk'):
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))
ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
ax.tick_params(axis='both', which='major', pad=30, labelsize=15)
plt.show()
We can now see that the plot has changed dramatically by adding two lines of code. However, the grid lines are a little faint for my liking.
We can lighten the lines by adding a few additional pieces of code.
Changing the grid lines is done using ax.grid()
and setting the color
and alpha
parameters to white and 0.3, respectively.
In order to add the outer ring for the radar plot, we need to set up a custom tuple containing 4 values: red, green, and blue colour values and an alpha value.
We can then pass this into a call to ax.spines['polar'].set_color()
with plt.style.context('cyberpunk'):
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
ax.tick_params(axis='both', which='major', pad=30, labelsize=15)
ax.spines['polar'].set_linewidth(3)
edge_color = (1, 1, 1, 0.2)
ax.spines['polar'].set_color(edge_color)
ax.grid(color='white', alpha=0.3)
plt.show()
When we run the code, we now have the following radar plot with the radial grid lines. We can play around with the colour and transparency to get the effect we want.
However, bear in mind we do not want to have the lines too bright that they will distract the reader from the main data.
To finish the styling we can set the grid lines so that they range from and to a number that we want. This will make the outer ring feel more balanced with the rest of the rings.
We can do this by setting the ax.set_ylim()
and passing in our min and max values.
with plt.style.context('cyberpunk'):
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
ax.tick_params(axis='both', which='major', pad=30, labelsize=15)
ax.spines['polar'].set_linewidth(3)
edge_color = (1, 1, 1, 0.2)
ax.spines['polar'].set_color(edge_color)
ax.grid(color='white', alpha=0.3)
ax.set_ylim(0, 60)
plt.show()
We now have a much better-looking radar plot.
To finish off our plot, we can help the reader understand the plot better, by adding a legend. This will allow the reader to know what area is what.
We could simply add some labels in the ax.plot()
calls and then call upon ax.legend
, however, this would only add lines to our legend.
If we want to add small rectangles representing the shaded area, we must create some patches first.
This is done by importing Patch
from matplotlib.patches
.
The created patches are then passed into the ax.legend
call.
from matplotlib.patches import Patchwith plt.style.context('cyberpunk'):
fig, ax = plt.subplots(figsize=(10,10), subplot_kw=dict(polar=True))
ax.plot(label_loc, well1, lw=2)
ax.plot(label_loc, well2, lw=2)
ax.fill(label_loc, well1, alpha=0.3)
ax.fill(label_loc, well2, alpha=0.3)
lines, labels = plt.thetagrids(np.degrees(label_loc), labels=lithologies)
ax.tick_params(axis='both', which='major', pad=30, labelsize=15)
ax.spines['polar'].set_linewidth(3)
edge_color = (1, 1, 1, 0.2)
ax.spines['polar'].set_color(edge_color)
ax.grid(color='white', alpha=0.3)
ax.set_ylim(0, 60)
# Create custom legend handles
well1_legend = Patch(facecolor='C0', alpha=0.5, label='Well 1')
well2_legend = Patch(facecolor='C1', alpha=0.5, label='Well 2')
# Add a legend with custom position and handles
ax.legend(handles=[well1_legend, well2_legend],
bbox_to_anchor=(1.3, 0.2), fontsize=20,
frameon=True)
plt.show()
And when we run the code we now have the finished plot.
Radar plots provide a nice data visualisation and can easily be created using matplotlib. With additional style from the Cyberpunk theme library, we can take our radar plot to the next level and make it stylish.
Why don’t you give radar plots a go on your next data visualisation project.