TheStumbler
TheStumbler

Reputation: 99

Matplotlib: Making Polar Plot with Logarithmic Angular Axis

I want to draw some circular axes to plot the artwork for an old-fashioned circular slide rule. In almost every example, polar plots have an angular axis of degrees or radians. I found one example of changing the labels to be like a clock (1 - 12), but I couldn’t see how to extend that technique to logarithmic scales.

I can draw the desired axis in a linear manner, as I see examples of making a logarithmic radial axis. But I can’t find a way to apply log scales to the angular theta axis.

A somewhat related issue, how can you use matplotlib to draw a single axis (e.g., no data, axis only)? This could be helpful for making nomographs, for example. In my use case, if I can’t plot the circular log scale directly, I could perhaps post-process a linear log scale into a circle — maybe.

Here is an old circular slide rule as an example. I’d like to make scales such as C or D (one complete log cycle in 360 degrees), A (two cycles) and K (three cycles).

Upvotes: 1

Views: 477

Answers (2)

TheStumbler
TheStumbler

Reputation: 99

Based on @ljbkusters answer, I crafted a single-cycle, circular log-base-10 scale. This will serve as a basis for more detailed plots for the final slide rule artwork.

import numpy as np
import matplotlib.pyplot as plt     

ax = plt.subplot(111, polar=True)

# Make the labels go clockwise
ax.set_theta_direction(-1)
# Place 0 at the top
ax.set_theta_offset(np.pi/2.0)       

# Polar plots don't have tick marks, 
# cannot draw outside the max radius set by ylim
# Remove the frame, and y-axis,
# * draw our own axis circle
# * draw our own tick marks (below)
ax.set_ylim(0,100)
ax.grid(False)
ax.set_frame_on(False)
ax.axes.get_yaxis().set_visible(False)
angles = np.linspace(0.0, 2.0*np.pi, 100)
ax.plot( angles, 100*[95], color='black', lw=1.2 )

# Major
lim_min = 1 
lim_max = 10 
N_LABLES = 10
XTICKS = [2.0 * np.pi * np.log10(x) for x in np.linspace(lim_min, lim_max, N_LABLES-1, endpoint=False)]
ax.set_xticks(XTICKS)
# Set the circumference labels
ax.set_xticklabels(range(1, N_LABLES))
# Draw major tick marks
tick = [93,100]
for t in XTICKS:
    ax.plot([t,t], tick, lw=1.0, color='black')

# Minor
lim_min = 1 
lim_max = 10 
N_LABLES = 100
XTICKS = [2.0 * np.pi * np.log10(x) for x in np.linspace(lim_min, lim_max, N_LABLES-1, endpoint=False)]
ax.set_xticks(XTICKS)
# Draw minor tick marks
tick = [95,98]
for t in XTICKS:
    ax.plot([t,t], tick, lw=0.5, color='blue')

plt.show()

Upvotes: 2

ljbkusters
ljbkusters

Reputation: 197

Try this

import numpy as np
import matplotlib.pyplot as plt     

ax = plt.subplot(111, polar=True)


lim_min = 1             # ln(1) = 0
lim_max = np.e**(2*np.pi)  # ln(e**(2*pi)) = 2*pi (np.log = ln)
N_LABLES = 24
XTICKS = [np.log(x) for x in np.linspace(lim_min, lim_max, N_LABLES, endpoint=False)]

# Set the circumference labels
ax.set_xticks(XTICKS)
ax.set_xticklabels(range(N_LABLES))

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

You can play with the exact scaling from 0 to 2pi to get the plot the way you want it. Figure produced: enter image description here

Upvotes: 3

Related Questions