How to set axis ticks in multiples of pi (Python) (matplotlib) - python

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

f,ax=plt.subplots(1)
x=linspace(0,3*pi,1001)
y=sin(x)
ax.plot(x/pi,y)
ax.xaxis.set_major_formatter(FormatStrFormatter('%g $\pi$'))
ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=1.0))
I used info from these answers:
https://stackoverflow.com/a/19972993/6605826
https://stackoverflow.com/a/29188910/6605826

If you want to avoid dividing x by pi in the plot command, this answer can be adjusted slightly using a FuncFormatter instead of a FormatStrFormatter:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.ticker import FuncFormatter, MultipleLocator
fig,ax = plt.subplots()
x = np.linspace(-5*np.pi,5*np.pi,100)
y = np.sin(x)/x
ax.plot(x,y)
#ax.xaxis.set_major_formatter(FormatStrFormatter('%g $\pi$'))
ax.xaxis.set_major_formatter(FuncFormatter(
lambda val,pos: '{:.0g}$\pi$'.format(val/np.pi) if val !=0 else '0'
))
ax.xaxis.set_major_locator(MultipleLocator(base=np.pi))
plt.show()
gives the following image:

Solution for pi fractions:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
rc('text', usetex=True) # Use LaTeX font
import seaborn as sns
sns.set(color_codes=True)
Plot your function:
fig, ax = plt.subplots(1)
x = np.linspace(0, 2*np.pi, 1001)
y = np.cos(x)
ax.plot(x, y)
plt.xlim(0, 2*np.pi)
Modify the range of the grid so that it corresponds to the pi values:
ax.set_xticks(np.arange(0, 2*np.pi+0.01, np.pi/4))
Change axis labels:
labels = ['$0$', r'$\pi/4$', r'$\pi/2$', r'$3\pi/4$', r'$\pi$',
r'$5\pi/4$', r'$3\pi/2$', r'$7\pi/4$', r'$2\pi$']
ax.set_xticklabels(labels)

import numpy as np
import matplotlib.pyplot as plt
x=np.linspace(0,3*np.pi,1001)
plt.ylim(-3,3)
plt.xlim(0, 4*np.pi)
plt.plot(x, np.sin(x))
tick_pos= [0, np.pi , 2*np.pi]
labels = ['0', '$\pi$', '$2\pi$']
plt.xticks(tick_pos, labels)

I created a PyPi Package that creates formatter and locator instances like Scott Centoni's answer.
"""Show a simple example of using MultiplePi."""
import matplotlib.pyplot as plt
import numpy as np
from matplot_fmt_pi import MultiplePi
fig = plt.figure(figsize=(4*np.pi, 2.4))
axes = fig.add_subplot(111)
x = np.linspace(-2*np.pi, 2*np.pi, 512)
axes.plot(x, np.sin(x))
axes.grid(True)
axes.axhline(0, color='black', lw=2)
axes.axvline(0, color='black', lw=2)
axes.set_title("MultiplePi formatting")
pi_manager = MultiplePi(2)
axes.xaxis.set_major_locator(pi_manager.locator())
axes.xaxis.set_major_formatter(pi_manager.formatter())
plt.tight_layout()
plt.savefig("./pi_graph.png", dpi=120)

