I'd like to make a plot in Python and have x range display ticks in multiples of pi.
Is there a good way to do this, not manually?
I'm thinking of using matplotlib, but other options are fine.
EDIT 3: EL_DON's solution worked for me like this:
import matplotlib.ticker as tck
import matplotlib.pyplot as plt
import numpy as np
f,ax=plt.subplots(figsize=(20,10))
x=np.linspace(-10*np.pi, 10*np.pi,1000)
y=np.sin(x)
ax.plot(x/np.pi,y)
ax.xaxis.set_major_formatter(tck.FormatStrFormatter('%g $\pi$'))
ax.xaxis.set_major_locator(tck.MultipleLocator(base=1.0))
plt.style.use("ggplot")
plt.show()
giving:
EDIT 2 (solved in EDIT 3!): EL_DON's answer doesn't seem to work right for me:
import matplotlib.ticker as tck
import matplotlib.pyplot as plt
import numpy as np
f,ax=plt.subplots(figsize=(20,10))
x=np.linspace(-10*np.pi, 10*np.pi)
y=np.sin(x)
ax.plot(x/np.pi,y)
ax.xaxis.set_major_formatter(tck.FormatStrFormatter('%g $\pi$'))
ax.xaxis.set_major_locator(tck.MultipleLocator(base=1.0))
plt.style.use("ggplot")
plt.show()
gives me
which really doesn't look right
This is inspired by Python Data Science Handbook, although Sage attempts to do without explicit parameters.
EDIT: I've generalized this to allow you to supply as optional parameters the denominator, the value of the unit, and the LaTeX label for the unit. A class definition is included if you find that helpful.
import numpy as np
import matplotlib.pyplot as plt
def multiple_formatter(denominator=2, number=np.pi, latex='\pi'):
def gcd(a, b):
while b:
a, b = b, a%b
return a
def _multiple_formatter(x, pos):
den = denominator
num = np.int(np.rint(den*x/number))
com = gcd(num,den)
(num,den) = (int(num/com),int(den/com))
if den==1:
if num==0:
return r'$0$'
if num==1:
return r'$%s$'%latex
elif num==-1:
return r'$-%s$'%latex
else:
return r'$%s%s$'%(num,latex)
else:
if num==1:
return r'$\frac{%s}{%s}$'%(latex,den)
elif num==-1:
return r'$\frac{-%s}{%s}$'%(latex,den)
else:
return r'$\frac{%s%s}{%s}$'%(num,latex,den)
return _multiple_formatter
class Multiple:
def __init__(self, denominator=2, number=np.pi, latex='\pi'):
self.denominator = denominator
self.number = number
self.latex = latex
def locator(self):
return plt.MultipleLocator(self.number / self.denominator)
def formatter(self):
return plt.FuncFormatter(multiple_formatter(self.denominator, self.number, self.latex))
This can be used very simply, without any parameters:
x = np.linspace(-np.pi, 3*np.pi,500)
plt.plot(x, np.cos(x))
plt.title(r'Multiples of $\pi$')
ax = plt.gca()
ax.grid(True)
ax.set_aspect(1.0)
ax.axhline(0, color='black', lw=2)
ax.axvline(0, color='black', lw=2)
ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2))
ax.xaxis.set_minor_locator(plt.MultipleLocator(np.pi / 12))
ax.xaxis.set_major_formatter(plt.FuncFormatter(multiple_formatter()))
plt.show()
Or it can be used in a more sophisticated way:
tau = np.pi*2
den = 60
major = Multiple(den, tau, r'\tau')
minor = Multiple(den*4, tau, r'\tau')
x = np.linspace(-tau/60, tau*8/60,500)
plt.plot(x, np.exp(-x)*np.cos(60*x))
plt.title(r'Multiples of $\tau$')
ax = plt.gca()
ax.grid(True)
ax.axhline(0, color='black', lw=2)
ax.axvline(0, color='black', lw=2)
ax.xaxis.set_major_locator(major.locator())
ax.xaxis.set_minor_locator(minor.locator())
ax.xaxis.set_major_formatter(major.formatter())
plt.show()