Forklift17
Forklift17

Reputation: 2477

custom labels pyplot matplotlib

Here is a script that I am running in Python:

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(0, 2*np.pi, 150)
plt.figure()
theGrid = np.outer(np.sin(t), np.sin(t))
plt.xlim(0, 2*np.pi) #forces an exact fit!
plt.ylim(0, 2*np.pi) #ditto!
plt.xticks(np.linspace(0, 2*np.pi, 5))
plt.yticks(np.linspace(0, 2*np.pi, 5))
plt.pcolormesh(t, t, theGrid)

It's a nice plot, and both the x and y labels are clearly multiples of pi/2. But how would I get the x and y labels to read, say, 0, pi/2, pi, 3*pi/2, and 2*pi, respectively, in particular the introduction of text? (The mapping of say, the number np.pi/2 to the text "pi/2" shouldn't be hard, but getting text labels is basically what I am getting at here.)

Upvotes: 0

Views: 2834

Answers (2)

tmdavison
tmdavison

Reputation: 69213

To extend @leal26's answer into something more flexible (i.e. for if we want to zoom/pan around the plot), we can use the matplotlib.ticker module.

The locations of the ticks can be set using ax.xaxis.set_major_locator (and ax.yaxis.set_major_locator) with a ticker.MultipleLocator, using a base = pi/2.

We can then use a ticker.FuncFormatter to set the format of the tick labels (this time using ax.xaxis.set_major_formatter. There are several special cases we have to account for (e.g. 1 in the numerator and/or denominator, etc.). See the fracformat function below.

Notice also that we have to use a raw string (r prefix) and double braces for the LaTeX formatting.

The real advantage here is that we can easily change the tick locations, the plot limits, etc., without having to manually redefine the tick labels every time.

Finally, I also slightly changed your script to make use of matplotlib's object-oriented approact, so that we create a Figure instance with plt.figure, an Axes instance (ax) with fig.add_subplot, and then for everything else, we call the relevant functions from ax.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from fractions import Fraction

t = np.linspace(0, 4*np.pi, 150)

fig = plt.figure()
ax = fig.add_subplot(111)

theGrid = np.outer(np.sin(t), np.sin(t))

ax.set_xlim(0, 4*np.pi) #forces an exact fit!
ax.set_ylim(0, 4*np.pi) #ditto!

def fracformat(x,pos):
    frac = Fraction(x/np.pi)
    if frac.numerator == 0:
        return 0
    elif frac.numerator == frac.denominator:
        return r"$\pi$"
    elif frac.numerator == 1:
        return r"$\frac{{ \pi }}{{ {:2d} }}$".format(frac.denominator)
    elif frac.denominator == 1:
        return r"${:2d} \pi$".format(frac.numerator)
    else:
        return r"$\frac{{ {:2d} \pi }}{{ {:2d} }}$".format(frac.numerator,frac.denominator)

ax.xaxis.set_major_locator(ticker.MultipleLocator(np.pi/2.))
ax.yaxis.set_major_locator(ticker.MultipleLocator(np.pi/2.))

ax.xaxis.set_major_formatter(ticker.FuncFormatter(fracformat))
ax.yaxis.set_major_formatter(ticker.FuncFormatter(fracformat))

ax.pcolormesh(t, t, theGrid)

plt.show()

I increased the plot limits to 4*pi to show how easy it is to extend this to different ranges, with minimal effort.

enter image description here

Upvotes: 0

leal26
leal26

Reputation: 86

Actually, you are almost there. The following code was based on this post. The first input for xticks and yticks is the list of locations while the second input is the list of labels, which can be strings.

I also used Latex notation to get the greek letter for pi.

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(0, 2*np.pi, 150)
plt.figure()
theGrid = np.outer(np.sin(t), np.sin(t))
plt.xlim(0, 2*np.pi) #forces an exact fit!
plt.ylim(0, 2*np.pi) #ditto!
plt.xticks(np.linspace(0, 2*np.pi, 5),['0','$\pi/2$','$\pi$','$3\pi/2$','$2\pi$'])
plt.yticks(np.linspace(0, 2*np.pi, 5),['0','$\pi/2$','$\pi$','$3\pi/2$','$2\pi$'])
plt.pcolormesh(t, t, theGrid)

Upvotes: 4

Related Questions