Here is a version converting floats into fractions of pi. Just use your favorite formatter, then convert the float values it produced into pi fractions using function convert_to_pi_fractions(ax, axis='x'), specifying which spine must be converted (or both). You get that:
from that:
from fractions import Fraction
import numpy as np
from numpy import pi
import matplotlib.pyplot as plt
import matplotlib.ticker as tck
def convert_to_pi_fractions(ax, axis='x'):
assert axis in ('x', 'y', 'both')
if axis in ('x', 'both'):
vals, labels = process_ticks(ax.get_xticks())
if len(vals) > 0: ax.set_xticks(vals, labels)
if axis in ('y', 'both'):
vals, labels = process_ticks(ax.get_yticks())
if len(vals) > 0: ax.set_yticks(vals, labels)
def process_ticks(ticks):
vals = []
labels = []
for tick in ticks:
frac = Fraction(tick/pi)
if frac.numerator < 10 and frac.numerator < 10:
if frac.numerator == 0: label = '0'
elif frac.denominator == 1:
if frac.numerator == 1: label = '$\pi$'
elif frac.numerator == -1: label = '-$\pi$'
else: label = f'{frac.numerator} $\pi$'
elif frac.numerator == -1: label = f'-$\pi$/{frac.denominator}'
elif frac.numerator == 1: label = f'$\pi$/{frac.denominator}'
else: label = f'{frac.numerator}$\pi$/{frac.denominator}'
vals.append(tick)
labels.append(label)
return vals, labels
# Generate data
w_fr = np.linspace(-0.5*pi, 3.1*pi, 60)
H_func = lambda h, w: np.sum(h * np.exp(-1j * w[:, None] * np.arange(len(h))), axis=1)
r_fr = H_func([1, -1], w_fr)
# Prepare figure
fig, ax = plt.subplots(figsize=(10, 4), layout='constrained')
ax.grid()
ax.set_title('Frequency response')
ax.set_xlabel('normalized radian frequency')
ax.xaxis.set_major_locator(tck.MultipleLocator(base=pi/2))
g_c, p_c = 'C0', 'C1'
# Plot gain
ax.set_ylabel('amplitude', c=g_c)
ax.plot(w_fr, abs(r_fr), label='gain', c=g_c)
ax.tick_params(axis='y', labelcolor=g_c)
# Plot phase shift
ax1 = ax.twinx()
ax1.set_ylabel('phase shift', c=p_c)
ax1.yaxis.set_major_locator(tck.MultipleLocator(base=pi/4))
ax1.plot(w_fr, np.unwrap(np.angle(r_fr), period=2*pi), label='phase shift', c=p_c)
ax1.tick_params(axis='y', labelcolor=p_c)
# Convert floats to pi fractions
convert_to_pi_fractions(ax)
convert_to_pi_fractions(ax1, axis='y')

Related

Scale y-axis for really small numbers

I'm trying to scale the y-axis so my errorbars can be seen.
Any help would be appreciated! :)
Here is my current code.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
# if using a Jupyter notebook, include:
%matplotlib inline
x = ntermsList
y = allPmuCycleCountAverages
xerr = 0
yerr = allPmuCycleCountStandardDeviations
fig, ax = plt.subplots()
ax.errorbar(x, y, xerr=xerr, yerr=yerr,fmt='-o')
ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_title('Line plot with error bars')
ax.set_xticks(ntermsList)
ax.set_xticklabels(ntermsList)
ax.set_yticks(allPmuCycleCountAverages)
ax.yaxis.grid(True)
plt.show()
I've tried these solutions, but no joy:
plt.ylim(-1, 1)
plt.rcParams["figure.figsize"] = [7.50, 3.50]
plt.rcParams["figure.autolayout"] = True
plt.yticks(np.arange(min(y), max(y)+0.5, 0.01))
I was expecting the y-axis scale to zoom close enough to the points so my errorbars could be seen
Try autoscalling based in y ticks. Here I'm adding some logic that just rescales the y-axis based on the data that is in the visible x-region. As I don't have your data I took random data.
import numpy as np
import random
ntermsList = np.random.randint(low=0, high=10, size=(555,))
allPmuCycleCountAverages = np.random.randint(low=0, high=10, size=(555,))
allPmuCycleCountStandardDeviations = np.random.randint(low=0, high=10, size=(555,))
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
# if using a Jupyter notebook, include:
%matplotlib inline
x = ntermsList
y = allPmuCycleCountAverages
xerr = 0
yerr = allPmuCycleCountStandardDeviations
fig, ax = plt.subplots()
ax.errorbar(x, y, xerr=xerr, yerr=yerr,fmt='-o')
ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_title('Line plot with error bars')
ax.set_xticks(ntermsList)
ax.set_xticklabels(ntermsList)
ax.set_yticks(allPmuCycleCountAverages)
#plt.setp(ax.get_yticklabels(), rotation=90, horizontalalignment='right')
ax.yaxis.grid(True)
margin =0.1
def get_bottom_top(line):
xd = line.get_xdata()
yd = line.get_ydata()
lo,hi = ax.get_xlim()
y_displayed = yd[((xd>lo) & (xd<hi))]
h = np.max(y_displayed) - np.min(y_displayed)
bot = np.min(y_displayed)-margin*h
top = np.max(y_displayed)+margin*h
return bot,top
lines = ax.get_lines()
bot,top = np.inf, -np.inf
for line in lines:
new_bot, new_top = get_bottom_top(line)
if new_bot < bot: bot = new_bot
if new_top > top: top = new_top
ax.set_ylim(bot,top)
plt.show()
Before Rescalling
After rescalling

