Search code examples
pythonmatplotlibalignmentradar-chart

Radar Plot Matplotlib Python : how to set label alignment


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 enter image description here.

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 enter image description here

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()

Solution

  • 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

    enter image description here

    or it can be "anchor", in which case the text is first aligned and then rotated.

    enter image description here

    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()
    

    enter image description here