Use matplotlib.ticker.Locator to put ticks only if location is divisible by a given value

Viewed 549

For example I need to set a tick only if dividing by 9 gives a "nice" value (nice here has the same meaning as in https://matplotlib.org/3.2.1/api/ticker_api.html).

Accordingly any set of following ticks should be allowed.

  • [9, 18, 27] allowed b/c dividing by 9 gives [1, 2, 3]
  • [27, 54, 81, 108] allowed b/c dividing by 9 gives [3, 6, 9, 12]
  • [0.45, 0.9, 1.35] allowed b/c dividing by 9 gives [0.05, 0.1, 0.15]

Note that I am not looking to set ticks at every multiple of 9, so MultipleLocator doesn't work here.

I also want the locator to automatically figure out "how many ticks" and "best tick interval" etc. Basically I need the functionality of MaxNLocator with my additional divisibility requirement.

Current best solution is MaxNLocator with the arguments MaxNLocator('auto', steps=[9]). But MaxNLocator explicitly adds 10 to the steps argument (see https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/ticker.py#L2058) so sometimes I get tick values like [1000, 2000, 3000] which aren't acceptable. (1000 is not a multiple of 9, see the example 1,2,3 below)

Any suggestions?

UPDATE: See below for some nice vs. not nice examples.

import matplotlib.pyplot as plt
import numpy as np

def draw(name, xmax, steps, verdict):
    x = np.linspace(0, xmax)
    plt.plot(x, np.sin(x))
    plt.gca().set_xlim([0, xmax])
    plt.gca().xaxis.set_major_locator(plt.MaxNLocator('auto', steps=steps))

    vd = 'NICE!!!\n' if verdict else 'NOT nice!\n'
    print(name, plt.gca().get_xticks()/9, '>>', vd)


draw('example 1', 64, [9], True)
draw('example 2', 7.2, [9], True)
draw('example 3', 90000, [9], False)
draw('example 4', 9, [1, 3, 9], False)
draw('example 5', 90, [1.8, 4.5, 9], False)

Output:

example 1 [0. 1. 2. 3. 4. 5. 6. 7. 8.] >> NICE!!!

example 2 [0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8] >> NICE!!!

example 3 [0. 1111.11111111  2222.22222222  3333.33333333
  4444.44444444  5555.55555556  6666.66666667  7777.77777778
  8888.88888889 10000.] >> NOT nice!

example 4 [0. 0.11111111 0.22222222 0.33333333 0.44444444 0.55555556
 0.66666667 0.77777778 0.88888889 1.] >> NOT nice!

example 5 [ 0.  1.11111111  2.22222222  3.33333333  4.44444444  5.55555556
  6.66666667  7.77777778  8.88888889 10.] >> NOT nice!
1 Answers

You can provide a steps= argument to MaxNLocator. These steps need to be between 1 and 10. In your case you could put there the values you like to be used as multiplicator. For example [1.8, 4.5, 9].

from matplotlib.ticker import MaxNLocator
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 999)
y = np.sin(x/100)
plt.plot(x, y)
plt.gca().xaxis.set_major_locator(MaxNLocator(steps=[1.8, 4.5, 9]))
plt.gca().yaxis.set_major_locator(MaxNLocator(steps=[1.8, 4.5, 9]))
plt.show()

example plot

Related