Python matplotlib.animation Jupyter Notebook

I use Windows 10 / 64 / Google chrome
I found a good set-up for animation over Jupyter with the call %matplotlib notebook as here :
import numpy as np
import scipy.stats as st
%matplotlib notebook
import matplotlib.pyplot as plt
import matplotlib.animation as animation
For exemple, this one is working pretty well :
n = 100
X = st.norm(0,1).rvs(200)
number_of_frames = np.size(X)
def update_hist(num, second_argument):
plt.cla()
plt.hist(X[:num], bins = 20)
plt.title("{}".format(num))
plt.legend()
fig = plt.figure()
hist = plt.hist(X)
ani = animation.FuncAnimation(fig, update_hist, number_of_frames, fargs=(X, ), repeat = False )
plt.show()
But, weirdly the code below doesn't work while it's the same structure, it puzzles me :
X = np.linspace(-5,5, 150)
number_of_frames = np.size(X)
N_max = 100
N = np.arange(1,N_max+1)
h = 1/np.sqrt(N)
def update_plot(n, second_argument):
#plt.cla()
plt.plot(X, [f(x) for x in X], c = "y", label = "densité")
plt.plot(X, [fen(sample_sort[:n],h[n],x) for x in X], label = "densité")
plt.title("n = {}".format(n))
fig = plt.figure(6)
plot = plt.plot(X, [f(x) for x in X], c = "y", label = "densité")
ani = animation.FuncAnimation(fig, update_plot, number_of_frames, fargs=(X, ), repeat = False )
plt.show()
Thanks for your help, best regards.
EDIT : You don't have the funciton fen(sample_sort[:n],h[n],x) it is a function from float to float taking a x in argument and returning a flot. The argument sample_sort[:n],h[n] it is just maths things I'm trying to understand some statistics anyway, you can remplace with line with what you want np.cos(N[:n]) for exemple.
EDIT : New code according to the suggestion :
N_max = 100
X = np.linspace(-5,5, N_max )
number_of_frames = np.size(X)
N = np.arange(1,N_max+1)
h = 1/np.sqrt(N)
def update_plot(n):
#plt.cla()
lines.set_data(X, np.array([fen(sample_sort[:n],h[n],x) for x in X]))
ax.set_title("n = {}".format(n))
return lines
fig = plt.figure()
ax = plt.axes(xlim=(-4, 4), ylim=(-0.01, 1))
ax.plot(X, np.array([f(x) for x in X]), 'y-', lw=2, label="d")
lines, = ax.plot([], [], 'b--', lw=3, label="f")
ani = animation.FuncAnimation(fig, update_plot, number_of_frames, repeat = False )
plt.show()
EDIT 2:
I found a code over internet which does exactly what I would like
# Fermi-Dirac Distribution
def fermi(E: float, E_f: float, T: float) -> float:
return 1/(np.exp((E - E_f)/(k_b * T)) + 1)
# Create figure and add axes
fig = plt.figure(figsize=(6, 4))
ax = fig.add_subplot(111)
# Get colors from coolwarm colormap
colors = plt.get_cmap('coolwarm', 10)
# Temperature values
T = np.array([100*i for i in range(1,11)])
# Create variable reference to plot
f_d, = ax.plot([], [], linewidth=2.5)
# Add text annotation and create variable reference
temp = ax.text(1, 1, '', ha='right', va='top', fontsize=24)
# Set axes labels
ax.set_xlabel('Energy (eV)')
ax.set_ylabel('Fraction')
# Animation function
def animate(i):
x = np.linspace(0, 1, 100)
y = fermi(x, 0.5, T[i])
f_d.set_data(x, y)
f_d.set_color(colors(i))
temp.set_text(str(int(T[i])) + ' K')
temp.set_color(colors(i))
# Create animation
ani = animation.FuncAnimation(fig, animate, frames=range(len(T)), interval=500, repeat=False)
# Ensure the entire plot is visible
fig.tight_layout()
# show animation
plt.show()
What I want to draw is a curve at random because the actual state of the function is unknown. The basic structure looks like this, so please modify it based on this.
import numpy as np
import scipy.stats as st
# %matplotlib notebook
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# from IPython.display import HTML
# from matplotlib.animation import PillowWriter
X = np.linspace(-5,5, 100)
number_of_frames = np.size(X)
N_max = 100
N = np.arange(1,N_max+1)
h = 1/np.sqrt(N)
def update_plot(n):
#plt.cla()
lines.set_data(X[:n], h[:n])
lines2.set_data(X[:n], h[:n]*-1)
ax.set_title("n = {}".format(n))
return lines, lines2
fig = plt.figure()
ax = plt.axes(xlim=(-5, 5), ylim=(-1, 1))
lines, = ax.plot([], [], 'y-', lw=2, label="densité")
lines2, = ax.plot([], [], 'b--', lw=3, label="densité2")
ani = animation.FuncAnimation(fig, update_plot, frames=number_of_frames, repeat=False )
plt.show()
# ani.save('lines_ani2.gif', writer='pillow')
# plt.close()
# HTML(ani.to_html5_video())

