Improving Plotly’s Polar Bar Charts | by Ray Heberer | Towards Data Science

Polar Bar Charts can be great ways to display categorical information if you want to draw more attention to the categories with higher values. They were used to great effect in the Wall Street Journal’s article on Amazon’s HQ2 Shortlist. But even with the powerful visualization tools at our fingertips, producing charts like that takes a bit of legwork.
I’m here today to provide you with the pieces of geometrical drudgery needed to turn Plotly’s polar bar chart into something visually acceptable. By the end, you can throw these annoying bits of housekeeping code into a function and never think about it again.
This is the plot produced by Plotly’s tutorial code.
While I understand that they were demonstrating how to create the Matplotlib logo in fewer lines of code, the example is not useful for practical applications.
Below, I will be addressing the main issues I have with this example in a reproducible way. Those issues being:
The slices of the plot and of the background are different in number and not aligned.
The gaps between the plot slices are not uniform.
It’s not clear from the example how to make a legend.
As usual, you can skip to the end for the full code snippet.
Let’s start by framing our problem. We have a set of values, and we’d like to map each value to the radius of a circle slice. Perhaps we also have labels for each value.
vals = [3, 2, 5, 1, 2]labels = ["Strength", "Intelligence", "Dexterity", "Wisdom", "Stealth"]
From these values alone, we’d like to determine the positioning and alignment of both the slices and the background.
The radius, position, and width of each slice are determined by the r, theta, and width arguments of the Plotly barpolar function respectively. Note that theta corresponds to the angle of the center of each slice.
If we wish to have slices with no space between them, and of equal width, we can infer these parameters from the length of our original values.
num_slices = len(vals)theta = [(i + 1.5) * 360 / num_slices for i in range(num_slices)]width = [360 / num_slices for _ in range(num_slices)]
Here the “+ 1.5” will ensure that the right edge of our first slice will be at zero degrees.
Something else I like to do is produce a color sequence for my slices by taking steps through a palette (e.g. Pastel):
color_seq = px.colors.qualitative.Pastelcolor_indices = range(0, len(color_seq), len(color_seq) // num_slices)colors = [color_seq[i] for i in color_indices]
Let’s take a look at our progress:
fig = go.Figure(go.Barpolar(r=vals, theta=theta, width=width, marker_color=colors))fig.show()
It’s getting better! As you can see, the slices start from zero degrees and are evenly spaced. But the background is still completely unaligned.
Aligning the background is a matter of adjusting the polar_angularaxis_tickvals layout option of the figure. We’ll infer the sequence of angles similarly to the way we did with the slices.
angular_tickvals = [(i + 1) * 360 / num_slices for i in range(num_slices)]fig.update_layout(polar_angularaxis_tickvals=angular_tickvals)fig.show()
Good, now the background is aligned with the slices. There are many more options you can adjust with update_layout that will make the plot more aesthetic. I’ll leave you with a few of them at the end.
Now, how to add a legend that associates a label with each slice? If you just use the name argument, you run into an issue: Plotly treats all of our slices as a single trace, which can only be associated with a single name.
How might we associate each individual slice with its own name?
This isn’t ideal, but at the moment I’ve found that making a separate barpolar object for each slice is the best way to do this. I’m hopeful that there exists a better way, or at least that one will be developed soon.
barpolar_plots = [go.Barpolar(r=[r], theta=[t], width=[w], name=n, marker_color=[c])for r, t, w, n, c in zip(vals, theta, width, labels, colors)]fig = go.Figure(barpolar_plots)angular_tickvals = [(i + 1) * 360 / num_slices for i in range(num_slices)]fig.update_layout(polar_angularaxis_tickvals=angular_tickvals)fig.show()
Since we inferred all the angle parameters just from the raw values, it makes sense to wrap all of that effort into a function. I’ve demonstrated its usage with a few extra layout options. You can find more at the Plotly Figure Reference.
At the moment, it takes a little work to get from Plotly’s tutorial polar bar chart to something usable. In particular, some angle munging is required to align the slices and the background. In addition, adding a legend is not as straightforward as it could be.
Here, I’ve gone through the steps required to make such adjustments, and packaged them all up in a reusable function. I hope you find it helpful.