234

I have too many ticks on my graph and they are running into each other.

How can I reduce the number of ticks?

For example, I have ticks:

1E-6, 1E-5, 1E-4, ... 1E6, 1E7

And I only want:

1E-5, 1E-3, ... 1E5, 1E7

I've tried playing with the LogLocator, but I haven't been able to figure this out.

ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
jlconlin
  • 14,206
  • 22
  • 72
  • 105

10 Answers10

347

Alternatively, if you want to simply set the number of ticks while allowing matplotlib to position them (currently only with MaxNLocator), there is pyplot.locator_params,

pyplot.locator_params(nbins=4)

You can specify specific axis in this method as mentioned below, default is both:

# To specify the number of ticks on both or any single axes
pyplot.locator_params(axis='y', nbins=6)
pyplot.locator_params(axis='x', nbins=10)
Mr. T
  • 11,960
  • 10
  • 32
  • 54
bgamari
  • 5,913
  • 1
  • 18
  • 21
  • 38
    This was a great suggestion, also being able to specify `pyplot.locator_params(axis = 'x', nbins = 4)` (or `axis = 'y'`) made the process really straightforward. Thanks @bgamari! – benjaminmgross Dec 28 '12 at 19:40
  • Is there any way I could use that with a FixedLocator? – branwen85 Jul 08 '14 at 13:44
  • how to use the MaxNLocator instead of FixedLocator? – Ash Nov 09 '15 at 01:10
  • 12
    With log scale, this worked with ``numticks`` instead of ``nbins`` – meduz May 19 '16 at 10:01
  • 1
    @bgamari, could you please add the `numticks` solution for logarithmic plots as pointed out by @meduz? – Løiten Jan 08 '17 at 13:44
  • 1
    How does this function work, how do you have to use it? I used `import matplotlib.pyplot as plt; plt.locator_params(axis='x', nticks=4)` before and after `plt.plot` but it does nothing. I use version `2.1.1`. – DerWeh Feb 25 '18 at 20:00
  • 19
    This doesn't seem to place the labels where they should be. For example, if the original tick labels are `[0, 1, ..., 99]` and now one sets `nticks=10`, then the new sparse labels will be placed ten times as long apart along the axis, i.e. now `1` will sit where `9` was, `2` where `19` was... and `9` where `99` was. – Vim Mar 08 '18 at 02:35
  • 5
    Check your results before trusting this method. @Vim is correct. The tick values will be placed incorrectly. – David J. Jan 31 '19 at 01:34
  • Adding to @mikkokotila comment about future version incompatibility. You can use the `set_major_locator`, see full answer below – Prageeth Jayathissa Oct 29 '19 at 15:09
  • This is very straightforward. I could also use it on an existing `matplotlib.axes.Axes` (https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.axes.Axes.locator_params.html) – panc Oct 14 '20 at 16:56
  • 2
    I got incorrect labeling with this method, but @Katerina's worked as expected. – ldmtwo Nov 04 '20 at 22:05
  • I have been looking for how to plot timeseries data without weekend gaps and this code helped me to format the xticks nicely. Thanks a bunch – theAfricanQuant Jul 26 '22 at 06:10
118

To solve the issue of customisation and appearance of the ticks, see the Tick Locators guide on the matplotlib website

ax.xaxis.set_major_locator(plt.MaxNLocator(3))

would set the total number of ticks in the x-axis to 3, and evenly distribute them across the axis.

There is also a nice tutorial about this

Adriaan
  • 17,741
  • 7
  • 42
  • 75
Prageeth Jayathissa
  • 1,798
  • 1
  • 10
  • 16
  • Only select first 3 datetime index. when get ax from pandas.DataFrame.plot `ax = df.plot()` – Mithril Nov 01 '19 at 02:27
  • @Mithril sorry I don't quite understand your comment. Could you please elaborate? – Prageeth Jayathissa Nov 04 '19 at 02:59
  • 1
    If I have a df ( `pandas.DataFrame` ) with datetime index [2019-01-01, ...2019-11-01], call `ax = df.plot()` , return a figure object . call `ax.xaxis.set_major_locator(plt.MaxNLocator(3))` only show first 3 index [2019-01-01, 2019-01-02, 2019-01-03] . – Mithril Nov 04 '19 at 05:57
  • 1
    @Mithril, `df.plot()` often displays the `minor_locator`, so you might want to try `ax1.xaxis.set_minor_locator(plt.MaxNLocator(3))`. Also remember to substitute the `3` for the number of ticks that you want to display. For pandas timeseries I recommend `import matplotlib.dates as mdates` and run `ax.xaxis.set_minor_locator(mdates.MonthLocator(interval = 1))` with `ax.xaxis.set_minor_formatter(mdates.DateFormatter('%m-%Y'))` – Prageeth Jayathissa Nov 04 '19 at 11:18
  • As I had a lot of datapoints to plot, pyplot was printing a lot (>500) ticks per axis. This heavily slowed down the plotting process. By reducing the number of thicks with `set_major_locator(5)` the plotting process is much faster. – Sparkofska Aug 11 '20 at 09:27
  • Another wonderful solution – theAfricanQuant Jul 26 '22 at 06:13
107

If somebody still gets this page in search results:

fig, ax = plt.subplots()

plt.plot(...)

every_nth = 4
for n, label in enumerate(ax.xaxis.get_ticklabels()):
    if n % every_nth != 0:
        label.set_visible(False)