Matplotlib Second x-axis with transformed values

I have been using a piece of code (based on the solution to another's problem given here) to create plots of spectroscopic data with two x-axis. The first (bottom) is in frequency units, the second (top) is just transformed to wavelength units (wavelength = 3E8/frequency). This was working well until I upgraded MPL to 1.4.2 after which the values on the upper axis are just the same as those on the lower axis (see example).
A MWE (an exact copy from the MPL mailing list) is:
from matplotlib.transforms import Transform, BlendedGenericTransform, IdentityTransform
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid.parasite_axes import SubplotHost
import numpy as np
c = 3.e2
class Freq2WavelengthTransform(Transform):
input_dims = 1
output_dims = 1
is_separable = False
has_inverse = True
def transform(self, tr):
return c/tr
def inverted(self):
return Wavelength2FreqTransform()
class Wavelength2FreqTransform(Freq2WavelengthTransform):
def inverted(self):
return Freq2WavelengthTransform()
aux_trans = BlendedGenericTransform(Freq2WavelengthTransform(),
IdentityTransform())
fig = plt.figure(2)
ax_GHz = SubplotHost(fig, 1,1,1)
fig.add_subplot(ax_GHz)
ax_GHz.set_xlabel("Frequency (GHz)")
xvals = np.arange(199.9, 999.9, 0.1)
#make some test data
data = np.sin(0.03*xvals)
ax_mm = ax_GHz.twin(aux_trans)
ax_mm.set_xlabel('Wavelength (mm)')
ax_mm.set_viewlim_mode("transform")
ax_mm.axis["right"].toggle(ticklabels=False)
ax_GHz.plot(xvals, data)
ax_GHz.set_xlim(200, 1000)
plt.draw()
plt.show()
This produces
Can any one advise me how to address this in MPL 1.4.2?
Using a combination of Adobe's answer from the thread linked to in wwii's comment, and your own code.
import numpy as np
import matplotlib.pyplot as plt
c=3.e2
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twiny()
xvals = np.arange(199.9, 999.9, 0.1)
data = np.sin(0.03*xvals)
ax1.plot(xvals, data)
ax1Ticks = ax1.get_xticks()
ax2Ticks = ax1Ticks
def tick_function(X):
V = c/X
return ["%.3f" % z for z in V]
ax2.set_xticks(ax2Ticks)
ax2.set_xbound(ax1.get_xbound())
ax2.set_xticklabels(tick_function(ax2Ticks))
ax1.set_xlabel("Frequency (GHz)")
ax2.set_xlabel('Wavelength (mm)')
ax1.grid(True)
plt.ylim(ymin=-1.1,ymax=1.1)
plt.show()
This produces;
I hope this helps!

How to change the number of minorticks?

I want to create such figure (https://lh3.googleusercontent.com/-DCFuRHtqTmk/VFq_cf0Cj4I/AAAAAAAAETA/hDPE8N8LLDE/w682-h695-no/figure.png) using PLT.MATSHOW with labeled majorticks (in range from 0 to 20 with step = 1), unlabeled minorticks (from 0.5 to 18.5 with step = 1) and minorgrids.
Here is my code, but something is wrong with it (it draws more lines than I need and I do not know how to change number of minorticks):
import numpy as np
import numpy.random as random
import matplotlib.pyplot as plt
S0 = np.ones([20,20], int)
S = np.copy(S0)
Mx = np.shape(S)[0]
My = np.shape(S)[1]
for x in range(Mx):
for y in range(My):
S[x,y]=2*random.randint(2)-1
plt.matshow(S, fignum = None, alpha = 0.75, cmap = "summer")
plt.xticks(range(0, Mx, 1))
plt.yticks([i for i in range(0, My)])
plt.grid(which = 'minor', ls = '-')
plt.minorticks_on()
plt.show()
How should I rewrite it to solve my problem?
Thank you for the help! (:
I believe this solves your problem.
import numpy as np
import numpy.random as random
import matplotlib.pyplot as plt
S0 = np.ones([20,20], int)
S = np.copy(S0)
Mx = np.shape(S)[0]
My = np.shape(S)[1]
for x in range(Mx):
for y in range(My):
S[x,y]=2*random.randint(2)-1
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.matshow(S,alpha=0.75, cmap="summer", interpolation="none")
ax.set_xticks([i+0.5 for i in range(Mx)])
ax.set_yticks([i+0.5 for i in range(My)])
ax.set_xticklabels(range(Mx))
ax.set_yticklabels(range(My))
#plt.grid(which = 'minor', ls = '-')
plt.grid(which = 'major', ls = '-')
plt.minorticks_on()
plt.show()
The changes that i have made are threefold; the first replaces the grid with the major grid lines, and the second is that i have moved the ticks and corresponding labels by 0.5, so that they match the boundaries. The third is to remove interpolation in the matshow command. This all gives this:
I have finally found how to solve the problem and get what I want. Thank you, Will, for the help!
If someone else knows how to get the final result using a different method, I would appretiate if you let me know.
Here is new code:
import numpy as np
import numpy.random as random
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, AutoMinorLocator
S0 = np.ones([20,20], int)
S = np.copy(S0)
Mx = np.shape(S)[0]
My = np.shape(S)[1]
for x in range(Mx):
for y in range(My):
S[x,y]=2*random.randint(2)-1
fig = plt.figure()
ax = fig.add_subplot(111)
ax.matshow(S, alpha = .75, cmap = "summer")
ax.set_xticklabels(range(-1,Mx))
ax.set_yticklabels(range(-1,My))
ax.xaxis.set_major_locator(MultipleLocator(1))
ax.xaxis.set_minor_locator(AutoMinorLocator(2))
ax.yaxis.set_major_locator(MultipleLocator(1))
ax.yaxis.set_minor_locator(AutoMinorLocator(2))
ax.xaxis.grid(True,'minor', lw = 1.5, ls = '-')
ax.yaxis.grid(True,'minor', lw = 1.5, ls = '-')
plt.show()

How can I set the y axis in radians in a Python plot?

I would like to write the radian units of the axes as proportional to \pi: something like
$\frac{\pi}{4}$, $\frac{\pi}{2}$, ...
in place of
0.785, 1.5707 ...
Is there any standard way?
As an example, what should I add to the following code?
from pylab import *
x=arange(-10.0,10.0,0.1)
y= arctan(x)
plot(x,y,'b.')
show()
I found this example http://matplotlib.sourceforge.net/examples/units/radian_demo.html but it does not work because I don't have basic_units module.
Thank you!
hard code them in fractions or accept floating numbers
import matplotlib.pyplot as plt
import numpy as np
x=np.arange(-10.0,10.0,0.1)
y=np.arctan(x)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x,y,'b.')
y_pi = y/np.pi
unit = 0.25
y_tick = np.arange(-0.5, 0.5+unit, unit)
y_label = [r"$-\frac{\pi}{2}$", r"$-\frac{\pi}{4}$", r"$0$", r"$+\frac{\pi}{4}$", r"$+\frac{\pi}{2}$"]
ax.set_yticks(y_tick*np.pi)
ax.set_yticklabels(y_label, fontsize=20)
y_label2 = [r"$" + format(r, ".2g")+ r"\pi$" for r in y_tick]
ax2 = ax.twinx()
ax2.set_yticks(y_tick*np.pi)
ax2.set_yticklabels(y_label2, fontsize=20)
plt.show()
the result is
i wrote a little function, that gives you back a list of labels:
import numpy as np
from fractions import Fraction
def create_pi_labels(a, b, step):
max_denominator = int(1/step)
# i added this line and the .limit_denominator to solve an
# issue with floating point precision
# because of floataing point precision Fraction(1/3) would be
# Fraction(6004799503160661, 18014398509481984)
values = np.arange(a, b+step/10, step)
fracs = [Fraction(x).limit_denominator(max_denominator) for x in values]
ticks = values*np.pi
labels = []
for frac in fracs:
if frac.numerator==0:
labels.append(r"$0$")
elif frac.numerator<0:
if frac.denominator==1 and abs(frac.numerator)==1:
labels.append(r"$-\pi$")
elif frac.denominator==1:
labels.append(r"$-{}\pi$".format(abs(frac.numerator)))
else:
labels.append(r"$-\frac{{{}}}{{{}}} \pi$".format(abs(frac.numerator), frac.denominator))
else:
if frac.denominator==1 and frac.numerator==1:
labels.append(r"$\pi$")
elif frac.denominator==1:
labels.append(r"${}\pi$".format(frac.numerator))
else:
labels.append(r"$\frac{{{}}}{{{}}} \pi$".format(frac.numerator, frac.denominator))
return ticks, labels
https://github.com/MaxNoe/python-plotting/blob/master/source/create_pi_labels.py
You can download the basic_units.py file here:
After it should work like this:
from pylab import *
from basic_units import radians
x = arange(-10.0,10.0,0.1)
y = map(lambda y: y*radians,arctan(x))
x = map(lambda x: x*radians,x)
plot(x,y,'b.',xunits=radians,yunits=radians)
show()
Alternatively you could implement the arctan function the way they implemented the cos function in basic_units.py
I created a PyPi Package that can format and place ticks at multiples of fractions of pi.
"""Show an example of using MultiplePi on the y-axis."""
import matplotlib.pyplot as plt
import numpy as np
from matplot_fmt_pi import MultiplePi
x = np.arange(-10.0, 10.0, 0.1)
y = np.arctan(x)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title("MultiplePi formatting")
ax.plot(x, y, 'b.')
y_pi = y / np.pi
unit = 0.25
y_tick = np.arange(-0.5, 0.5 + unit, unit)
# New way
manager = MultiplePi(4)
ax.yaxis.set_major_locator(manager.locator())
ax.yaxis.set_major_formatter(manager.formatter())
# Other way
y_label2 = [r"$" + format(r, ".2g") + r"\pi$" for r in y_tick]
ax2 = ax.twinx()
ax2.set_yticks(y_tick * np.pi)
ax2.set_yticklabels(y_label2)
plt.savefig("./pi_y_axis.png", dpi=120)

Categories