Whilst plotting my complex radar chart, I am getting the error "radial grids must be strictly positive", but all of the values and ranges I have used in my radar chart are all positive. Can someone tell me why I am getting this error, and what I can do to fix it? The code I have used is below:-
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns # improves plot aesthetics
def _invert(x, limits):
"""inverts a value x on a scale from
limits[0] to limits[1]"""
return limits[1] - (x - limits[0])
def _scale_data(data, ranges):
"""scales data[1:] to ranges[0],
inverts if the scale is reversed"""
for d, (y1, y2) in zip(data[1:], ranges[1:]):
assert (y1 <= d <= y2) or (y2 <= d <= y1)
x1, x2 = ranges[0]
d = data[0]
if x1 > x2:
d = _invert(d, (x1, x2))
x1, x2 = x2, x1
sdata = [d]
for d, (y1, y2) in zip(data[1:], ranges[1:]):
if y1 > y2:
d = _invert(d, (y1, y2))
y1, y2 = y2, y1
sdata.append((d-y1) / (y2-y1)
* (x2 - x1) + x1)
return sdata
class ComplexRadar():
def __init__(self, fig, variables, ranges,
n_ordinate_levels=6):
angles = np.arange(0, 360, 360./len(variables))
axes = [fig.add_axes([0.1,0.1,0.8,0.8],polar=True,
label = "axes{}".format(i))
for i in range(len(variables))]
l, text = axes[0].set_thetagrids(angles,
labels=variables)
[txt.set_rotation(angle-90) for txt, angle
in zip(text, angles)]
for ax in axes[1:]:
ax.patch.set_visible(False)
ax.grid("off")
ax.xaxis.set_visible(False)
for i, ax in enumerate(axes):
grid = np.linspace(*ranges[i],
num=n_ordinate_levels)
gridlabel = ["{}".format(round(x,2))
for x in grid]
if ranges[i][0] > ranges[i][1]:
grid = grid[::-1] # hack to invert grid
# gridlabels aren't reversed
gridlabel[0] = "" # clean up origin
ax.set_rgrids(grid, labels=gridlabel,
angle=angles[i])
#ax.spines["polar"].set_visible(False)
ax.set_ylim(*ranges[i])
# variables for plotting
self.angle = np.deg2rad(np.r_[angles, angles[0]])
self.ranges = ranges
self.ax = axes[0]
def plot(self, data, *args, **kw):
sdata = _scale_data(data, self.ranges)
self.ax.plot(self.angle, np.r_[sdata, sdata[0]], *args, **kw)
def fill(self, data, *args, **kw):
sdata = _scale_data(data, self.ranges)
self.ax.fill(self.angle, np.r_[sdata, sdata[0]], *args, **kw)
# example data
variables = ("Attribute1", "Attribute2", "Attribute3",
"Attribute4", "Attribute5", "Attribute6", "Attribute7",
"Attribute8", "Attribute9", "Attribute10", "Attribute11",
"Attribute12", "Attribute13", "Attribute14", "Attribute15",
"Attribute16", "Attribute17")
data = (1.0, 185, 0.02,
1.5, 0.1, 1.3, 1.1,
2.3, 0.1, 6.6, 24.5,
83, 7.5, 5.6, 4.6,
6.5, 1.3)
data2 = (1.0, 177, 0.0,
0.0, 0.7, 1.3, 1.3,
4.3, 0.0, 4.7, 30,
93, 3.7, 5.0, 7.7,
8.0, 3.5)
ranges = [(10.0, 0.0), (170.0, 189.0), (0.0, 0.3),
(0.6, 5.2), (0.1, 2.0), (0.3, 2.2), (1.4, 0.3),
(0.9, 3.0), (0.0, 0.7), (2.6, 11.4), (14.4, 43.5),
(72.0, 94.0), (1.4, 7.9), (2.5, 6.5), (2.4, 6.2),
(4.0, 7.9), (1.1, 3.9)]
# plotting
fig1 = plt.figure(figsize=(9, 9))
radar = ComplexRadar(fig1, variables, ranges)
radar.plot(data, color="deepskyblue", label= "Compare1")
radar.fill(data, color="deepskyblue", alpha=0.5)
radar = ComplexRadar(fig1, variables, ranges)
radar.plot(data2, color="orangered", label= "Compare2")
radar.fill(data2, color="orangered", alpha=0.5)
radar.ax.legend(loc='upper center', bbox_to_anchor=(0.9, 1.10),
fancybox=False, shadow=False, ncol=48)
plt.show()
Hi this is great time to learn some debugging skills. The first thing you'll want to do when confronted with an error like this is localize it in your code. Fortunately python does this for you with the traceback. These tracebacks can at times be super intimidating but on closer inspection start to make a little more sense and can provide very valuable information. So lets start with the traceback for the error.
The first two sections of the traceback are:
ValueError Traceback (most recent call last)
<ipython-input-454-63a43ee81b1a> in <module>()
91 # plotting
92 fig1 = plt.figure(figsize=(9, 9))
---> 93 radar = ComplexRadar(fig1, variables, ranges)
94 radar.plot(data, color="deepskyblue", label= "Compare1")
95 radar.fill(data, color="deepskyblue", alpha=0.5)
<ipython-input-454-63a43ee81b1a> in __init__(self, fig, variables, ranges, n_ordinate_levels)
54 gridlabel[0] = "" # clean up origin
55 ax.set_rgrids(grid, labels=gridlabel,
---> 56 angle=angles[i])
57 #ax.spines["polar"].set_visible(False)
58 ax.set_ylim(*ranges[i])
So we can see that the issue is coming from ax.set_rgrids
in the ComplexRadar __init__
method. So we know its going to be a problem with the arguments we are passing that, either grid
or angles
. To get more of an idea lets look at the final section of the error message:
/home/ianhi/anaconda/lib/python2.7/site-packages/matplotlib/projections/polar.pyc in set_rgrids(self, radii, labels, angle, fmt, **kwargs)
565 rmin = radii.min()
566 if rmin <= 0:
--> 567 raise ValueError('radial grids must be strictly positive')
568
569 self.set_yticks(radii)
ValueError: radial grids must be strictly positive
if rmin <= 0:
So there is where we get "strictly positive" from. While none of the radial values you are passing are negative some of them may not be greater than 0. For short code like this an easy way to investigate is to simply add a print statement before the offending line.
print(grid)
ax.set_rgrids(grid, labels=gridlabel,
angle=angles[i])
Doing this I find that grid = [ 0. 2. 4. 6. 8. 10.]
. AhHah! This contains a 0. You'll have to choose how you where you want to start grid from.
For testing purpose I used the quick and dirty fix of grid[0]=.01
only to find that the second radar.plot command also throws an error!
This gives us a rather mysterious blank AssertionError
:
AssertionError Traceback (most recent call last)
<ipython-input-458-2a30b515f0d9> in <module>()
96 radar.fill(data, color="deepskyblue", alpha=0.5)
97 radar = ComplexRadar(fig1, variables, ranges)
---> 98 radar.plot(data2, color="orangered", label= "Compare2")
99 radar.fill(data2, color="orangered", alpha=0.5)
100 radar.ax.legend(loc='upper center', bbox_to_anchor=(0.9, 1.10),
<ipython-input-458-2a30b515f0d9> in plot(self, data, *args, **kw)
63 self.ax = axes[0]
64 def plot(self, data, *args, **kw):
---> 65 sdata = _scale_data(data, self.ranges)
66 self.ax.plot(self.angle, np.r_[sdata, sdata[0]], *args, **kw)
67 def fill(self, data, *args, **kw):
<ipython-input-458-2a30b515f0d9> in _scale_data(data, ranges)
13 inverts if the scale is reversed"""
14 for d, (y1, y2) in zip(data[1:], ranges[1:]):
---> 15 assert (y1 <= d <= y2) or (y2 <= d <= y1)
16 x1, x2 = ranges[0]
17 d = data[0]
AssertionError:
The reason there is no helpful message describing the problem is that it is your method _scale_data
that is actually raising this error. This assert statement is telling us something about the value of d relative to y2 and y1. Rather, its actually telling us that some thing we think about d is untrue. To figure out what is going on lets surround the assert statement in a try except block:
try:
assert (y1 <= d <= y2) or (y2 <= d <= y1)
except AssertionError:
print(d,y1,y2)
Leaving us with several examples where d satisfies neither of the or conditions:
d y1 y2
(0.0, 0.6, 5.2)
(4.3, 0.9, 3.0)
(7.7, 2.4, 6.2)
(8.0, 4.0, 7.9)
(0.0, 0.6, 5.2)
(4.3, 0.9, 3.0)
(7.7, 2.4, 6.2)
(8.0, 4.0, 7.9)
This try except statement along with grid[0]=.001 right before ax.set_rgrids command allows your code to run giving the below plot. However, you should look into changing your data set to avoid the grid[0]=0 issue and possibly remove the assert statement or else correct the data if it fails. Hope this helps and you have an easier time debugging next time because of this.