问题
I want to create a radarchart with legends for every plot that is created but until now it has been impossible. Then, you can see the code used to create the plot (thanks to another user that developed it).
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns # improves plot aesthetics
import pandas as pd
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)
Every plot represents to different people. I want to add a legend showing the name of everyone and the color that represents him in the plot. I tried to add labels and then the legends but it didn´t work. The plot produced is something like that:
df = pd.DataFrame({
"Spe": pd.Series([89, 83, 70, 60, 30, 49, 28]),
"Str": pd.Series([69, 53, 30, 20, 10, 29, 48]),
"Det": pd.Series([82, 44, 79, 39, 20, 10, 85]),
"Extr": pd.Series([59, 74, 29, 36, 18, 29, 18]),
"Int": pd.Series([63, 11, 20, 36, 97, 58, 91]),
"Est": pd.Series([12, 69, 89, 59, 19, 58, 98]),
"Ape": pd.Series([29, 13, 94, 30, 20, 10, 67]),
"ID": pd.Series(["Carl","Michael","Peter","Louis","Sarah",
"Laura","Nicholas"])
})
dfo = df.drop('ID', 1)
print(dfo)
variables = ("1", "2", "3",
"4", "5", "6", "7")
ranges = [(1,100),(1,100),(1,100),(1,100),(1,100),(1,100),(1,100)]
fig1 = plt.figure(figsize=(8, 8))
radar = ComplexRadar(fig1, variables, ranges)
for i in range(len(dfo.index)-1):
data=dfo.iloc[i,:]
radar.plot(data)
radar.fill(data,alpha=0.2)
sns.plt.show()
Link to plot produced
Thanks for your help.
回答1:
In case someone is interested, the code of the ComplexRadar
comes from here.
The ComplexRadar
class uses many axes and makes all of them invisible, so the best way to create a legend here, would be to create a new axes to put the legend in. For that we need the handles of the plotted lines, so we need to change the ComplexRadar.plot()
method to return the line. We then need to collect those lines in a list (call it lax
) and supply them to the new legend.
I did make some changes to the code to make it work in python 2.7 and also to make it more comprehensible to myself. Here is, what should work:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
def _scale_data(data, ranges):
"""scales data[1:] to ranges[0],
"""
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]
sdata = [d]
for d, (y1, y2) in zip(data[1:], ranges[1:]):
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)
l = self.ax.plot(self.angle, np.r_[sdata, sdata[0]], *args, **kw)
return l
def fill(self, data, *args, **kw):
sdata = _scale_data(data, self.ranges)
self.ax.fill(self.angle, np.r_[sdata, sdata[0]], *args, **kw)
index = ["Carl","Michael","Peter","Louis","Sarah", "Laura","Nicholas"]
df = pd.DataFrame({
"Spe": pd.Series([89, 83, 70, 60, 30, 49, 28]),
"Str": pd.Series([69, 53, 30, 20, 10, 29, 48]),
"Det": pd.Series([82, 44, 79, 39, 20, 10, 85]),
"Extr": pd.Series([59, 74, 29, 36, 18, 29, 18]),
"Int": pd.Series([63, 11, 20, 36, 97, 58, 91]),
"Est": pd.Series([12, 69, 89, 59, 19, 58, 98]),
"Ape": pd.Series([29, 13, 94, 30, 20, 10, 67]),
})
variables = [k[0] for k in df.iteritems()]
ranges = [(1.,100.),(1.,100.),(1.,100.),(1.,100.),(1.,100.),(1.,100.),(1.,100.)]
fig1 = plt.figure(figsize=(8, 8))
radar = ComplexRadar(fig1, variables, ranges)
lax = []
for i, name in enumerate(index):
data=df.iloc[i].values
l, = radar.plot(data, label=name)
lax.append(l)
radar.fill(data,alpha=0.2)
legendax = fig1.add_axes([0.8,0.8,0.1,.1])
legendax.legend(handles = lax, labels=index, loc=3, bbox_to_anchor=(0,0,1,1), bbox_transform=fig1.transFigure )
legendax.axis('off')
sns.plt.show()
producing the following plot
来源:https://stackoverflow.com/questions/40208035/adding-legend-to-a-radarchart-in-python