问题
I am trying to align labels of my radar plot with axis and I face difficulties.
Without label alignment, my labels are centered and exceed the axis line .
When I set for the first half of my labels (the right side of the figure) alignment to left and for the second half the alignment to the right I get a non "circular" alignment
To do so I looped within xticklabels and set horizontal alignment.
# Draw one axe per variable + add labels labels yet
plt.xticks(angles, categories)
for label,i in zip(ax.get_xticklabels(),range(0,len(angles))):
if i<len(angles)/2:
angle_text=angles[i]*(-180/pi)+90
#label.set_horizontalalignment('left')
else:
angle_text=angles[i]*(-180/pi)-90
#label.set_horizontalalignment('right')
label.set_rotation(angle_text)
I guess there is a proper way to do this and I can't figure it out, as I don't know if it should consider an offset, a translation or how to adapt the polar coordinate to do so.
Thanks for your help
Paul
Here follows the full code for more information
from math import pi
import matplotlib.pyplot as plt
%matplotlib inline
import pandas as pd
## Generate Data
labels_test=[]
for i in range(0,40):
labels_test.append("Fooooooooo"+str(i))
pd_radar=pd.DataFrame(data=np.random.randint(low=0, high=10, size=(2, 40)),columns=labels_test)
# number of variable
categories=list(pd_radar)
N = len(categories)
# What will be the angle of each axis in the plot? (we divide the plot / number of variable)
angles = [n / float(N) * 2 * pi for n in range(N)]
# Initialise the spider plot
fig=plt.figure(figsize=(20,10))
ax = plt.subplot(111, polar=True)
# If you want the first axis to be on top:
ax.set_theta_offset(pi / 2)
ax.set_theta_direction(-1)
# Draw one axe per variable + add labels labels yet
plt.xticks(angles, categories)
for label,i in zip(ax.get_xticklabels(),range(0,len(angles))):
if i<len(angles)/2:
angle_text=angles[i]*(-180/pi)+90
label.set_horizontalalignment('left')
else:
angle_text=angles[i]*(-180/pi)-90
label.set_horizontalalignment('right')
label.set_rotation(angle_text)
# Draw ylabels
ax.set_rlabel_position(0)
# ------- PART 2: Add plots
# Plot each line of the data
# Ind1
values0=pd_radar.iloc[0].values.flatten().tolist()
ax.plot(angles, values0, linewidth=1, linestyle='solid', label="Label 1",color='yellow')
ax.fill(angles, values0, 'r', alpha=0.1,color='yellow')
# Ind2
values1=pd_radar.iloc[1].values.flatten().tolist()
ax.plot(angles, values1, linewidth=1, linestyle='solid', label="Label 2",color='deepskyblue')
ax.fill(angles, values1, 'r',color='deepskyblue', alpha=0.1)
# Add legend
plt.show()
回答1:
The code currently does not run with matplotlib 2.2. The following solution works however with version 2.0.2 that the OP is using. For a workaround using newer versions, see this question.
Texts in matplotlib have a rotation mode.
The rotation mode can be "default"
, in which case the text is first rotated and then aligned
or it can be "anchor"
, in which case the text is first aligned and then rotated.
Therefore, in order to have the ticklabels pointing outwards on the polar plot, you would want to set the alignment of the text to "left"
, but then set the rotation mode to "anchor"
.
for label,rot in zip(ax.get_xticklabels(),ticks):
label.set_rotation(rot*180./np.pi)
label.set_horizontalalignment("left")
label.set_rotation_mode("anchor")
Full example:
import numpy as np
import matplotlib.pyplot as plt
fig=plt.figure(figsize=(5,5))
ax = plt.subplot(111, polar=True)
ticks = np.linspace(0, 2*np.pi, 20, endpoint=False)
text = lambda : "".join(np.random.choice(list("manuladefil"), size=10))
labels = [text() for _ in range(len(ticks))]
plt.xticks(ticks, labels, size=16)
for label,rot in zip(ax.get_xticklabels(),ticks):
label.set_rotation(rot*180./np.pi)
label.set_horizontalalignment("left")
label.set_rotation_mode("anchor")
plt.tight_layout()
plt.show()
回答2:
I found a solution that doesn't seems elegant. Main idea was to set both vertical and horizontal alignment as a function of the angle.
from math import pi
import matplotlib.pyplot as plt
%matplotlib inline
import pandas as pd
import numpy as np
## Generate Data
labels_test=[]
for i in range(0,40):
labels_test.append("Fooooooooo"+str(i))
pd_radar=pd.DataFrame(data=np.random.randint(low=0, high=10, size=(2, 40)),columns=labels_test)
# number of variable
categories=list(pd_radar)
N = len(categories)
# What will be the angle of each axis in the plot? (we divide the plot / number of variable)
angles = [n / float(N) * 2 * pi for n in range(N)]
# Initialise the spider plot
fig=plt.figure(figsize=(20,10))
ax = plt.subplot(111, polar=True)
# If you want the first axis to be on top:
ax.set_theta_offset(pi / 2)
ax.set_theta_direction(-1)
# Draw one axe per variable + add labels labels yet
# idea is to add both vertical and horizontal alignment as a function of pi
plt.xticks(angles, categories)
for label,i in zip(ax.get_xticklabels(),range(0,len(angles))):
angle_rad=angles[i]
if angle_rad <= pi/2:
ha= 'left'
va= "bottom"
angle_text=angle_rad*(-180/pi)+90
elif pi/2 < angle_rad <= pi:
ha= 'left'
va= "top"
angle_text=angle_rad*(-180/pi)+90
elif pi < angle_rad <= (3*pi/2):
ha= 'right'
va= "top"
angle_text=angle_rad*(-180/pi)-90
else:
ha= 'right'
va= "bottom"
angle_text=angle_rad*(-180/pi)-90
label.set_rotation(angle_text)
label.set_verticalalignment(va)
label.set_horizontalalignment(ha)
# Draw ylabels
ax.set_rlabel_position(0)
# ------- PART 2: Add plots
# Plot each line of the data
# Ind1
values2=pd_radar.iloc[0].values.flatten().tolist()
ax.plot(angles, values2, linewidth=1, linestyle='solid', label="Label 1",color='yellow')
ax.fill(angles, values2, 'r', alpha=0.1,color='yellow')
# Ind2
values3=pd_radar.iloc[1].values.flatten().tolist()
ax.plot(angles, values3, linewidth=1, linestyle='solid', label="Label 2",color='deepskyblue')
ax.fill(angles, values3, 'r',color='deepskyblue', alpha=0.1)
# Add legend
plt.show()
来源:https://stackoverflow.com/questions/49488018/radar-plot-matplotlib-python-how-to-set-label-alignment