Search code examples
pythonmatplotlibplotpolar-coordinates

Python polar clock-like plot with matplotlib


I am trying to plot data in a clock-wise fashion using matplotlib in Python in the style of this answer. I noticed weird behaviour when plotting my data; the data points had the correct y value, but would not appear at the correct x values, i.e. times. I first thought that my data was erroneous, but upon recreating my problem with the following working example I came to the conclusion that the mistake must be somewhere else.

import numpy as np
import matplotlib.pyplot as plt     

ax = plt.subplot(111, polar=True)
equals = np.linspace(0, 360, 24, endpoint=False) #np.arange(24)
ones = np.ones(24)
ax.scatter(equals, ones)       

# Set the circumference labels
ax.set_xticks(np.linspace(0, 2*np.pi, 24, endpoint=False))
ax.set_xticklabels(range(24))      

# Make the labels go clockwise
ax.set_theta_direction(-1)       

# Place 0 at the top
ax.set_theta_offset(np.pi/2.0)       

plt.show()

This results in the following plot: enter image description here

I would have expected that the x values of the points line up with the hours, considering the definition of equals. It is currently defined as an angle, but I also tried defining it as an hour. Why is this not the case and how can I get my data to line up with the corresponding time?


Solution

  • Matplotlib expects angles to be in units of radians and not degrees (see the open bug report). You can use the numpy function np.deg2rad to convert to radians:

    import numpy as np
    import matplotlib.pyplot as plt     
    
    ax = plt.subplot(111, polar=True)
    equals = np.linspace(0, 360, 24, endpoint=False) #np.arange(24)
    ones = np.ones(24)
    ax.scatter(np.deg2rad(equals), ones)       
    
    # Set the circumference labels
    ax.set_xticks(np.linspace(0, 2*np.pi, 24, endpoint=False))
    ax.set_xticklabels(range(24))      
    
    # Make the labels go clockwise
    ax.set_theta_direction(-1)       
    
    # Place 0 at the top
    ax.set_theta_offset(np.pi/2.0)       
    
    plt.show()
    

    This produces the following picture:

    enter image description here

    Alternatively, you could have changed your definition of equals to produce angles in terms of radians: equals = np.linspace(0, 2*np.pi, 24, endpoint=False)