5

I am building a line plot with two lines, one for high temperatures and the other for low temperatures. The x-axis is based on days over one complete year in datetime format (2014-01-01, etc.). However, I changed the labels–not the data–for months (Jan, Feb, Mar, etc). The problem is that the first label 'Jan' is in the origin. What I want is to move all labels to the right to center them between ticks.

fig, ax = plt.subplots()

plt.plot(x, y1)
plt.plot(x, y2)

# Change x-axis from %y-%m-%d format to %m:
monthsFmt = mdates.DateFormatter('%m')
plt.gca().xaxis.set_major_formatter(monthsFmt)

# Replace numeric x-axis labels (1,2,3, ..., 12) for abbreviations of months ('Jan', 'Feb', 'Mar', etc.):
labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
ax.set_xticklabels(labels)

# CODE GOES HERE TO CENTER X-AXIS LABELS...

# Render plot:
plt.show()

This is the result that I am looking for:

enter image description here

Antonio Serrano
  • 882
  • 2
  • 14
  • 27

3 Answers3

4

Using the minor ticks as suggested in the thread posted by DavidG should work. Below is a MWE that I've adapted for your specific problem, forcing the major ticks to appear on the first of each month and using the minor ticks to place the labels in-between the major ticks :

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import datetime

# Generate some data for example :

yr = 2014
fig, ax = plt.subplots()

x0 = datetime.datetime(yr, 1, 1)
x = np.array([x0 + datetime.timedelta(days=i) for i in range(365)])

y1 = np.sin(2*np.pi*np.arange(365)/365) + np.random.rand(365)/5
y2 = np.sin(2*np.pi*np.arange(365)/365) + np.random.rand(365)/5 - 1

# Draw high and low temperatures lines :

ax.plot(x, y1, color='#c83c34')
ax.plot(x, y2, color='#28659c')
ax.fill_between(x, y2, y1, facecolor='#daecfd', alpha=0.5)

# Force the major ticks position on the first of each month and hide labels:

xticks = [datetime.datetime(yr, i+1, 1) for i in range(12)]
xticks.append(datetime.datetime(yr+1, 1, 1))
ax.set_xticks(xticks)
ax.tick_params(axis='both', direction='out', top=False, right=False)
ax.axis([xticks[0], xticks[-1], -2.5, 1.5])
ax.set_xticklabels([])

# CODE GOES HERE TO CENTER X-AXIS LABELS...

labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
          'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
mticks = ax.get_xticks()
ax.set_xticks((mticks[:-1]+mticks[1:])/2, minor=True)
ax.tick_params(axis='x', which='minor', length=0)
ax.set_xticklabels(labels, minor=True)
fig.tight_layout()

plt.show()

which results in: enter image description here

Jean-Sébastien
  • 2,649
  • 1
  • 16
  • 21
1

It often makes sense to use dates directly when plotting. So instead of manually setting fixed locations for the labels, one may use matplotlib.dates locators. To position ticks in the middle of the months, one could choose the 15th of the month,

matplotlib.dates.MonthLocator(bymonthday=15)

A full example:

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as mticker
import random

df = pd.DataFrame(random.sample(range(10000),8760),
                  index = pd.date_range(start ='2013-01-01 00:00:00',
                                        end ='2013-12-31 23:30:00',
                                        freq='1H'),
                  columns =['value'])

fig, ax = plt.subplots()
ax.plot(df.index, df['value'])
ax.set_xlim(pd.Timestamp('2013-01-01 00:00:00'), pd.Timestamp('2013-12-31 23:30:00'))

months = mdates.MonthLocator(bymonth =(1,6,12), bymonthday=15 )
allmonths = mdates.MonthLocator()
m_fmt = mdates.DateFormatter('%b')
ax.xaxis.set_major_locator(allmonths)
ax.xaxis.set_minor_locator(months)

ax.xaxis.set_major_formatter(mticker.NullFormatter())
ax.xaxis.set_minor_formatter(m_fmt)
ax.tick_params(axis="x", which="minor", length=0)
plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • This works brilliantly for this simplified example. i have a more complex subplotting scenario where it doesn't work... will post another question. – doctorer Aug 27 '18 at 10:20
  • My related question is here: https://stackoverflow.com/questions/52037844/formatting-matplotlib-dates-locators-on-multiple-plots – doctorer Aug 27 '18 at 11:03
0

Almost, Jean-Sébastien. It looks everything perfect but the last month. It does not show up.

Your solution tweaked a bit:

fig, ax = plt.subplots()

# Draw high and low temperatures lines:
plt.plot(x, y1, color = '#c83c34')
plt.plot(x, y2, color = '#28659c')

# Fill area between lines:
plt.gca().fill_between(x,
                       y2, y1,
                       facecolor='#daecfd',
                       alpha=0.5)

# Force major ticks on a monthly time scale only:
locator = mpl.dates.MonthLocator()
ax.xaxis.set_major_locator(locator)

# Hide major labels and set axis limits:
ax.set_xticklabels([])
ax.tick_params(axis='both', direction='out')
ax.axis(xmin=datetime.datetime(2014, 1, 1),
        xmax=datetime.datetime(2014, 12, 31),
        ymin=-50, ymax=50)

labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
          'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

mticks = ax.get_xticks()
ax.set_xticks((mticks[:-1]+mticks[1:])/2, minor=True)
ax.tick_params(axis='x', which='minor', length=0)
ax.set_xticklabels(labels, minor=True)

plt.show()

The plot that I got is this one: enter image description here

Any clue about what is needed to include December in the x-axis labels? Anyway, thx very much.

Community
  • 1
  • 1
Antonio Serrano
  • 882
  • 2
  • 14
  • 27