778

I am trying to fix how python plots my data. Say:

x = [0, 5, 9, 10, 15]
y = [0, 1, 2, 3, 4]

matplotlib.pyplot.plot(x, y)
matplotlib.pyplot.show()

The x axis' ticks are plotted in intervals of 5. Is there a way to make it show intervals of 1?

1

15 Answers 15

922

You could explicitly set where you want to tick marks with plt.xticks:

plt.xticks(np.arange(min(x), max(x)+1, 1.0))

For example,

import numpy as np
import matplotlib.pyplot as plt

x = [0,5,9,10,15]
y = [0,1,2,3,4]
plt.plot(x,y)
plt.xticks(np.arange(min(x), max(x)+1, 1.0))
plt.show()

(np.arange was used rather than Python's range function just in case min(x) and max(x) are floats instead of ints.)


The plt.plot (or ax.plot) function will automatically set default x and y limits. If you wish to keep those limits, and just change the stepsize of the tick marks, then you could use ax.get_xlim() to discover what limits Matplotlib has already set.

start, end = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(start, end, stepsize))

The default tick formatter should do a decent job rounding the tick values to a sensible number of significant digits. However, if you wish to have more control over the format, you can define your own formatter. For example,

ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))

Here's a runnable example:

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

x = [0,5,9,10,15]
y = [0,1,2,3,4]
fig, ax = plt.subplots()
ax.plot(x,y)
start, end = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(start, end, 0.712123))
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
plt.show()
10
  • 132
    Is there no way to get it to still decide it's own limits, but just change the step size? This method is not very good if the min is something like 3523.232512!
    – Corvus
    Commented Oct 1, 2013 at 16:41
  • 3
    @Corone, It has been a while since you asked, but I have posted an answer below that allows for easy control of step size while still using automatic bounds determination.
    – jthomas
    Commented Mar 26, 2016 at 13:04
  • 5
    Note that the +1 in plt.xticks(np.arange(min(x), max(x)+1, 1.0)) is required to show the last tick mark. Commented May 19, 2017 at 15:23
  • 1
    Yes, np.arange(start, stop) generates values in the half-open interval [start, stop), including start but excluding stop. So I used max(x)+1 to ensure that max(x) is included.
    – unutbu
    Commented May 19, 2017 at 16:19
  • 8
    is there an equivalent for datetime e.g. plt.xticks(np.arange(min(dates), max(dates)+0.1,0.1) ? it seems to only plots the year Commented Jan 10, 2018 at 11:42
303

Another approach is to set the axis locator:

import matplotlib.ticker as plticker

loc = plticker.MultipleLocator(base=1.0) # this locator puts ticks at regular intervals
ax.xaxis.set_major_locator(loc)

There are several different types of locator depending upon your needs.

Here is a full example:

import matplotlib.pyplot as plt
import matplotlib.ticker as plticker

x = [0,5,9,10,15]
y = [0,1,2,3,4]
fig, ax = plt.subplots()
ax.plot(x,y)
loc = plticker.MultipleLocator(base=1.0) # this locator puts ticks at regular intervals
ax.xaxis.set_major_locator(loc)
plt.show()
8
  • 9
    This does not work as expected. Specifically, when using dates, it does not use the appropriate dates. Commented Feb 5, 2014 at 17:02
  • 65
    When using dates, you should use the methods in the matplotlib.dates module. For example matplotlib.dates.AutoDateLocator()
    – robochat
    Commented Mar 20, 2014 at 13:06
  • 6
    It worked as expected for me, with dates. This solution is much easier than the accepted one.
    – Pablo Suau
    Commented Jul 8, 2016 at 13:58
  • 4
    What does base=1.0 actually mean/do ? Commented Apr 23, 2020 at 6:18
  • 2
    base=1.0 means that there will a locator for every whole number. The documentation says that MultipleLocator "Set[s] a tick on each integer multiple of a base within the view interval.". So if base=2 then there'll be a tick for even numbers and I think that you could event put base=2.5.
    – robochat
    Commented Apr 29, 2020 at 17:22
201

I like this solution (from the Matplotlib Plotting Cookbook):

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

x = [0,5,9,10,15]
y = [0,1,2,3,4]

tick_spacing = 1

fig, ax = plt.subplots(1,1)
ax.plot(x,y)
ax.xaxis.set_major_locator(ticker.MultipleLocator(tick_spacing))
plt.show()

This solution give you explicit control of the tick spacing via the number given to ticker.MultipleLocater(), allows automatic limit determination, and is easy to read later.

9
  • 4
    A way to do this without calculating the ticks explicitly! Commented Aug 31, 2016 at 9:48
  • 6
    This is the same answer as this one. It does not make sense to add an identical answer two years later. Commented Jun 23, 2017 at 10:41
  • 13
    Good catch. I did not recognize them as the same when I posted the answer. Still, I think this presentation is a little easier to understand.
    – jthomas
    Commented Jun 23, 2017 at 15:17
  • 2
    The book reference in this answer also provide a helpful source for more information. Commented Apr 16, 2018 at 19:50
  • 2
    For future reference, instead of writing a new answer with the same solution, simply edit the other one to be more clear. I also find yours much more clear.
    – Emir
    Commented Jan 19, 2022 at 16:58
160

In case anyone is interested in a general one-liner, simply get the current ticks and use it to set the new ticks by sampling every other tick.

ax.set_xticks(ax.get_xticks()[::2])
7
  • 5
    This is the only generalisable answer for different tick types (str, float, datetime) Commented Sep 20, 2018 at 14:54
  • 4
    Remove non-integer ticks: ax.set_xticks([tick for tick in ax.get_xticks() if tick % 1 == 0]) Commented Jun 22, 2019 at 22:04
  • Lots of detailed solutions above but I agree this is the most concise. You could even extract the length of ax.get_xticks() and set the slicing frequency by this length divided by the number of required ticks.
    – Iain D
    Commented Aug 30, 2019 at 15:34
  • 2
    I think this is the best answer. Most other answers are too complicated and hard to apply/generalize. Thank you!
    – Sean
    Commented Sep 1, 2019 at 6:08
  • 15
    It can only reduce number of sticks, whereas in the question (and mine goal how I found it) was to increase it. Commented Oct 24, 2019 at 13:06
58

if you just want to set the spacing a simple one liner with minimal boilerplate:

plt.gca().xaxis.set_major_locator(plt.MultipleLocator(1))

also works easily for minor ticks:

plt.gca().xaxis.set_minor_locator(plt.MultipleLocator(1))

a bit of a mouthfull, but pretty compact

3
  • 1
    Worth mentioning the argument plt.MultipleLocator(arg) is the tick interval. So, if you want your ticks to be 5 units away from each other, just use plt.MultipleLocator(5). Otherwise like this solution the most. Thank you!
    – Ray Walker
    Commented Mar 14, 2022 at 11:30
  • ax.xaxis.set_major_locator(plt.MultipleLocator(val)) in case you are using fig, ax = plt.subplots()
    – Alexander
    Commented Dec 4, 2022 at 19:34
  • This seems like the most straightforward answer to the original question. Not sure why it's buried so far down in the answers. Please upvote this one if you agree. Commented Jul 19 at 20:27
40

This is a bit hacky, but by far the cleanest/easiest to understand example that I've found to do this. It's from an answer on SO here:

Cleanest way to hide every nth tick label in matplotlib colorbar?

for label in ax.get_xticklabels()[::2]:
    label.set_visible(False)

Then you can loop over the labels setting them to visible or not depending on the density you want.

edit: note that sometimes matplotlib sets labels == '', so it might look like a label is not present, when in fact it is and just isn't displaying anything. To make sure you're looping through actual visible labels, you could try:

visible_labels = [lab for lab in ax.get_xticklabels() if lab.get_visible() is True and lab.get_text() != '']
plt.setp(visible_labels[::2], visible=False)
3
  • 3
    This is the most simple and generic solution. A tiny adjustment: usually ax.get_xticklabels()[1::2] are the labels to be hidden.
    – jolvi
    Commented Sep 22, 2015 at 12:02
  • This doesn't work with matplotlib.finance.candlestick2
    – BCR
    Commented Feb 12, 2016 at 16:12
  • @BCR it could be that some of the xticklabels are just set to '' so that when you loop through them, you're making xticklabels that are empty invisible (which would have no effect on the visualization, but might mean that you aren't pulling the correct labels). You could try: vis_labels = [label for label in ax.get_xticklabels() if label.get_visible() is True]; plt.setp(vis_labels[::2], visible==False)
    – choldgraf
    Commented Feb 15, 2016 at 19:57
19

This is an old topic, but I stumble over this every now and then and made this function. It's very convenient:

import matplotlib.pyplot as pp
import numpy as np

def resadjust(ax, xres=None, yres=None):
    """
    Send in an axis and I fix the resolution as desired.
    """

    if xres:
        start, stop = ax.get_xlim()
        ticks = np.arange(start, stop + xres, xres)
        ax.set_xticks(ticks)
    if yres:
        start, stop = ax.get_ylim()
        ticks = np.arange(start, stop + yres, yres)
        ax.set_yticks(ticks)

One caveat of controlling the ticks like this is that one does no longer enjoy the interactive automagic updating of max scale after an added line. Then do

gca().set_ylim(top=new_top) # for example

and run the resadjust function again.

13

I developed an inelegant solution. Consider that we have the X axis and also a list of labels for each point in X.

Example:
import matplotlib.pyplot as plt

x = [0,1,2,3,4,5]
y = [10,20,15,18,7,19]
xlabels = ['jan','feb','mar','apr','may','jun']
Let's say that I want to show ticks labels only for 'feb' and 'jun'
xlabelsnew = []
for i in xlabels:
    if i not in ['feb','jun']:
        i = ' '
        xlabelsnew.append(i)
    else:
        xlabelsnew.append(i)
Good, now we have a fake list of labels. First, we plotted the original version.
plt.plot(x,y)
plt.xticks(range(0,len(x)),xlabels,rotation=45)
plt.show()
Now, the modified version.
plt.plot(x,y)
plt.xticks(range(0,len(x)),xlabelsnew,rotation=45)
plt.show()
10

Generalisable one liner, with only Numpy imported:

ax.set_xticks(np.arange(min(x),max(x),1))

Set in the context of the question:

import numpy as np
import matplotlib.pyplot as plt 
fig, ax = plt.subplots()
x = [0,5,9,10,15]
y = [0,1,2,3,4]
ax.plot(x,y)
ax.set_xticks(np.arange(min(x),max(x),1))
plt.show()

How it works:

  1. fig, ax = plt.subplots() gives the ax object which contains the axes.
  2. np.arange(min(x),max(x),1) gives an array of interval 1 from the min of x to the max of x. This is the new x ticks that we want.
  3. ax.set_xticks() changes the ticks on the ax object.
2
  • so, can you explain the code?
    – jagapathi
    Commented Jan 2, 2022 at 13:07
  • I've rewritten it to make it clearer. Hope that helps
    – Johnny V
    Commented Jan 5, 2022 at 13:00
9

Pure Python Implementation

Below's a pure python implementation of the desired functionality that handles any numeric series (int or float) with positive, negative, or mixed values and allows for the user to specify the desired step size:

import math

def computeTicks (x, step = 5):
    """
    Computes domain with given step encompassing series x
    @ params
    x    - Required - A list-like object of integers or floats
    step - Optional - Tick frequency
    """
    xMax, xMin = math.ceil(max(x)), math.floor(min(x))
    dMax, dMin = xMax + abs((xMax % step) - step) + (step if (xMax % step != 0) else 0), xMin - abs((xMin % step))
    return range(dMin, dMax, step)

Sample Output

# Negative to Positive
series = [-2, 18, 24, 29, 43]
print(list(computeTicks(series)))

[-5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45]

# Negative to 0
series = [-30, -14, -10, -9, -3, 0]
print(list(computeTicks(series)))

[-30, -25, -20, -15, -10, -5, 0]

# 0 to Positive
series = [19, 23, 24, 27]
print(list(computeTicks(series)))

[15, 20, 25, 30]

# Floats
series = [1.8, 12.0, 21.2]
print(list(computeTicks(series)))

[0, 5, 10, 15, 20, 25]

# Step – 100
series = [118.3, 293.2, 768.1]
print(list(computeTicks(series, step = 100)))

[100, 200, 300, 400, 500, 600, 700, 800]

Sample Usage

import matplotlib.pyplot as plt

x = [0,5,9,10,15]
y = [0,1,2,3,4]
plt.plot(x,y)
plt.xticks(computeTicks(x))
plt.show()

Plot of sample usage

Notice the x-axis has integer values all evenly spaced by 5, whereas the y-axis has a different interval (the matplotlib default behavior, because the ticks weren't specified).

6
xmarks=[i for i in range(1,length+1,1)]

plt.xticks(xmarks)

This worked for me

if you want ticks between [1,5] (1 and 5 inclusive) then replace

length = 5
1
  • 3
    fyi, you could simply write xmarks = range(1, length+1, 1). pretty sure the list comprehension is redundant.
    – Neal
    Commented Jul 21, 2017 at 13:30
6

Since None of the above solutions worked for my usecase, here I provide a solution using None (pun!) which can be adapted to a wide variety of scenarios.

Here is a sample piece of code that produces cluttered ticks on both X and Y axes.

# Note the super cluttered ticks on both X and Y axis.

# inputs
x = np.arange(1, 101)
y = x * np.log(x) 

fig = plt.figure()     # create figure
ax = fig.add_subplot(111)
ax.plot(x, y)
ax.set_xticks(x)        # set xtick values
ax.set_yticks(y)        # set ytick values

plt.show()

Now, we clean up the clutter with a new plot that shows only a sparse set of values on both x and y axes as ticks.

# inputs
x = np.arange(1, 101)
y = x * np.log(x)

fig = plt.figure()       # create figure
ax = fig.add_subplot(111)
ax.plot(x, y)

ax.set_xticks(x)
ax.set_yticks(y)

# which values need to be shown?
# here, we show every third value from `x` and `y`
show_every = 3

sparse_xticks = [None] * x.shape[0]
sparse_xticks[::show_every] = x[::show_every]

sparse_yticks = [None] * y.shape[0]
sparse_yticks[::show_every] = y[::show_every]

ax.set_xticklabels(sparse_xticks, fontsize=6)   # set sparse xtick values
ax.set_yticklabels(sparse_yticks, fontsize=6)   # set sparse ytick values

plt.show()

Depending on the usecase, one can adapt the above code simply by changing show_every and using that for sampling tick values for X or Y or both the axes.

If this stepsize based solution doesn't fit, then one can also populate the values of sparse_xticks or sparse_yticks at irregular intervals, if that is what is desired.

4

Setting big & small ticks together:

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

ax = plt.axes()
ax.xaxis.set_major_locator(ticker.MultipleLocator(5)) # set BIG ticks
ax.xaxis.set_minor_locator(ticker.MultipleLocator(1)) # set small ticks

x = range(0,21)
y = [4,8,3,10,6,12,5] * 3

plt.plot(x, y)
plt.show()

enter image description here

1

You can loop through labels and show or hide those you want:

   for i, label in enumerate(ax.get_xticklabels()):
        if i % interval != 0:
            label.set_visible(False)
1

If you need to change the ticklabel frequency along with tick frequency using the old ticklabels, using set_xticks and set_xticklabels one after the other throws a ValueError that looks like the following:

ValueError: The number of FixedLocator locations (5), usually from 
a call to set_ticks, does not match the number of labels (3).

A way get around the issue is to use set() method to set the two simultaneously. An example may illustrate it better.

import pandas as pd
ax = pd.Series(range(20), index=pd.date_range('2020', '2024', 20).date).plot()
ax.set_xticks(ax.get_xticks()[::2])             # <---- error   
ax.set_xticklabels(ax.get_xticklabels()[::2]);  # <---- error


ax = pd.Series(range(20), index=pd.date_range('2020', '2024', 20).date).plot()
ax.set(xticks=ax.get_xticks()[::2], 
       xticklabels=ax.get_xticklabels()[::2]);  # <---- OK

result


For this specific case, matplotlib.dates.YearLocator and matplotlib.dates.DateFormatter is more flexible (e.g. ax.xaxis.set_major_locator(matplotlib.dates.YearLocator())) and is probably the preferred way to set ticklabels but the post above offers a quick fix for a common error.

Not the answer you're looking for? Browse other questions tagged or ask your own question.