Skip to content

Instantly share code, notes, and snippets.

@kylerbrown
Last active May 2, 2024 12:07
Show Gist options
  • Save kylerbrown/29ce940165b22b8f25f4 to your computer and use it in GitHub Desktop.
Save kylerbrown/29ce940165b22b8f25f4 to your computer and use it in GitHub Desktop.
Code for a complex radar plot
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.9,0.9],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)
if __name__ == "__main__":
# example data
variables = ("Normal Scale", "Inverted Scale", "Inverted 2",
"Normal Scale 2", "Normal 3", "Normal 4 %", "Inverted 3 %")
data = (1.76, 1.1, 1.2,
4.4, 3.4, 86.8, 20)
ranges = [(0.1, 2.3), (1.5, 0.3), (1.3, 0.5),
(1.7, 4.5), (1.5, 3.7), (70, 87), (100, 10)]
# plotting
fig1 = plt.figure(figsize=(6, 6))
radar = ComplexRadar(fig1, variables, ranges)
radar.plot(data)
radar.fill(data, alpha=0.2)
plt.show()
@jsoutherland
Copy link

It looks like recent versions of matplotlib broke support for L39/40:

[txt.set_rotation(angle-90) for txt, angle in zip(text, angles)]

The labels are not rotating for me either.

@amarruedo797
Copy link

amarruedo797 commented Mar 1, 2021

Hi!
I have the same problem as @OD1995 , did you find a solution? The code works with non inverted ranges, but it gets stuck when inverting them.
Thanks

@jsoutherland
Copy link

@amarruedo797 I didn't find a solution yet, I just moved the labels outwards a bit so they didn't overlap for now:

        for txt, angle in zip(text, angles):
            # txt.set_rotation(angle-90) # TODO: doesn't work
            txt.set_position((-0.1,-0.1)) # move labels outward

@amarruedo797
Copy link

@amarruedo797 I didn't find a solution yet, I just moved the labels outwards a bit so they didn't overlap for now:

        for txt, angle in zip(text, angles):
            # txt.set_rotation(angle-90) # TODO: doesn't work
            txt.set_position((-0.1,-0.1)) # move labels outward

Hi! thanks for your answer,
in my case I can only get a graph when the ranges are not inversed, for example: (0,1), when I have a range : (1,0) the code does not work. I think it migth be an issue with the python and matplotlib versions? @kylerbrown, do you know if this could be the problem? Thanks!

@w-t-effi
Copy link

w-t-effi commented Nov 4, 2021

@amarruedo797 I had the same problem and for me, removing the lines 50 and 51 did the trick. I'm not 100% sure why that is the case, but I'm guessing newer matplotlib versions recognize when the ticks are reversed and do it automatically for the grid as well, so we're rereversing them with that line.

@sophont01
Copy link

@w-t-effi Thanks ! That solved my issue of not getting the axes inverted .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment