Matplotlib showing x-tick labels overlapping

Ok, finally got it working. The trick was to use plt.setp to manually rotate the tick labels. Using fig.autofmt_xdate() did not work as it does some unexpected things when you have multiple subplots in your figure. Here's the working code with its output:

for i, d in enumerate([360, 30, 7, 1]):
    ax = axes.flatten()[i]
    earlycut = now - relativedelta(days=d)
    data = df.loc[df.index>=earlycut, :]
    ax.plot(data.index, data['value'])

    ax.get_xaxis().set_minor_locator(mpl.ticker.AutoMinorLocator())
    ax.get_yaxis().set_minor_locator(mpl.ticker.AutoMinorLocator())

    ax.grid(b=True, which='major', color='w', linewidth=1.5)
    ax.grid(b=True, which='minor', color='w', linewidth=0.75)

    plt.setp(ax.get_xticklabels(), rotation=30, horizontalalignment='right')

fig.tight_layout()

enter image description here

By the way, the comment earlier about some matplotlib things taking forever is very interesting here. I'm using a raspberry pi to act as a weather station at a remote location. It's collecting the data and serving the results via the web. And boy oh boy, it's really wheezing trying to put out these graphics.


Due to the way text rendering is handled in matplotlib, auto-detecting overlapping text really slows things down. (The space that text takes up can't be accurately calculated until after it's been drawn.) For that reason, matplotlib doesn't try to do this automatically.

Therefore, it's best to rotate long tick labels. Because dates most commonly have this problem, there's a figure method fig.autofmt_xdate() that will (among other things) rotate the tick labels to make them a bit more readable. (Note: If you're using a pandas plot method, it returns an axes object, so you'll need to use ax.figure.autofmt_xdate().)

As a quick example:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

time = pd.date_range('01/01/2014', '4/01/2014', freq='H')
values = np.random.normal(0, 1, time.size).cumsum()

fig, ax = plt.subplots()
ax.plot_date(time, values, marker='', linestyle='-')

fig.autofmt_xdate()
plt.show()

If we were to leave fig.autofmt_xdate() out:

enter image description here

And if we use fig.autofmt_xdate():

enter image description here


For the problems which don't have date values in x axis, rather a string, you can insert \n character in x axis values so they don't overlap. Here is an example -

The data frame is

somecol               value
category 1 of column   16
category 2 of column   13
category 3 of column   21
category 4 of column   20
category 5 of column   11
category 6 of column   22
category 7 of column   19
category 8 of column   14
category 9 of column   18
category 10 of column   23
category 11 of column   10
category 12 of column   24
category 13 of column   17
category 14 of column   15
category 15 of column   12

I need to plot value on y axis and somecol on x axis, which will normally be plotted like this -

Normal plot with overlap

As you can see, there is a lot of overlap. Now introduce \n character in somecol column.

somecol = df['somecol'].values.tolist()
for i in range(len(somecol)):   
    x = somecol[i].split(' ')   
    # insert \n before 'of'     
    x.insert(x.index('of'),'\n')
    somecol[i] = ' '.join(x) 

Now if you plot, it will look like this -

plt.plot(somecol, df['val'])

No overlap

This method works well if you don't want to rotate your labels.

The only con so far I found in this method is that you need to tweak your labels 3-4 times i.e., try with multiple formats to display the plot in best format.