29

I am plotting time series using pandas .plot() and want to see every month shown as an x-tick.

Here is the dataset structure data set

Here is the result of the .plot()

enter image description here

I was trying to use examples from other posts and matplotlib documentation and do something like

ax.xaxis.set_major_locator(
   dates.MonthLocator(revenue_pivot.index, bymonthday=1,interval=1))

But that removed all the ticks :(

I also tried to pass xticks = df.index, but it has not changed anything.

What would be the rigth way to show more ticks on x-axis?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Rotkiv
  • 1,051
  • 2
  • 13
  • 33
  • 1
    Are you parsing the dates as datetimes? – Demetri Pananos Sep 27 '16 at 03:22
  • @DemetriP thanks. looks like it was at least a part of the problem. now after using ax.xaxis.set_major_locator I do see ticks... The only problem is now I only see one tick per year. I clearly using MonthLocator wrong. – Rotkiv Sep 27 '16 at 04:13

5 Answers5

33

No need to pass any args to MonthLocator. Make sure to use x_compat in the df.plot() call per @Rotkiv's answer.

import pandas as pd
import numpy as np
import matplotlib.pylab as plt
import matplotlib.dates as mdates

df = pd.DataFrame(np.random.rand(100,2), index=pd.date_range('1-1-2018', periods=100))
ax = df.plot(x_compat=True)
ax.xaxis.set_major_locator(mdates.MonthLocator())
plt.show()
  • formatted x-axis with set_major_locator

enter image description here

  • unformatted x-axis

enter image description here

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Kyle
  • 2,814
  • 2
  • 17
  • 30
11

You could also format the x-axis ticks and labels of a pandas DateTimeIndex "manually" using the attributes of a pandas Timestamp object.

I found that much easier than using locators from matplotlib.dates which work on other datetime formats than pandas (if I am not mistaken) and thus sometimes show an odd behaviour if dates are not converted accordingly.

Here's a generic example that shows the first day of each month as a label based on attributes of pandas Timestamp objects:

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


# data
dim = 8760
idx = pd.date_range('1/1/2000 00:00:00', freq='h', periods=dim)
df = pd.DataFrame(np.random.randn(dim, 2), index=idx)

# select tick positions based on timestamp attribute logic. see:
# https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Timestamp.html
positions = [p for p in df.index
             if p.hour == 0
             and p.is_month_start
             and p.month in range(1, 13, 1)]
# for date formatting, see:
# https://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior
labels = [l.strftime('%m-%d') for l in positions]

# plot with adjusted labels
ax = df.plot(kind='line', grid=True)
ax.set_xlabel('Time (h)')
ax.set_ylabel('Foo (Bar)')
ax.set_xticks(positions)
ax.set_xticklabels(labels)

plt.show()

yields:

enter image description here

Hope this helps!

Cord Kaldemeyer
  • 6,405
  • 8
  • 51
  • 81
4

The right way to do that described here Using the x_compat parameter, it is possible to suppress automatic tick resolution adjustment

df.A.plot(x_compat=True)

Rotkiv
  • 1,051
  • 2
  • 13
  • 33
  • 4
    Does not work for me. Python 3.6, pandas 0.19.2. Any idea why? – famargar Oct 13 '17 at 14:15
  • 3
    me neither, python 3.6 and pandas 0.22 – seanysull Jan 10 '18 at 16:37
  • 1
    @seanysull It should be quite simple to fix with the exact information. Unfortunately, this question has no [MCVE](https://stackoverflow.com/help/mcve) so it's difficult to tell whether your dataframe etc is set up the same as OPs (as OP answered themselves they may have done something "behind the scenes" that fixed the problem). I personally would have asked another question, linking to this one explaining why this didn't work for you. – DavidG Jan 11 '18 at 00:36
0

If you want to just show more ticks, you can also dive deep into the structure of pd.plotting._converter:

dai = ax.xaxis.minor.formatter.plot_obj.date_axis_info
dai['fmt'][dai['fmt'] == b''] = b'%b'

After plotting, the formatter is a TimeSeries_DateFormatter and _set_default_format has been called, so self.plot_obj.date_axis_info is not None. You can now manipulate the structured array .date_axis_info to be to your liking, namely contain less b'' and more b'%b'

flying sheep
  • 8,475
  • 5
  • 56
  • 73
0

Remove tick labels:

ax = df.plot(x='date', y=['count'])
every_nth = 10
for n, label in enumerate(ax.xaxis.get_ticklabels()):
  if n % every_nth != 0:
    label.set_visible(False)

Lower every_nth to include more labels, raise to keep fewer.

There
  • 498
  • 6
  • 18