Matplotlib DateFormatter for axis label not working

Question:

I’m trying to adjust the formatting of the date tick labels of the x-axis so that it only shows the Year and Month values. From what I’ve found online, I have to use mdates.DateFormatter, but it’s not taking effect at all with my current code as is. Anyone see where the issue is? (the dates are the index of the pandas Dataframe)

import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import pandas as pd 

fig = plt.figure(figsize = (10,6))
ax = fig.add_subplot(111)

ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))

basicDF['some_column'].plot(ax=ax, kind='bar', rot=75)

ax.xaxis_date()

enter image description here

Reproducible scenario code:

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

rng = pd.date_range('1/1/2014', periods=20, freq='m')

blah = pd.DataFrame(data = np.random.randn(len(rng)), index=rng)

fig = plt.figure(figsize = (10,6))
ax = fig.add_subplot(111)

ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))

blah.plot(ax=ax, kind='bar')

ax.xaxis_date()

Still can’t get just the year and month to show up.

If I set the format after .plot , get an error like this:

ValueError: DateFormatter found a value of x=0, which is an illegal date. This usually occurs because you have not informed the axis that it is plotting dates, e.g., with ax.xaxis_date().

It’s the same for if I put it before ax.xaxis_date() or after.

Asked By: SpicyClubSauce

||

Answers:

pandas just doesn’t work well with custom date-time formats.

You need to just use raw matplotlib in cases like this.

import numpy
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import pandas

N = 20
numpy.random.seed(N)

dates = pandas.date_range('1/1/2014', periods=N, freq='m')
df = pandas.DataFrame(
    data=numpy.random.randn(N), 
    index=dates,
    columns=['A']
)

fig, ax = plt.subplots(figsize=(10, 6))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
ax.bar(df.index, df['A'], width=25, align='center')

And that gives me:

enter image description here

Answered By: Paul H

The accepted answer claims that “pandas won’t work well with custom date-time formats”, but you can make use of pandas’ to_datetime() function to use your existing datetime Series in the dataframe:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter
import pandas as pd

rng = pd.date_range('1/1/2014', periods=20, freq='m')

blah = pd.DataFrame(data = np.random.randn(len(rng)), index=pd.to_datetime(rng))

fig, ax = plt.subplots()

ax.xaxis.set_major_formatter(DateFormatter('%m-%Y'))
ax.bar(blah.index, blah[0], width=25, align='center')

Will result in:

a bar graph with the dates formatted as described

You can see the different available formats here.

Answered By: Cilantro Ditrek

I stepped into the same problem and I used an workaround to transform the index from date time format into the desired string format:

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

rng = pd.date_range('1/1/2014', periods=20, freq='m')

blah = pd.DataFrame(data = np.random.randn(len(rng)), index=rng)

fig = plt.figure(figsize = (10,6))
ax = fig.add_subplot(111)

# transform index to strings
blah_test = blah.copy()
str_index = []
for s_year,s_month in zip(blah.index.year.values,blah.index.month.values):
    # build string accorind to format "%Y-%m"
    string_day = '{}-{:02d}'.format(s_year,s_month)
    str_index.append(string_day)
blah_test.index = str_index

blah_test.plot(ax=ax, kind='bar', rot=45)
plt.show()

which results in the following figure:
figure with labels formatted correctly

Answered By: Maximilian S.

Solution with pandas only

You can create nicely formatted ticks by using the DatetimeIndex and taking advantage of the datetime properties of the timestamps. Tick locators and formatters from matplotlib.dates are not necessary for a case like this unless you would want dynamic ticks when using the interactive interface of matplotlib for zooming in and out (more relevant for time ranges longer than in this example).

import numpy as np   # v 1.19.2
import pandas as pd  # v 1.1.3

# Create sample time series with month start frequency, plot it with a pandas bar chart
rng = np.random.default_rng(seed=1) # random number generator
dti = pd.date_range('1/1/2014', periods=20, freq='m')
df = pd.DataFrame(data=rng.normal(size=dti.size), index=dti)
ax = df.plot.bar(figsize=(10,4), legend=None)

# Set major ticks and tick labels
ax.set_xticks(range(df.index.size))
ax.set_xticklabels([ts.strftime('%bn%Y') if ts.year != df.index[idx-1].year
                    else ts.strftime('%b') for idx, ts in enumerate(df.index)])
ax.figure.autofmt_xdate(rotation=0, ha='center');

pd_bar_chart_date_ticks

Answered By: Patrick FitzGerald
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.