Katerina
  • 2,580
  • 1
  • 22
  • 25
27

There's a set_ticks() function for axis objects.

André Caron
  • 44,541
  • 12
  • 67
  • 125
  • 4
    This would work if I knew beforehand what ticks I wanted. The example I gave above was only an example. I don't know what the ticks are, I just know I want fewer of them, i.e., every other one. – jlconlin Jul 13 '11 at 17:28
  • 12
    You could call `get_xticks()` or `get_yticks()` first for the axes object, edit as needed, and then pass the list back to `set_ticks()`. – user812786 Jul 13 '11 at 17:44
  • 4
    I don't have `set_ticks()`, but I do have `set_xticks()` and `set_yticks()`. These are attributes of axes objects, not axis. Maybe this has changed during the last couple of years. – Gauthier Jun 11 '13 at 08:01
  • 2
    I am not sure if I should, some people have found your answer useful as is, and just because it is different for me does not mean it is for everybody. – Gauthier Jun 12 '13 at 07:24
  • 7
    An example would go a long way towards making this answer useful. – Richard May 31 '18 at 23:00
14

in case somebody still needs it, and since nothing here really worked for me, i came up with a very simple way that keeps the appearance of the generated plot "as is" while fixing the number of ticks to exactly N:

import numpy as np
import matplotlib.pyplot as plt

f, ax = plt.subplots()
ax.plot(range(100))

ymin, ymax = ax.get_ylim()
ax.set_yticks(np.round(np.linspace(ymin, ymax, N), 2))
raphael
  • 2,159
  • 17
  • 20
  • 3
    I had to modify the last line slightly in order to get it to return the values as int instead of float: `ax.set_yticks(np.linspace(int(ymin), int(ymax), N), 2)` – Nick Settje Sep 07 '18 at 09:01
  • @NickSettje still floats with me! – Mohd Jul 07 '20 at 17:12
9

The solution @raphael gave is straightforward and quite helpful.

Still, the displayed tick labels will not be values sampled from the original distribution but from the indices of the array returned by np.linspace(ymin, ymax, N).

To display N values evenly spaced from your original tick labels, use the set_yticklabels() method. Here is a snippet for the y axis, with integer labels:

import numpy as np
import matplotlib.pyplot as plt

ax = plt.gca()

ymin, ymax = ax.get_ylim()
custom_ticks = np.linspace(ymin, ymax, N, dtype=int)
ax.set_yticks(custom_ticks)
ax.set_yticklabels(custom_ticks)
Tantris
  • 91
  • 1
  • 4
6

If you need one tick every N=3 ticks :

N = 3  # 1 tick every 3
xticks_pos, xticks_labels = plt.xticks()  # get all axis ticks
myticks = [j for i,j in enumerate(xticks_pos) if not i%N]  # index of selected ticks
newlabels = [label for i,label in enumerate(xticks_labels) if not i%N]

or with fig,ax = plt.subplots() :

N = 3  # 1 tick every 3
xticks_pos = ax.get_xticks()
xticks_labels = ax.get_xticklabels()
myticks = [j for i,j in enumerate(xticks_pos) if not i%N]  # index of selected ticks
newlabels = [label for i,label in enumerate(xticks_labels) if not i%N]

(obviously you can adjust the offset with (i+offset)%N).

Note that you can get uneven ticks if you wish, e.g. myticks = [1, 3, 8].

Then you can use

plt.gca().set_xticks(myticks)  # set new X axis ticks

or if you want to replace labels as well

plt.xticks(myticks, newlabels)  # set new X axis ticks and labels

Beware that axis limits must be set after the axis ticks.

Finally, you may wish to draw only an arbitrary set of ticks :

mylabels = ['03/2018', '09/2019', '10/2020']
plt.draw()  # needed to populate xticks with actual labels
xticks_pos, xticks_labels = plt.xticks()  # get all axis ticks
myticks = [i for i,j in enumerate(xticks_labels) if j.get_text() in mylabels]
plt.xticks(myticks, mylabels)

(assuming mylabels is ordered ; if it is not, then sort myticks and reorder it).

Skippy le Grand Gourou
  • 6,976
  • 4
  • 60
  • 76
3

xticks function auto iterates with range function

start_number = 0

end_number = len(data you have)

step_number = how many skips to make from strat to end

rotation = 90 degrees tilt will help with long ticks

plt.xticks(range(start_number,end_number,step_number),rotation=90)
dminchev
  • 31
  • 3
  • I came to contribute a range based solution. Your example is much more concise. But to add to it, you can use the shape to get what you want eg `plt.xticks(range(0, df.shape[0], 12),rotation=90)` – Joe Still Aug 22 '22 at 22:01
2

if you want 10 ticks:

for y axis: ax.set_yticks(ax.get_yticks()[::len(ax.get_yticks())//10])

for x axis: ax.set_xticks(ax.get_xticks()[::len(ax.get_xticks())//10])

this simply gets your ticks and chooses every 10th of the list and sets it back to your ticks. you can change the number of ticks as you wish.

Leo
  • 55
  • 6
-2

When a log scale is used the number of major ticks can be fixed with the following command

import matplotlib.pyplot as plt

....

plt.locator_params(numticks=12)
plt.show()

The value set to numticks determines the number of axis ticks to be displayed.

Credits to @bgamari's post for introducing the locator_params() function, but the nticks parameter throws an error when a log scale is used.

Ébe Isaac
  • 11,563
  • 17
  • 64
  • 97